If I send POST such /image/cover or /image/sub/ from client, the router function doesn't work at all so It sends 404. It's supposed to work but I literally have no idea. I never had this case It just doesn't work for no reason.
router
import Router from 'koa-router'
const router = new Router({ prefix: '/image' })
router.post('/cover', async (ctx, next) => {
let URLpath = ctx.request.body.files.cover.path
ctx.body = { url: URLpath }
})
router.post('/sub', async (ctx, next) => {
let URLpath = ctx.request.body.files.sub.path
ctx.body = { url: URLpath }
})
export default router
log
<-- POST /image/cover
--> POST /image/cover 404 33ms -
import code (UPDATED)
router/index.js
import compose from 'koa-compose'
import accountRouter from './account'
import mypageRouter from './mypage'
import imageRouter from './image'
import postRouter from './post'
const routes = [
accountRouter,
mypageRouter,
imageRouter,
postRouter
]
export default () => compose([].concat(
...routes.map(r => [r.routes(), r.allowedMethods()])
))
app.js
import Koa from 'koa'
import serve from 'koa-static'
import logger from 'koa-logger'
import views from 'koa-views'
import session from 'koa-session'
import passport from 'koa-passport'
import bodyParser from 'koa-body'
import path from 'path'
import routes from './routes'
import config from './config'
import form from './util/formidable'
import mongoose from 'mongoose'
mongoose.Promise = global.Promise
mongoose
.connect(config.MONGODB_URI)
.then(startApp).catch(::console.error)
function startApp() {
const app = new Koa()
const port = process.env.PORT || 3000
const dist = __dirname + '/views/'
console.log(form)
const bpOption = { IncomingForm: form, multipart: true }
app.keys = ['secret', 'key'];
require('./util/passport')
app
.use(logger())
.use(serve(dist))
.use(session({}, app))
.use(bodyParser(bpOption))
.use(passport.initialize())
.use(passport.session())
.use(views(__dirname+'/views', { extension: 'pug'}))
.use(routes())
app.listen(port, () => console.log(`[!] Server is Running on ${port}`))
}
you need help function:
// #help function record route table map
'use strict';
const fs = require('fs');
const resolve = require('path').resolve;
module.exports = function record(router, filename) {
const routeTable = fs.createWriteStream(resolve(__dirname, filename));
routeTable.write(`Update Date: ${new Date().toString()}\n\n`);
for (let len = router.stack.length, i = 0; i < len; i += 1) {
const route = router.stack[i];
routeTable.write(`URL:${route.path}\t${route.methods.join('|')}\n`);
}
routeTable.end();
};
then you can involve it by
const record = require('./record');
record(routeApp, '.routerc')
you will get .routerc file(route map) with content such as:
URL: /a/c/c POST
URL: /b/a/d GET
it can help you to solve problem. maybe work. good luck!
Related
I am trying to unit test my router object in Express but inside the unit test file the object returns undefined
Here is a minimal version of my app
src/config/apiVersion.js
// update major versions here
const version = '/v2'
export default version
src/routes/index.js
import express from 'express'
import {
healthRouter,
healthUrl
} from './health/index.js'
const router = express.Router()
// add new routes here
const allRoutes = [
{
path: healthUrl,
route: healthRouter
}
]
// tell the router to use the routes you added
allRoutes.forEach((route) => {
router.use(route.path, route.route)
})
export default router
src/routes/health/index.js
import express from 'express'
import { healthController } from '../../controllers/health/index.js'
const healthRouter = express.Router()
const healthUrl = '/health'
healthRouter.route('/')
.get(healthController)
export {
healthRouter,
healthUrl
}
src/app.js (note I omitted most of the app.use's such as app.us(cors()) for example
// version is just the string '/v2'
import version from './config/apiVersion.js'
import router from './routes/index.js'
const app = express()
// some other app.use's here omitted like app.use(cors)
// add routes
app.use(`${version}`, router)
// custom 404 to handle non-existent paths/typos on paths
app.use((req, res) => {
res.status(404).send({ error: 'Path does not exist, check for typos. If querying /soap you also need vendor and method in the path' })
})
// custom error handler
app.use((err, req, res) => {
appLogger.error('There was an error: ' + err.stack)
res.status(500).send('Something broke!')
})
export default app
Here is my test file
import router from '../../../src/routes/index.js'
// to make sure the number of routes doesn't change without a new test added
const actualNumberRoutes = 2
describe('router', () => {
it('should return all the routes', () => {
let numberOfRoutes = 0
router.stack.forEach((layer) => {
expect(layer.name).toEqual('router')
numberOfRoutes += 1
})
expect(numberOfRoutes).toEqual(actualNumberRoutes)
})
})
And the error for this file where router is coming up as undefined
Try to provide your app to use routes after importing your respective route files like this.
import healthRouter from 'src/routes/health/index.js';
import router from '../../../src/routes/index.js';
const app=express();
app.use("your_path",router);
app.use("your_health_path",healthRouter);
My fetch takes too long until it fails
I tried chrome, edge and postman
other fetch requests from pixabay api are working great
I compared the code to other projects I've made and found nothing
I also added a proxy to "package.json" as suggested on some places and it didnt work
posting below parts of my code:
controller:
import axios from 'axios'
export const getAll = async () =>{
const response = await axios.get('https://pixabay.com/api/?key=25540812-faf2b76d586c1787d2dd02736')
.then(resp=>resp)
.catch(err=>console.log(err));
return response;
}
router:
import express from "express";
import * as homeController from '../controllers/home.controller.js'
const homeRouter = express.Router();
homeRouter.get('/all', homeController.getAll)
export default homeRouter
indexjs:
import express from "express"
import dotenv from "dotenv"
import homeRouter from './routers/home.router.js'
dotenv.config();
const PORT = 3000 //process.env.PORT
console.log(PORT);
const app = express();
app.use(express.json());
app.use(homeRouter)
app.listen(PORT, ()=>{console.log(`server is connected on port ${PORT}`)})
fetch:
const getAll = async()=>{
try {
const response = await fetch (`http://localhost:3000/all`)
console.log("hu");
if (!response.ok) {
throw new Error();
}
else{
console.log("ok");
}
const responseObj = await response.json();
console.log(responseObj);
}
catch (error) {
console.log(error);
}
}
useEffect(()=>{
getAll();
},[])
Posting the answer by #Jaromanda X for everyone to see:
"see this app.get('/', (req, res) => { ... where's you req and res ??? nowhere, that's where - hint: export const getAll = async (req, res) =>{"
Apparently EVERY controller made with express needs to send a response back (in the form of res.send)
Should be obvious but somehow I missed it
Thanks everyone!
We are having problem accessing the API-key from our .env file when trying to fetch in our server.js. If we add the API-key manually to the URL in server.js it works, so the problem seems to be the connection between server.js and .env-file.
We have npm installed dotenv.
In the .env file we have written the key like this: WEATHER_API_KEY = XXXXXXXXXXXX
Does anyone know what we have done wrong?
import express from "express";
import cors from "cors";
import mongoose from "mongoose";
import crypto from "crypto";
import bcrypt from "bcrypt";
import request from "request";
import dotenv from "dotenv";
// import { stringify } from "querystring";
const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.Promise = Promise;
const port = process.env.PORT || 8080;
const app = express();
dotenv.config();
app.get("/home", (req, res) => {
let city = req.query.city;
// const request = require("request");
// const options = {
// url: `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}`,
// method: "GET",
// headers: {
// Accept: "application/json",
// },
// };
const key = "*******************";
const requesturl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`;
request(requesturl, function (error, response, body) {
let data = JSON.parse(body);
console.log(response);
if (response.statusCode === 200) {
res.send(`The weather in ${city} is ${data.weather[0].description}`);
} else {
res.send(data.message);
}
});
console.log(process.env.WEATHER_API_KEY);
});
You may try this
import 'dotenv/config';
in place of import dotenv from "dotenv"; and remove the dotenv.config(); call.
Source and explanation: https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
And update the request URL (which you might have changed for testing purpose) to
const requesturl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.key}`;
Also, try defining your key without any spaces, though this is less likely to be the root cause.
WEATHER_API_KEY="XXXXXXXXXXXX"
Although the question is not specifically about React, it might be helpful for those who use React. For a React App, Environment Variables have to start with the REACT_APP_ prefix otherwise it won't work.
REACT_APP_WEATHER_API_KEY="XXXXXXXXXXXX"
I am trying to upload an image with multer to cloudinary and then store the resulting url in a database column. I have the following code:
The cloudinary config file:
import { config, uploader } from 'cloudinary'
import dotenv from 'dotenv';
dotenv.config();
const cloudinaryConfig = (req, res, next) => {
config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
next();
}
export { cloudinaryConfig, uploader };
The multer config file:
import multer from 'multer';
import Datauri from 'datauri';
import path from 'path';
const storage = multer.memoryStorage();
const multerUploads = multer({ storage }).single('image');
const dUri = new Datauri();
/**
* #description This function converts the buffer to data url
* #param {Object} req containing the field object
* #returns {String} The data url from the string buffer
*/
const dataUri = req => dUri.format(path.extname(req.file.originalname).toString(), req.file.buffer);
export { multerUploads, dataUri };
The entry point file(app.js)
import express from 'express';
import { urlencoded, json } from 'body-parser';
import { resolve } from 'path';
import { uploader, cloudinaryConfig } from './config/cloudinaryConfig'
import { multerUploads, dataUri } from './middlewares/multerUpload';
const app = express();
const Port = process.env.PORT || 3000;
app.use(express.static(resolve(__dirname, 'src/public')));
app.use(urlencoded({ extended: false }));
app.use(json());
app.use('*', cloudinaryConfig);
**app.post('/upload', multerUploads, (req, res) => {**
if(req.file) {
const file = dataUri(req).content;
return uploader.upload(file).then((result) => {
const image = result.url;
return res.status(200).json({
messge: 'Your image has been uploaded successfully to cloudinary',
data: {
image
}
})
}).catch((err) => res.status(400).json({
messge: 'something went wrong while processing your request',
data: {
err
}
}))
}
});
app.listen(Port, () => console.log(`Server started at http://localhost:${Port}`));
I have tried to split the functions and call them in the application routes but req.file was undefined while for the other scenario, the url wasn't generated and no error was thrown. I have also looked at the express documentation to see if I can store the result of a route in a variable but I didn't see anything like so written.
carRouter.post('/car', verifyToken, postAdchecker, cloudinaryConfig, multerUploads, postCarAd);
I want the result of the upload route which is the url generated to be passed into another route(postCarAd) as a field(imageurl). How do I make that happen?
So using axios, I am attempting to make a request to my express server, here is my Axios request:
/* #flow */
import type {
Dispatch,
GetState,
ThunkAction,
Reducer,
} from '../../types';
export const USERS_INVALID = 'USERS_INVALID';
export const USERS_REQUESTING = 'USERS_REQUESTING';
export const USERS_FAILURE = 'USERS_FAILURE';
export const USERS_SUCCESS = 'USERS_SUCCESS';
export const API_URL = '/api/articleList';
// Export this for unit testing more easily
export const fetchUsers = (axios: any, URL: string = API_URL): ThunkAction =>
(dispatch: Dispatch) => {
dispatch({ type: USERS_REQUESTING });
return axios.get(URL)
.then((res) => {
dispatch({ type: USERS_SUCCESS, data: res.data });
})
.catch((err) => {
dispatch({ type: USERS_FAILURE, err });
});
};
// Preventing dobule fetching data
/* istanbul ignore next */
const shouldFetchUsers = (state: Reducer): boolean => {
// In development, we will allow action dispatching
// or your reducer hot reloading won't updated on the view
if (__DEV__) return true;
const userListFetch = state.userListFetch;
if (userListFetch.readyStatus === USERS_SUCCESS) return false; // Preventing double fetching data
return true;
};
/* istanbul ignore next */
export const fetchUsersIfNeeded = (): ThunkAction =>
(dispatch: Dispatch, getState: GetState, axios: any) => {
/* istanbul ignore next */
if (shouldFetchUsers(getState())) {
/* istanbul ignore next */
return dispatch(fetchUsers(axios));
}
/* istanbul ignore next */
return null;
};
and here is my code on the express server:
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('hello');
});
It does not log "hello" and it displays no errors, I just think I'm missing something for the Axios to reach my server...
I had something similar working on another app but cannot seem to implement it here, I've searched online and cannot find a solution, any help or advice is appreciated - thank you in advance!
NOTE: the Axios request works fine when the "API_URL" variable is
set to a myjson url link, so I know my action works fine, I just feel
like I'm missing something for it to reach my server
EDIT: Please see my entire server.js:
/* #flow */
import path from 'path';
import morgan from 'morgan';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
import hpp from 'hpp';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { matchRoutes } from 'react-router-config';
import { Provider } from 'react-redux';
import chalk from 'chalk';
import createHistory from 'history/createMemoryHistory';
import configureStore from './redux/store';
import Html from './utils/Html';
import App from './containers/App';
import routes from './routes';
import { port, host } from './config';
const app = express();
// Using helmet to secure Express with various HTTP headers
app.use(helmet());
// Prevent HTTP parameter pollution.
app.use(hpp());
// Compress all requests
app.use(compression());
// Use morgan for http request debug (only show error)
app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400 }));
app.use(favicon(path.join(process.cwd(), './build/public/favicon.ico')));
app.use(express.static(path.join(process.cwd(), './build/public')));
// Run express as webpack dev server
if (__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../tools/webpack/webpack.client.babel');
const compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
hot: true,
noInfo: true,
stats: 'minimal',
}));
app.use(require('webpack-hot-middleware')(compiler));
}
// Register server-side rendering middleware
app.get('*', (req, res) => {
if (__DEV__) webpackIsomorphicTools.refresh();
const history = createHistory();
const store = configureStore(history);
const renderHtml = (store, htmlContent) => { // eslint-disable-line no-shadow
const html = renderToStaticMarkup(<Html store={store} htmlContent={htmlContent} />);
return `<!doctype html>${html}`;
};
// If __DISABLE_SSR__ = true, disable server side rendering
if (__DISABLE_SSR__) {
res.send(renderHtml(store));
return;
}
// Load data on server-side
const loadBranchData = () => {
const branch = matchRoutes(routes, req.url);
const promises = branch.map(({ route, match }) => {
// Dispatch the action(s) through the loadData method of "./routes.js"
if (route.loadData) return route.loadData(store.dispatch, match.params);
return Promise.resolve(null);
});
return Promise.all(promises);
};
// Send response after all the action(s) are dispathed
loadBranchData()
.then(() => {
// Setup React-Router server-side rendering
const routerContext = {};
const htmlContent = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={routerContext}>
<App />
</StaticRouter>
</Provider>,
);
// Check if the render result contains a redirect, if so we need to set
// the specific status and redirect header and end the response
if (routerContext.url) {
res.status(301).setHeader('Location', routerContext.url);
res.end();
return;
}
// Checking is page is 404
const status = routerContext.status === '404' ? 404 : 200;
// Pass the route and initial state into html template
res.status(status).send(renderHtml(store, htmlContent));
})
.catch((err) => {
res.status(404).send('Not Found :(');
console.error(`==> 😠Rendering routes error: ${err}`);
});
});
//----------------------------------------------------
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('yoyoyo');
var indexLimit = parseInt(req.query.indexLimit, 10);
var articleId = req.query.articleId
var articles = [];
db.collection('articles')
.find()
.sort("dateAdded", -1)
.limit(indexLimit)
.toArray()
.then(result => {
articles = articles.concat(result);
}).then(() => {
res.send(articles);
}).catch(e => {
console.error(e);
});
});
//------------------------------------
//connect to mongo db
var db
const MongoClient = require('mongodb').MongoClient
MongoClient.connect('mongodb://##CHANGED###:test#ds123930.mlab.com:###/###', (err, database) => {
if (err) return console.log(err);
db = database
console.log('db connected');
})
if (port) {
app.listen(port, host, (err) => {
if (err) console.error(`==> 😠OMG!!! ${err}`);
console.info(chalk.green(`==> 🌎 Listening at http://${host}:${port}`));
// Open Chrome
require('../tools/openBrowser').default(port);
});
} else {
console.error(chalk.red('==> 😠OMG!!! No PORT environment variable has been specified'));
}
You will need to move your /api routes above:
app.get('*', (req, res) => {
...
}
Your call to /api/articleList is hitting that catch all route handler of '*' and responding to the request with the rendered page. When communicating with your api for data, you don't want a page render, you want the response from the api :)
Middleware is executed in order of appearance from top to bottom.