Express: Disable headers for specific routes (Etag, set-cookie, etc) - node.js

For my application, we have rest API and the webapp server from the same app. (it is small enought not to have separate deployment)
Is there any way I can exclude all /api/* route paths to disable caching and cookies?
Note: I cannot do app.disable('etag') as it will disable for the entire webapp.

Afaik this is currently not possible - also there are a few open issues on github like this one for example: https://github.com/expressjs/express/issues/2472
As a workaround you could remove the headers for requests on the /api-route using something like this:
const onHeaders = require('on-headers')
// mount custom middleware for all api-requests
app.use("/api*", (req, res, next) => {
removeHeaders(res);
next();
});
function removeHeaders(res) {
onHeaders(res, () => {
res.removeHeader('ETag');
// remove other headers ...
});
}

Related

Node.js REST API versioning the right way?

I would like to manage my REST API based on URL version specifying.
For example:
api.mydomain.com/v1/rides/
// will return all rides based on v1.
api.mydomain.com/v2/rides/
// will return all rides based on v2 (probably with some braking changes).
api.mydomain.com/rides/
// will return all rides based on v2, since v2 is the newest.
Thats awesome.
Before we get started dealing with the practical way of handling this,
we should talk about the logical "default newest versioning" - I mean, if user does not going to specify any kind of version, should I serve him with the newest version or throw a 404 not found error?
Should I oblige the user for specifying an API version?
If I do, is there any standard of "parsing" the specific / newest version?
I tell you why im concern about this: Lets say that "Dan" have app installed which relays on the newest API endpoint (V1 for example), then I release V2 which has braking changes.
Since Dans "listens" to the newest version by default, Dans app is going to be crashed.
That is not a good behaviour at all.
Maybe should I prevent using the "default newest versioning"?
Maybe should I use Dans app to listen for a specific version, while remote developers accessing my API as a web service can have the privilege to choose between specific version or the newest by default?
Is there any standard?
**
Now lets talk practically. Lets say that I have a router handling those requests, maybe something like this:
// app.js file
app.use((req, res, next) => {
try {
require('../resources/' + req.url.split('/')[1] + '/' + req.url.split('/')[1] + '-router')(app);
next();
} catch(err) {
dep.cast(res, 404, new Error("Not Found"));
}
});
And some handler, like this:
// resources/rides/rides-router.js file
module.exports = function(app) {
// GET ride - select a ride
app.get("/v1/rides/:id", dep.verifyToken(), require('./api/v1/get-ride'));
app.get("/v2/rides/:id", dep.verifyToken(), require('./api/v2/get-ride'));
// POST ride - insert a new ride
app.post("/v1/rides", dep.verifyToken(), require('./api/v1/set-ride'));
}
As you can see, I have handler which sends the requests to the specific divisions in the API, split by V1, V2, etc..
It makes me wonder if its right to have the same page containing the same function over and over in different folders, one for V1 and one for V2.
Ofcourse, with some braking changes, but they are probably going to be similar. Is not it bordering with repetitive code?
Look at the project structure:
What do you think about this?
Instead of adding version in every route you can add it in app level. So It won't be tightly coupled with API route.
import * as express from 'express';
// v1/get-ride.js
const router = express.Router();
router.post('/rides/:id', dep.verifyToken(), (req, res) => {
// Your code
});
app.use('/v1', router);
// v2/get-ride.js
const router = express.Router();
router.post('/rides/:id', dep.verifyToken(), (req, res) => {
// Your code
});
app.use('/v2', router);
I would recommend using node-express-versioning module instead.
It would help you to support multiple versions without changing the url of API, just send the version of API and direct the call to that version route-controller.
*
*//version taken out from header
app.use(function(req, res, next)
{
req.version = req.headers['accept-version'];
console.log(req.version);
next();
});
//version path defined
app.use('/api', versionRoutes({
"1.0.0": respondV1,
"2.0.0": respondV2
}));
function respondV1(req, res, next)
{
app.use('/api',routeV1);
next();
}
function respondV2(req, res, next)
{
app.use('/api',routeV2);
next();
}*
*
There's no "right way" to do API versioning.
However, URI based global versioning is not at all RESTful.
Phil Sturgeon of "APIs you won't hate" recommends an API Evolution approach instead.
See: https://apisyouwonthate.com/blog/api-evolution-for-rest-http-apis
Have you considered this as an option?

Node express api routes for multilingual directory like url

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .
having 15 routes like this now:
app.get('/terms', function(req, res) {
res.render('terms',{
...
});
});
how it have to be set for routes like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Should I duplicate this routes and add for example a locale for
each like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Or Should do something like:
if cookie['lang'] && cookie['lang'] is in locales
// then redirect to /:lang/terms
else
// show default language in /terms
if req.headers["accept-language"] && req.headers["accept-language"]
// then redirect to /:lang/terms
else
//show default language in /terms
Or there is another way I should approach this that follows good practices or is better respecting standards?
Miro's Answer in :
How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...
Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts
var rxLocal = /^\/(de|en)/i;
...
app.get(/\/(de|en)\/login/i, routes.login);
thanks in advance
You need to consider 2 things :
1. How get the local :
Accept-Language
The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.
+Normalized
+Natively support by brower
-Not easy to by passe by the end user
Path / Cookies
You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.
You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).
Both
To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.
2. Use the local:
Each methods to get the local can be used from different way. I will
use random of them in exemple but they are all compatible.
Top level configuration.
In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.
app.use('/:local' (req, res, next) => {
let localKey = req.param('local');
res.locals = // Some ingenious method to get the locales from localKey
next();
}
Check res.locals and your engine documentation.
Use it in controller.
If the local is part of the contoller process. You can get directly is value in controller.
In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.
app.use((req, res, next) => {
let local = req.cookies.local;
if(!local) local = req.acceptsLanguages();
if(!local) local = 'en-US';
req.local = local;
}
Both
You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.
When you use middle where witch impact the controllers, be sure you declare them before your routes.
You can use a route parameter to get the locale from the URL, like this:
app.get('/:lang/terms', function (req, res) {
if (req.params === 'es') {
res.send('¡Hola!');
else {
res.send('Hi!');
}
});
The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang.
See express routing documentation for details.

per-request session in meteor server?

I am adding an auth layer and I think I have it figured out except for one tricky detail.
My Meteor app doesn't have any routes but I've added a hook into the connect middleware so that the "/" route errors if there isn't a correct API token. If the token is okay then I call next() to forward the route to Meteor.
The problem is that, depending on the token, I need to set server-side parameters for the connection, and I don't know how to do this. For example, say I have a static list of API keys mapped to permission levels. If a user sends a request with "ADMIN_API_KEY" then I would like to set Session.permission_level = "admin" for use by the Meteor server's functions. Session is just for the client in Meteor, though.
# this code's in coffeescript
WebApp.connectHandlers.use '/', (req, res, next) ->
validator = new RequestValidator(req, next)
validations = [
"valid_namespace",
"only_https"
]
error = validator.validate(validations)
next(error)
# <<<<<<<<<<<<<<<<<<<<<<<<
# Here I want to set some config option which can be
# read by the server in the same way it can read things like
# Meteor.user()
In Rails I would just say session[:permission_level] = "admin". But it seems to not work this way in Meteor.
By the way, I am not using a Routing package yet in Meteor, though if that would make this easier than I would.
I'm not sure about Session I've been doing something like
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';
export const authMiddleware = (req, res, next) => {
const userId = identifyUser(req); // parse the request to get the token you expect
if (!userId) {
return next();
}
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), () => {
next();
// in that context, Meteor.userId corresponds to userId
});
};
for my REST api and that works well regarding the user Id and being able to call Meteor function that should be invoke in a DDP context, like Users.find(...).

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

Is it possible to apply basic authentication / middleware in on routes with a whitelist in Express?

I'm implementing a RESTful API with Express in Node, and I'm new to both. I'd like to use basic authentication to control access.
I would like to apply it using something like a whitelist but I'm not sure how to do that.
Blacklisting is easy, I can just pepper my #VERB calls with the second argument:
app.get('/', asyncAuth, requestHandler);
I can take that even further and blacklist everything with:
app.all('*', asyncAuth, requestHandler);
But I want to apply my basicAuth to every single route, except for POST /users. Is there an elegant way to do that? Can I use the 'blacklist' approach then selectively remove it from the routes I'd like? I couldn't figure out how.
Define your route for POST /users before the blacklisted routes:
app.post('/users', function(req, res) {
...
});
app.all('*', asyncAuth, requestHandler);
You could maintain a list of regexps that are whitelisted, and match the url against each url in the list, if it matches any then proceed, else require auth
app.all('*', asyncAuth);
function asyncAuth(req, res, next) {
var done = false;
whitelist.forEach(function(regexp) {
if (req.url.match(regexp)) {
done = true;
next();
}
});
if (!done) requireAuth(next);
}
Something along those lines

Resources