Express.js - Helmet.js and other middleware, and mounted applications? - javascript

In a recent learning project, I'm using three Express.js applications to separate the project into more manageable pieces.
One application is the "primary" app, the one that listens for connections. The other two are mounted at specific routes on the primary app.
Is it sufficient to call app.disable('x-powered-by'); on the primary app to disable the X-Powered-By header, or would this need to be done in each of the mounted apps as well?
Similarly, I'm looking into using Helmet.js to try and add a bit of additional security to the entire project. Is it enough to include any middleware from Helmet.js on the primary app, or would these also need to be defined in the mounted apps?
I don't feel as though I understand how some settings and middleware affect mounted Express.js apps, and would appreciate further explanation from anyone with more experience.
Edit: After playing with app.disable('x-powered-by') and examining responses from the server, the X-Powered-By header appears if I don't disable it in both the primary application instance and any mounted application instances. I therefore presume Helmet.js middleware operate the same way, but I'm not 100% certain. Can anyone confirm if this is the expected behavior?

You're right about everything you've said.
It sounds like you're doing something like this:
var express = require('express')
var mainApp = express()
var miniAppA = express()
var miniAppB = express()
mainApp.use('/a', miniAppA)
mainApp.use('/b', miniAppB)
mainApp.listen(3000)
This is an okay way to do things, but headers will be overridden in the sub-apps, as you saw.
You can use Express 4's routers feature to mitigate this. Instead of making new mini-apps with express(), you can use express.Router(). These are Express apps with fewer features (for example, they don't set headers the same way).
Something like this might solve your issue:
var express = require('express')
var mainApp = express()
var miniAppA = express.Router()
var miniAppB = express.Router()
mainApp.use('/a', miniAppA)
mainApp.use('/b', miniAppB)
mainApp.listen(3000)

Related

How to access underlying express app in Keystone 6?

I'm using Keystone 6 for my backend and I'm trying to integrate Stripe, which requires access to the underlying express app to pass a client secret to the client from the server. This is highlighted in the Stripe documentation here: https://stripe.com/docs/payments/payment-intents#passing-to-client
I'm having trouble figuring out how to access the express app in Keystone 6 though and they don't seem to mention anything about this in the documentation. Any help would be appreciated.
The short answer is Keystone 6 doesn't support this yet.
The longer answer has two parts:
This functionality is coming
We've been discussing this requirement internally and the priority of it has been raised. We're updating the public roadmap to reflect this next week.
The functionality itself should arrive soon after. (Unfortunately I can't commit to a release date.)
Getting access to the Express app is possible, it's just a real pain right now
If you look at Keystone's start command you can see where it
calls createExpressServer().
This just returns an express app with the GraphQL API and a few other bits and bobs.
But there's actually nothing forcing you to use the build in keystone start command – You can copy this code, hack it up and just run it directly yourself.
Eg. you could replace this...
const server = await createExpressServer(
config,
graphQLSchema,
keystone.createContext,
false,
getAdminPath(cwd)
);
With...
const server = express();
server.get('/hello-world', (req, res) => {
res.send('Hello');
});
const keystoneServer = await createExpressServer(
config,
graphQLSchema,
keystone.createContext,
false,
getAdminPath(cwd)
);
server.use(keystoneServer);
And your /hello-world endpoint should take precedence over the stuff Keystone adds.
Unfortunately, this doesn't work for the dev command so, in your local environment you'll need to do it differently.
One option is to start a second express server that you control and put it on a different port and include your custom routes there.
You can still do this from within your Keystone app codebase but having different URLs in different environments can be annoying.
You'll probably need an environment variable just for your custom endpoints URL, with values like this in production:
# Production
GRAPHQL_ENDPOINT="https://api.example.com/api/graphql"
CUSTOM_ENDPOINT="https://api.example.com/hello-world"
And this in dev:
# Dev
GRAPHQL_ENDPOINT="http://localhost:3000/api/graphql"
CUSTOM_ENDPOINT="http://localhost:3100/hello-world"
It's ugly but it does work.
I'll update this answer when the "official" functionality lands.

Is there any purpose of splitting up express and app?

I see these two lines in ( every ?) Express app.
const express = require('express');
const app = express();
I have to wonder if any parameters can be passed to express()?
Checked here and did not see any
https://expressjs.com/en/4x/api.html
Why are some methods on express() and some on app()?
It seems like they should be combined and have all methods on one object.
express does not take any parameters, no. The purpose of calling it is to create the app object, so the fact you have to call it does make sense, even without parameters.
Another way you often see that written is:
const app = require("express")();
It'll need to be separate again when using ESM, though.
import express from "express"; // Or similar
const app = express();
In a comment you've said:
For example, is there anything useful I can do with out creating app? Is there anything I can do only with express?
As far as I know, you have to have create at least one Application object to do anything useful with Express. Note that you don't have to create just one app. That's the typical use case, but there's no reason you can't create multiple apps running on different ports.
You may be wondering why express can't just give you an Application object directly from require. The reason is that modules are only loaded once and cached, so what you get from require is shared. So Express exports the express function, which you use to create the Application object (or objects, plural, if you want more than one).
Is there anything I can do only with express?
The reason for doing two separate lines like this:
const express = require('express');
const app = express();
instead of this:
const app = require('express')();
is that the express module has other methods on it that are sometimes needed such as:
express.static(...) // middleware for serving static files
express.json(...) // middleware for parsing json requests
express.urlencoded(...) // middleware for parsing urlencoded requests
express.Router(...) // constructor for a new Router object

Best practice for language sub domains in an Express server?

So I have a Node/Express server set up, and we are making the transition from subdirectories to sub domains for localisation, eg:
es.example.com // old way
www.example.com/es // new way
Doing this for a variety of reasons, but mostly to facilitate the preservation of JWT login state across the internationalised content. What is the best practice for allowing this in express when declaring routes?
If I have a route simply set up like this:
app.use('/search')
Then when I try to hit a route, eg www.example.com/es/search, I will just be redirected to www.example.com/search, the es will be stripped. However, this can be fixed like this:
app.use('*/search')
I'm sure this must have some negative implications though? How do people typically allow for prefixes in routes via Express?
If you are using Express 4, you can use modular routes to create a set of routes that can then be shared across your different locale codes. This will ensure that you only apply routes to specific locale codes rather than all possible words prior to your route (such as /fakePath/search).
// routes.js
var express = require('express');
var router = express.Router();
router.use('/search');
module.exports = router;
// index.js
var routes = require('./routes');
app.use('/en', routes);
app.use('/es', routes);
// ...

Send URL string to other script with Node.js

I am starting to learn Node.js, using Express with Jade and Mongoose as my default toolbox. I used to develop in PHP, migrated to Python, and learned MVC through Django. Having a large client-side JS game and some inspiration from Mozilla.org, I am willing to make a multiplayer game -- and saw this as a noncommercial opportunity to learn Node: I can take my time with it.
However, I ran into a problem. I'm not trying to write an MVC system on my own, just to separate my site's "apps" like most MVCs do. The question is probably basic -- having this chunk of code:
app.get(/^blog/, function(req, res) {
require("./blog")();
});
... I understand the basics of Node/Express' URL masking, however I need to pass the rest of the URL string (everything that's after mysite.com/blog) to another URL parsing script, inside the blogapp.
I googled around for a while and couldn't find a good solution. I even found a full tutorial on building an MVC scheme in Node and Express written for an older Express version, but that's a bit over the top for now. Can you provide me a simple solution?
I think blog/index.js should look something like this:
module.exports = function(urlstring) {
if(urlstring.indexOf('post') != -1) {
// do stuff...
}
else if(urlstring === '/') {
// return home.jade or something
}
};
I hope I'm being clear. Thanks in advance!
With express there is no need to parse your URLs on your own. I guess you'll want to build your blog URLs somehow like this
/blog Show a list of blog posts
/blog/post/1 Show blog post with id '1'
With express 4 you can set up a router for your blog path or a mounted app. Mounted apps allow you to let an app handle all sub URLs of a base URL path. See the express documentation for more detail.
I'd like to demonstrate how you can use the express 4 router together with the mounting feature of express to build blog routes.
// Set up express app
var app = express();
// Set up a router
var router = express.Router();
router.get('/', function(req, res) {
// Show a list of blog posts
}
router.get('/post/:id', function(req, res) {
// Show a single blog post
}
// Mount router
app.use('/blog', router);
A benefit of this solution is that your routes registered in the router always get relative URLs with out the /blog prefix so you may reuse your blog routes in some other project under a URL like /companyblog.

Specifying routes by subdomain in Express using vhost middleware

I'm using the vhost express/connect middleware and I'm a bit confused as to how it should be used. I want to have one set of routes apply to hosts with subdomains, and another set to apply for hosts without subdomains.
In my app.js file, I have
var app = express.createServer();
app.use...(middlware)...
app.use(express.vhost('*.host', require('./domain_routing')("yes")));
app.use(express.vhost('host', require('./domain_routing')("no")));
app.use...(middlware)...
app.listen(8000);
and then in domain_routing.js:
module.exports = function(subdomain){
var app = express.createServer();
require('./routes')(app, subdomain);
return app;
}
and then in routes.js I plan to run sets of routes, dependent on whether the subdomain variable passed in is "yes" or "no".
Am I on the right track or is this not how you use this middleware? I'm a bit confused on the fact that there are two app server instances being created (as that's how examples on the web seem to do things). Should I instead pass in the original app server instance and just use that instead of creating a separate one instead the subdomain router?
Yes, you are on the right track. You should have a different server instance for each of the vhost. Be it a http.Server or express app.
If you pass the original app, a request you sent to the vhost will be emitted to the original app. So, unless the vhost has paths which are not used in original server, it will get response as if the request was sent to original server.
From the connect docs
connect()
.use(connect.vhost('foo.com', fooApp))
.use(connect.vhost('bar.com', barApp))
.use(connect.vhost('*.com', mainApp))

Categories

Resources