Express.js - prepend sub-route to all defined routes - javascript

Let's say I have an Express app defined in a file, say server.js like this:
const app = express();
app.use('/foo', foo);
app.use('/bar', bar);
module.exports = app;
I import this Express app in another file, say index.js:
const app = require('./server');
const port = process.env.PORT || 3000;
const listen = (port) => {
app.listen(port, () => {
console.log(`Backend listening on port ${port}!`);
});
};
listen(port);
Now, the routes that are available for this app are /foo and /bar.
Is there a way to edit configuration in the index.js file so that the routes become /api/foo and /api/bar? Without touching server.js file.
Use case:
I have a Nuxt.js app with a backend that is loaded into the Nuxt app via serverMiddleware property in nuxt.config.js like this:
serverMiddleware: [
...
{ path: '/api', handler: '~/server.js' },
],
This has the effect similar to what I described above: it imports the express app from server.js app and prepends all its routes with /api.
However, often I don't want to develop the frontend part of the Nuxt app, I just want to do changes on the backend. For this purpose I have a helper file like index.js above, which runs backend only. (Frontend often takes long time to compile, that's why I don't want to compile it when I don't need to.)
This creates a problem that all the routes are slightly different - they lack the /api at the beginning. The routes are being used in different tools like Postman etc. and suddenly they wouldn't work.
My current solution is to define index.js file in the same way as server.js file with all routes defined like I want them - instead of app.use('/foo', foo); there's app.use('/api/foo', foo); etc. but this has its own problems, e.g. if I change server.js I have to change index.js. I am looking for something more elegant.

According to the express 4.0 docs https://expressjs.com/en/4x/api.html#app.use you can use an application instance the same as you would a router. In short, just use the export of your server.js as a middleware at the route in which you want to insert it, as opposed to directly calling .listen() on it.
Here is some demo code that worked for me:
const express = require('express');
const app_inner = express();
app_inner.use('/foo', (req,res) => res.send('foo'));
const app_outer = express();
app_outer.use('/foo2', app_inner);
app_outer.listen(9999);
// web browser at localhost:9999/foo2/foo returns 'foo' as expected

Related

Is it possible to use application level middleware in express router?

I've been playing around with setting up a basic atlassian-connect-express (ACE) application, and have modified the starter code provided by the ACE package to be suitable for serverless deployment. One of the problems I faced after doing this was that routing is now divided into stages, e.g. /dev, /prod. I did a bit of research and found that a way to deal with this would be to use an express Router and mount it to the appropriate endpoint for the stage being deployed to. The problem I then faced is that the authentication middleware provided by ACE seems to be application level and then can't be used by each router.
Typically the routes were added to the express app like this:
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use(addon.middleware());
routes(app, addon);
and in ./routes/index.js
export default function routes(app, addon) {
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
app.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
app.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
// Add additional route handlers here...
}
I've changed ./routes/index.js to work as a router object and export that, however this leaves me unable to use the addon.authenticate() middleware
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use('/dev', require('./routes'));
and in ./routes/index.js
const express = require('express');
const router = express.Router();
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
router.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
router.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
module.exports = router;
Obviously having no knowledge of addon, the router cannot use that authentication middleware.
Is it possible to pass that middleware through to the router when attaching it to the application? If not, is there another way I can handle URL prefixes without using a router?

Exporting HTTP server for socket.io not working

I have a app.js
const express = require('express');
const app = express();
const server = require('./server.js');
// app.use
const io = require('socket.io').listen(server);
io.on('connection', function (socket) {
...
});
module.exports = app;
And a server.js
const app = require('./app');
const server = app.listen(5000 || process.env.PORT, () => {
console.log('App listening on port 5000!');
})
module.exports = server;
If I put the server in a separated file the socket is not working, but if I start the server inside the app.js the socket works.
What I'm doing wrong?
The issue here is that you have a circular dependency where app.js is loading server.js and server.js is loading app.js. You can't do that for this type of code.
It has an issue because you're trying to load server.js from within app.js and then in the process of loading server.js, it attempts to load app.js and get its exports, but app.js hasn't finished loading yet and thus hasn't even returned its exports yet. So, the loader either thinks there are no exports or recognizes the circular request (I'm not sure which), but in either case the exports from app.js don't work because of the circular requires.
There are several different ways to solve this. The two most common ways are:
Break some code into a common third module that each of these can load and only have one of these load the other.
Rather than having server.js load app to get the app object, have app.js pass the app object to server.js in a constructor function rather than trying to execute at module load time.
Here's how the constructor function idea would work:
app.js
const express = require('express');
const app = express();
// load server.js and call it's constructor, passing the app object
// that module constructor function will return the server object
const server = require('./server.js')(app);
// app.use
const io = require('socket.io').listen(server);
io.on('connection', function (socket) {
...
});
module.exports = app;
server.js
// export constructor function that must be called to initialize this module
module.exports = function(app) {
const server = app.listen(5000 || process.env.PORT, () => {
console.log('App listening on port 5000!');
});
return server;
};
So, rather than server.js trying to load the app.js module to get the app object, the app object is "pushed" to it with a constructor function. This prevents the circular dependency.

Can someone explain how i can use module.exports this way

I am trying to understand what is going on when I use module.exports with a variable in a model view controller type project. I just do not understand what the book means by using it in this way
var express = require("./config/express.js");
var app = express();
app.listen(3000);
module.exports = app; // my problem is right here what is it doing
console.log("Server running at http://localhost:3000/");
my config/ express file is also here
var express = require("express");
module.exports = function()
{
var app = express();
require("../app/routes/index.server.routes.js")(app);
return app;
}
The first example is exporting the Express app directly and the second example is exporting a function that returns the Express app.
This means that in the first example, require(...) will return app. In the second example, you would need to do require(...)() to have app returned.

node js included route file does not allow end point

I have a node project and something very strange is happening. I have a routes file that has all of my routes and I import that on my server.js file. When I import the routes file. A get request of that route does not work at all, but when I paste that same route on the server.js it works as expected. Other routes in the file work correctly, but this one in particular does not work as expected. Code below. Thanks for any help.
server.js
require('./server/routes/mainRoutes.js')(app);
mainRoutes (this will not work):
app.get('/accounts',function(req,res){
res.json({Test:'name'});
});
Now if I put that /accounts route on the main server.js it will work. For some reason it never works in the imported mainRoutes file.
I'm not sure why it's not working, It can help if you share the app initiation (I understand you use express?) and your use of app.listen.
Anyway, a best practice would be to use app.use, as follows:
server.js:
var routes = require('./server/routes/mainRoutes.js');
var express = require('express');
var app = express();
app.use('/', routes.app);
var server = app.listen(somePort, someIP, function() {
console.log('Listening...');
});
mainRoutes.js:
var express = require('express');
var app = express();
app.get('/accounts',function(req,res){
res.json({Test:'name'});
});
module.exports = {
app: app
};

Does module.exports have to be defined when require()-ing a file?

I'm using Babel to transpile my ES6 files on the fly on an Express server. In my server.js file (vanilla JS), I put require('babel-core/register') and require('./app').
In app.js (ES6), I do all my normal Express stuff stuff:
import express from 'express';
let app = express();
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
... etc
Even though I don't formally define module.exports in my app.js file, when I run node server, app.js gets correctly required and runs. Why does this work?
When you require a module, the code inside that module will be executed, but it is executed only once. Subsequent require calls to the same module will have no effect except to return whatever the module exports. That's the nature of node modules (look for the note on 'caching'). By using module.exports you are basically designating a return value for it.
You don't need to export anything in your case, the app.listen line is called as soon as you require app.js. Although you could export an API or something like this if you wanted to:
/*app.js*/
import express from 'express';
let app = express();
module.exports = {
start:function(){
app.listen(3000, () => {
console.log('Server listening on port 3000');
})
}
/*server.js*/
require('babel-core/register');
var app = require('./app');
app.start();
I might be misunderstanding the question, but I don't think babel and express are relevant really.

Categories

Resources