Add prefix in rest apis in nodejs - javascript

I have a server.js file in which I have required services.js that further requires all the other API files
require('./web_services/services')(app);
I have two other files Users.js and Events.js that holds the actual apis. The services.js require both the files
var logger = require("../logger/logger");
module.exports = function (app) {
require('./User/Users')(app,logger);
require('./Event/Events')(app,logger);
// in case of api error
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.send({ 'status': res.status, 'message': err });
logger.error(err);
});
}
Now I want to prefix /users before users apis and events/ before event apis.
I have tried
app.use('/user/', require('./User/Users')(app));
Users.js
module.exports = function (app, logger) {
// apis
};//exports
but this is not working? Am i missing something?

I believe you can solve your problem by using express routing
You can initialize router in service.js and then pass it around to enable routes and middleware in it and can define prefix in it in the service file as you were trying to do.
// service.js
var express = require('express')
var router = express.Router();
...
app.use('/users', require('./User/Users')(router, logger));
app.use('/events', require('./Event/Events')(router, logger));
...
All the routes in this file will be prefixed by the /users because we have mentioned it in the service file.
// Users.js
module.exports = function (router, logger) {
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
logger('users middleware', req); // or however you are using your logger
next();
})
// this route will be `/user/`
router.get('/', function (req, res) {
res.send('Users home page')
})
// this route will be `/users/about`
router.get('/about', function (req, res) {
res.send('About users')
})
return router;
};//exports
It's pretty straight forward I guess. You can refer this boilerplate, I made a while back ago using express and express router. Some naming convention is not good but yeah you can see how the router is defined and used in the boilerplate to enable route prefixing and middleware for specific routes.

Related

passport and serving files nodejs

i am using passport with google strategy for authentication
my folder structure:
views
home.html
enter.html (this has just one google+ button)
app.js
routes
auth.js (for google login)
i want the client to be directed to enter.html and not be able to use home.html if req.user is not set ( req.user is set when user is authenticated using google )
once authentication is done user should be redirected to home.html
app.use(express.static()) makes both of them available which is not what i want
the google login page comes by auth/google
and i also need to know what i should keep as the callback uri
in app.js
i have done mongodb configuration
i have done passport configuration
what to do next?
in auth.js
const router = require('express').Router();
const passport = require('passport');
router.route('/google')
.get(passport.authenticate('google', { scope: ["profile"] }));
router.route('/google/redirect')
.get(passport.authenticate('google'), (req, res, next) => {
// res.redirect what
});
module.exports = router;
To serve the home.html page you could redirect to a protected home route. Here an example of how I would go about implementing this.
auth.js
router.route('/google/redirect')
.get(passport.authenticate('google', { failureRedirect: '/' }), (req, res, next) => {
// Set to redirect to your home route / html page
res.redirect('/home')
});
To prevent users from going to home without authorization, you should also add a route guard to your /home route.
routes.js
const { checkAuth } = require('./guards'); // Protected routes
router.get('/home', checkAuth, async (req, res) => {
res.render('home')
});
guards.js
module.exports = {
checkAuth(req, res, next) {
if (req.isAuthenticated()) {
return next()
} else {
res.redirect('/')
}
},
}

How to automate next() call in every route function? (express.js)

Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)

Request parameter in Express router

I'm having some trouble accessing request parameters in express router.
My server.js file has this:
app.use('/user/:id/profile', require('./routes/profile');
And this is in my ./routes/profile.js file:
router.get('/', (req, res) => {
console.log(req.params.id);
}
But the console log prints undefined.
I'm new to express and feel like I'm missing something basic about how routing works.
Can someone please help me out?
Here is my full server.js:
const express = require('express');
const app = express();
app.use(express.json());
app.use('/user/:id/profile', require('./routes/profile'));
app.listen(5000, () => console.log('Listening'));
Here is my full profile.js:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
console.log(req.params.id);
res.status(200).send('In profile route');
});
module.exports = router;
URL parameters are not exposed to routers. You have a couple of options here:
Parse req.originalUrl to get the user id (not recommended). req.originalUrl isn't affected by the router's mount point and will remain as /user/112/profile or whatever url you visited.
Add some middleware to expose the id (recommended). Your new route statement will end up looking like this:
(Now you can use req.userId in your profile.js)
app.use('/user/:id/profile', function(req, res, next) {
req.userId = req.params.id;
next();
}, require('./routes/profile'));
Change the mount point from /user/:id/profile to /user, then edit your router to listen on /:id/profile (not recommended).

Route requiring a middleware function

I'm creating my routes module in nodejs with socket.io
var express = require("express"); // call express
var taskSchema = require("../models/taskModel");
var mongoose = require("mongoose");
var router = express.Router(); // get an instance of the express Router
module.exports = function (io) {
router.use(function (req, res, next) {
io.sockets.emit('payload');
console.log("Something is happening.");
next();
});
router
.route("/tasks")
.post(function (req, res, next) {
...
});
router
.route("/tasks")
.get(function (req, res) {
...
});
};
When I compile server I get this error
TypeError: Router.use() requires a middleware function but got a undefined
It appears to me that the problem is probably in the code that loads this module because you never export the actual router. So, assuming you do app.use() or router.use() in the caller who loads this module, your aren't returning the router from your function so there's no way to hook that router in and you would get the error you see.
I'm guessing that you can fix this by just returning the router from your exported function:
var express = require("express"); // call express
var taskSchema = require("../models/taskModel");
var mongoose = require("mongoose");
var router = express.Router(); // get an instance of the express Router
module.exports = function (io) {
router.use(function (req, res, next) {
io.sockets.emit('payload');
console.log("Something is happening.");
next();
});
router
.route("/tasks")
.post(function (req, res, next) {
...
});
router
.route("/tasks")
.get(function (req, res) {
...
});
return router; // <=========== Add this
};
Then, when you do:
let m = require('yourModule');
router.use(m(io));
Then function will return the router that router.use() will be happy with. You can pass either middleware or a router to .use().
If this guess isn't quite on target, then please show us the code that loads and calls this module.
When that function is called it's gonna return the equivalent of undefined. Also, normally a route is defined before the endpoint. It's typically structured like:
let myRouter = new Router();
Router.use('something', middlewareFunction, someotherprocess);

Breaking out express routes into separate files

I have tried every answer I've found on s/o, and I'm sure I must be missing something. What doesn't error on me instead gives me a 404. I tried answers from Organize routes in Node.js, strongloop's route-separation pattern, the answers from How to include route handlers in multiple files in Express?, hit similar errors as in Router.use requires middleware function? but none of those answers worked, either. The answer for Unable to Split Routes into Separate Files in Express 4.0 doesn't error, but also 404s. It seems like each answer has a different syntax and style, and maybe it's that I'm mixing and matching incorrectly?
Right now my /routes/persons.js has this pattern:
var express = require('express');
var persons = express.Router();
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
module.exports = persons;
In my server.js file, I've got:
var persons = require('./routes/persons');
app.use('/persons', persons);
This combination doesn't throw errors, but it also doesn't do anything. I've tried adding the endpoint to server.js lines:
var persons = require('./routes/persons');
app.get('/persons/:user_id', persons.addpersons);
and stripping persons.js down to just export functions:
exports.addpersons = function (req, res, next) {
var list = req.body;
// etc
}
Plus variations like wrapping the whole person.js file in module.exports = function(), sticking module.exports = router at the end, using app instead of router, etc.
What am I overlooking? Should I be adding some other middleware, rearranging how I call the endpoint, using app, or sticking with router.route? What are the most likely culprits when there's no error but the endpoint is still 404'ing?
many thanks in advance!
============= EDITED TO INCLUDE SERVER.JS =============
Since it's clear something is set wrong, somewhere, here's my server.js file:
var express = require('express');
var app = express();
var methodOverride = require('method-override');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var router = express.Router();
var jwt = require('jsonwebtoken');
var config = require('./config');
var nodemailer = require('nodemailer');
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var async = require('async');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'email#gmail.com',
pass: 'password'
}
});
// I don't know if both are necessary, used multiple conflicting tutorials
app.use(require('express-session')({
secret: 'secret',
resave: false,
saveUninitialized: false
}));
app.set('superSecret', config.secret);
var Schema = mongoose.Schema,
Person = require('./models/person.js'),
User = require('./models/user.js'),
Event = require('./models/event.js');
var port = process.env.PORT || 8080;
mongoose.connect(config.database);
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(express.static(__dirname + '/public'));
// routes go here
app.use('/api', router);
app.listen(port);
console.log('gogogo port ' + port);
I have no idea where else I might look for why including routes requires such a break in the usual pattern. My config files? My procfile? Those are the only other files sitting on the server, not counting /models and /routes.
The key here is to understand what app.use() does to your req object (in particular to req.path), how app.get() and friends are different, and how Express wraps path-to-regexp (its internal path matching module) to handle routes.
1) app.use(path, middleware) mounts the middleware. Inside the mounted middleware/router, req.path is relative to the mount path. Only the beginning of the request path needs to match, so /foo will work for requests at /foo (relative path will be /), /foo/bar (relative path is /bar), etc.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.use('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Try running the setup above, navigate to localhost/foo and see the following logs:
Main: GET /foo
In /foo: GET /
2) app.get(path, middleware), app.post(path, middleware) etc. do not mount the target middlewares, so req.path is preserved. req.path must match the whole pattern you defined your route with, so /foo will only work for /foo requests.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.get('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Navigate to localhost/foo and see :
Main: GET /foo
In /foo: GET /foo
3) app.route(path), as explained in the Express docs, is just a convenience to define multiple app.get(middleware), app.post(middleware) etc. sharing the same path.
Now in your case, here is a working setup:
main
var persons = require('./routes/persons');
app.use('/persons', persons);
routes/persons.js
var router = require('express').Router();
router.route('/:user_id')
.post(function (req, res) {
// handle potato data
})
.get(function (req, res) {
// get and send potato data
});
module.exports = router;
This is convenient as you only have to set the /persons entry point once in your main file, so you can easily update it later on if needed (you could also import that path value from a config file, from your router object or whatever, Node is pretty flexible in this regard). The persons router itself takes care of its business controllers, regardless of where it is exactly mounted at.
I FIGURED IT OUT!
Of course, this might be the totally wrong way to go about it (pls tell me if so) but it WORKS.
in my server.js file, I have:
var persons = require('./routes/persons');
router.get('/persons/:user_id', persons);
router.post('/persons/:user_id', persons);
and my persons.js file now looks like this:
var mongoose = require('mongoose');
var express = require('express');
var router = express.Router();
var Schema = mongoose.Schema,
Person = require('../models/person.js');
router.post('/persons/:user_id', function (req, res) {
var potatoBag = req.body;
Person.collection.insert(potatoBag, function onInsert(err, potatoBag) {
if (err) {
return res.json(err);
} else {
res.status(200).end();
}
});
});
router.get('/persons/:user_id', function(req, res) {
var id = req.params.user_id;
Person.find({'user_id':id},function(err, person) {
if (err)
return res.json(err);
res.send(person);
});
});
module.exports = router;
This seems like more overhead than most of the examples, but maybe it's because of a) using router.route and b) using imported schemas? I also had (req, res, next) in there, and it threw fits until I removed the next pieces. Probably still a bit awkward, but hey, it's working. Thanks for the help, everyone!
instead of
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
do:
persons.put('/persons/:user_id',function (req, res, next) {
// etc
});

Categories

Resources