I'm starting to learn Node.
I am trying to make a RESTful API for a books app with MySQL, so in my Books.js file I have the following code:
(I am using esm to enable import/exports)
import express from 'express';
import mysqlConnection from '../database';
const router = express.Router()
router.get('/', (req,res) => {
mysqlConnection.query('SELECT * FROM book', (err, rows, fields) => {
if(!err){
res.json(rows);
} else{
console.log(err);
}
})
});
router.get('/:id', (req, res)=> {
const {id} = req.params;
console.log('id is:',id);
mysqlConnection.query(`SELECT * FROM book WHERE isbn ='${id}'`, (err, rows, fields)=> {
if(!err){
res.json(rows);
} else{
console.log(err);
}
})
})
export default router;
And when I go to "http://localhost:3000/123-456-789-13" for example, I get this as logs in the console:
server started!
connected to DB!
id is: 123-456-789-13
id is: favicon.ico
It's not failing but I'm surprised to see this... Any idea of what I'm doing wrong?
Thanks in advance for your answers!
The browser is automatically requesting /favicon.ico to try to get a thumbnail to represent the website.
But, you have a top level wildcard route with:
router.get('/:id', (req, res)=> { ...});
which matches EVERYTHING at the top level, including the request for /favicon.ico. In general, top level wildcard routes like this are a source of trouble because they allow for NO other top level routes anywhere on your site because they match all top level URLs. Not only do they match things like /favicon.ico, but it will also match /robots.txt that search engines may request and it conflicts with you adding other top level routes in the future.
In general, it is recommended that you provide some resource name first such as:
router.get('/book/:id', (req, res)=> { ...});
Then, you won't have any of this conflict with other top level routes.
Related
So I have added some swagger configuration in a routes folder like so:
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import swaggerDocument from '../swagger/swagger.json';
const router = express.Router();
router.get('/api-docs', swaggerUi.setup(swaggerDocument));
export { router as swaggerRouter }
Now there is an authentication process in the root app.ts file, but my understanding is if I add the swagger endpoint before it executes the authenticate logic, it should not ask for headers:
app.get('/', (req, res) => {
res.send('Howdy!');
});
app.use(swaggerRouter);
app.use(async (req, res, next) => {
const result = await auth.verifyAuth(req).catch((err) => {
return err;
});
if (result.httpCode == 200) {
res.locals.authResult = result
next()
} else {
res.send(result)
}
});
So the authentication logic from where verifyAuth comes from would make for a good middleware to target endpoints instead of the whole entire application, but to refactor it to work as such a middleware is a pain because the author wrote every function to depend on every other function.
And yet, if I add a random endpoint above that authenticate logic like:
router.get('/pingMe', (req, res) => {}
You can go to that one without being asked to provide headers.
What am I missing?
I literally removed the authentication logic and I am still unable to get to the swagger endpoint without being asked for headers.
I am trying to access my application-level middleware from router in a project generated with express application generator.
Middleware is used to query database with user ID received from router.
I feel like I'm missing something very simple (or fundamental) but can't get around the problem (this being my first Node.js project). So more than best practice I'm looking for a simple solution
I've tried using different app methods including post.
/app.js
var MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
app.use(MyAppMidW);
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
res.send(req.app.get.MyAppMidW(req.header('auth-header'))); //The problem
}
else {
res.send(req.app.get('defaultData')); //This works
}
});
Error messages include "$middleware is not a function" and "$middleware is not defined".
Solution
/app.js
app.MyAppMidW = function (req) {
queryDB(req);
}
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
req.app.MyAppMidW(req.header('auth-header'))); //Makes a database query
res.send(req.app.get('defaultData')); //Fetches database query result
}
else {
res.send(req.app.get('defaultData'));
}
});
If you do it like this
app.use(MyAppMidW);
Every request will query your db, and thats not what you want. I guess you use the MVC design pattern.
In your route folder you have something like this:
import appController from "../controllers/app.js"
router.get("/dbquery", appController.MyAppQuery)
And in your controllers folder you have your logic that querys the db
exports.MyAppQuery = (req, res){
//If u use mongodb for example
YourModel.find().then(data => {
res.json(data)
})
}
You need to call app.set("MyAppMidW", MyAppMidW) and then you can use get. Or do this inside the app.js file
app.MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
Then call it by req.app.get('MyAppMidW')(req.header('auth-header')) or req.app.MyAppMidW(req.header('auth-header')) inside the routes file.
But middleware is called automatically when you say app.use(MyAppMidW) the function is called by default on each request. So no need to call it explicitly inside the router function.
I'm trying to build up an api for my website. However I kind of struggle with the route params.
I have two routes for now:
const route1 = require('./routes/route1')
const route2 = require('./routes/route2')
app.use('/route', route1)
app.use('/route/:id/details', route2);
The first route works perfectly fine. Within the first route, I check for the ID param as well as I do gets like
Code in route1
router.get('/:id', async (req, res) => {
...
})
I can easily check for the ID as a paramter in my query.
I wanted to make a 2nd path, so that I can call for the details of one object.
However its not working as expected.
Code in route2:
router.get('/', async (req, res) => {
console.log(req.params);
})
My parameters are always empty in this path. (I already checked that it can get called)
Wherever you use router add this to the top of that file:
i.e. in router.js
const router = require('express').Router({mergeParams: true});
notice the {mergeParams: true} you need this since you're nesting your routes in another file. If you left your routes all in on file, i.e.
app.use('/route/:id/details', (req, res) => {
console.log(req.params);// works fine
});
then you wouldn't need the mergeParams true flag.
I've been trying a variety of setups for hot reloading and one that I've come across is the https://github.com/glenjamin/ultimate-hot-reloading-example/. Modifying this boilerplate code as a starting point, I've come across the following problem in my server code:
// server.js
import chokidar from 'chokidar';
import express from 'express';
const app = express();
// this is the middleware for handline all of my routes
app.use(function (req, res, next) {
require('./server/index')(req, res, next);
// if I commented out any additional routes, the setup would work fine
require('./server/foo')(req, res, next);
require('./server/catch-all')(req, res, next);
});
//this watches the server folder for changes
const watcher = chokidar.watch('./server');
watcher.on('ready', function () {
watcher.on('all', function () {
console.log("Clearing /server/ module cache from server");
Object.keys(require.cache).forEach(function (id) {
if (/[\/\\]server[\/\\]/.test(id)) delete require.cache[id];
});
});
});
app.listen(3000, 'localhost', function (err) {
if (err) throw err;
const addr = this.address();
console.log('Listening at http://%s:%d', addr.address, addr.port);
});
The above is the server code that handles clearing the cache by watching for changes with the chokidar module. If I have just one route required inside the app.use middleware function (which listens for every incoming request), I can get it to work. However if have multiple routes, the following error occurs:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This is a common issue posted on stack overflow, but all of the solutions I've come across and attempted haven't worked. My route files are as follows:
//index.js
import express from 'express';
const router = express.Router();
router.get('/', (req, res, next) => {
res.send("greagrehgarhegrehuh").end();
return next('router');
});
module.exports = router;
//end of index.js
//foo.js
import express from 'express';
const router = express.Router();
router.get('/foo', (req, res, next) => {
res.send("foo").end();
return next('router');
});
module.exports = router;
//end of foo.js
//catch-all.js
import express from 'express';
const router = express.Router();
router.get('*', (req, res, next) => {
res.send("catch all").end();
return next('router');
});
module.exports = router;
// end of catch-all.js
All three routes do the same thing, bar the endpoint. So far I've explicitly called end on each to end the response, used return next('router') to skip the rest of the middleware functions and have also tried doing it without the above as well. Any ideas on what I'm missing here to get this working? Here's a github project that showcases the issue
https://github.com/RonanQuigley/express-chokidar-hot-reload
UPDATE
So I actually removed the next calls and seem to have almost got it working by doing the following:
app.use(function (req, res, next) {
require('./server/index')(req, res, next);
require('./server/foo')(req, res, next);
});
// a second app.use middleware, that does the same
// as the catch all // * router.get from my original post
app.use(function (req, res, next) {
app.get('*', (req, res) => res.send('catch all'));
})
However, I can't use this second app.use with another require call to a file with an express router like the others. So it seems that express runs through the middleware stack, reaches the * and tries to set the header twice.
The reason I need the * is normally if a user requests an endpoint that doesn't exist, Node correctly shows up with cannot GET/. However, for some reason, with the setup I've outlined express will then crash. My workaround is using * at the end of the middleware stack and I'd just use a res.redirect to send the user back to wherever, but this causes the above issue I've outlined in my original post. So not sure how to get around that one.
So currently I have either:
1) Hot reloading works without the require for a router.get('*'), but when the user navigates to an endpoint that doesn't exist, express will crash.
2) Hot reloading works with the app.get('*') inside a second app.use call, but I can't then use a router to move this into a separate file.
Okay, so posting this solution up for my own future reference and in case somebody else stumbles into this problem.
After speaking with the express devs, it turns out that this is indeed possible with a combination of the following:
// you need to use comma separated routes
app.use(
dynamic('./server/index'),
dynamic('./server/foo')
);
// require the library at runtime and apply the req, res, next arguments
function dynamic(lib) {
return function (req, res, next) {
return require(lib).apply(this, arguments)
}
}
In the case of webpack, this would break it as you can't use require as an expression. So use the following to get around that:
function createRoutes(router) {
const dynamic = (lib) => {
return function (req, res, next) {
// let webpack generate a regex expression from this require
// if we don't you would get a critical dependency warning
// which would result in the routes not being found
return require("./src/" + lib + ".js").apply(this, arguments);
}
}
router.use(
dynamic('index'),
dynamic('foo'),
);
return router;
}
Let's step back a bit and talk about middleware.
Say you have a function which runs some kind of middleware.
const runMiddleware = (req, res, next) => {
console.log(`this will run everytime a HTTP request comes in`);
}
Then to use that middleware within express:
app.use(runMiddleware);
Every time any (GET, POST, DELETE, etc) request comes in, this function is run.
Essentially you are doing the same thing below - You are wrapping three (3) route calls with a single function. This function is calling all of these routes at once, hence res is actually being sent 3 times in a row in the example below:
app.use(function (req, res, next) { // runs every time any request comes in
require('./server/index')(req, res, next); // res sent, ok
require('./server/foo')(req, res, next); // res sent, err
require('./server/catch-all')(req, res, next); // res sent, err
});
Here is a basic way of handling routes:
const index = require('./server/index');
const foo = require('./server/foo');
app.use('/', index);
app.use('/foo', foo);
// catch everything else
app.use(function (req, res) {
res.send('catch all');
})
I have three files server.js, views.js and access.js
In server.jsI have put all the dependencies and some routes like
app.post('/services/getallversions', (req, res) => {
...
// code to handle the req and send response
})
In views.js I have code like below,
module.exports = function(app, db, bodyParser, rules, constants, query) {
app.post('/services/user/:user/:type', (req, res) => {
// user can be 'abcd'
// type can be addview, deleteview etc.
...
// do processing for the req and send res
})
}
In access.js I have code like,
module.exports = function(app, db, bodyParser, rules, constants, query) {
app.post('/services/user/:user/:type', (req, res) => {
// user can be 'abcd'
// type can be addaccess, removeaccess etc.
...
// do processing for the req and send res
})
}
In server.js file I require the access.js and views.js in following way,
var access = require('./access')(app, db, bodyParser, rules, constants, query)
var views = require('./views')(app, db, bodyParser, rules, constants, query)
When I try to POST using /services/user/abcd/addaccess my views.js file code gets executed. constants, query, rules are other .js file which is already used in server.js using require('./filename').
I understand that the ambiguity causes due to same URL structure. I am using Express 4 and Node JS 6. I want to separate code of access.js and views.js from server.js and put them in separate files and require them in the above mentioned manner. views.js and access.js are created by me. They are not any Javascript Framework or something like that.
In view.js I have also tried the following code
var router = require('express').Router()
router.post('/services/user/:user/:type', (req,res)=>{})
But the same problem exists. Is there any way to achieve the thing ?
I suggest you use "miniApp" concept in Express, where each "miniApp" is distinguished using name-space.
For example:
Main App:
All routes with '/views/...' prefix will go to viewsCtrl. This middleware should appear before your default/main app routes:
var viewsCtrl = require('./views');
app.use('/views', viewsCtrl);
Inside views.js:
var router = require('express').Router();
// complete route /views/services/user/:user/:type
router.get('/services/user/:user/:type', function(req, res){...});
module.exports = router;
Same for access.js.
The routes are identical and express will never be able to tell which one to call. Order is not the problem here; as Chris G said in his comment, the second call to app.post(...) will overwrite the first (think of URLs as keys in a hashset).
You already know that the url will be in the format of /addview or /removaccess etc, so you can put that knowledge in the routing middleware:
// access.js
app.post('/services/user/:user/access/:type', (req, res) => {
// ... type is now only add, remove, etc...
})
// view.js
app.post('/services/user/:user/view/:type', (req, res) => {
// ...
})
or even (I think):
// access.js
app.post('/services/user/:user/:type((access)$)/', (req, res) => {
// ... will match addaccess, removeaccess and so on
// but I'm not entirely sure ...
})
Reference here:
https://expressjs.com/en/guide/routing.html