How to structure Express server to use granular API endpoints - node.js

I currently have an Express server I'm using for a mobile app which is structured as follows (server.js):
const PostRouter = require('./api/production/Post');
const UserRouter = require('./api/production/User');
...
app.use('/posts', PostRouter)
app.use('/users', UserRouter)
and then in api/production/Post I have:
router.get('/fetch', (req, res) => {
...
}
router.get('/delete', (req, res) => {
...
}
etc..
However, I would really like to rebuild the server to match the structure of my corresponding NextJS app and its API structure, which would be something like:
/api/posts/
add-post/
index.js
fetch-all/
index.js
edit-post/
index.js
Where each index.js file contains just one endpoint/query instead of the current structure where each file has multiple queries with the router.get thing.
It looks like this is possible by creating a Router for each endpoint with something like:
const PostFetchAllRouter = require('./api/posts/fetch-all');
const PostEditPostRouter = require('./api/posts/edit-post');
...
app.use('posts/fetch-all', PostFetchAllRouter)
app.use('posts/edit-post', PostEditPostRouter)
What would be the best way to do this, please? Is there an easier way to do this without all the boilerplate in the server.js file? I'm very new to Express - please excuse if it's a naive question

You could move the "boilerplate" code to the different router files and build a router chain. But you have to write a little bit more.
server.js
|-api/
|--posts/
|---PostsRouter.js
|---fetchAll.js
|--users/
|---UserRouter.js
fetchAll.js
const express = require("express");
const FetchAll = express.Router();
FetchAll.get("/fetch", (req, res) => { res.send("/posts/fetch") });
module.exports = FetchAll;
PostsRouter.js
const express = require("express");
const FetchAll = require("./fetchAll");
const PostsRouter = express.Router();
PostsRouter.use(FetchAll);
module.exports = PostsRouter;
server.js
const express = require('express');
const PostsRouter = require("./api/posts/PostsRouter");
let app = express();
app.use("/posts", PostsRouter);
app.listen(80, () => {});
If you build it like that you would plug the small routers into the next bigger one and then use them in the server.js.
GET localhost/posts/fetch HTTP/1.1
// returns in my example the string "/posts/fetch"
Is that what you were looking for?

Related

Store webhooks functions inside a master function

I have set up multiple webhook endpoints that follow the similar structure in Node.js: 1) stringify the JSON request and 2) do something about that stringified request in R.
This consequently leads to a lot of duplicate code, and I attempt to stay DRY by creating a function in which I specify the arguments that actually do change. Here's an example.
First, the top portion of the script:
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
app.use(express.json());
Then, the part I would like to rewrite into a master function (WH) in which everything capitalized below between < and > becomes the argument.
app.post(<ENDPOINT>, foobar);
function foobar(req, res) {
var spawn = require("child_process").spawn;
let body = JSON.stringify(req.body);
var R_shell = spawn("/usr/local/bin/Rscript", [<PATH_TO_SCRIPT>, body]);
res.end("Processing completed");
}
app.get(<ENDPOINT>, (req, res) => res.send(`<html><body><h1>All working!</h1></body></html>
`));
Hence, with two endpoints, I'd end up with:
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
app.use(express.json());
WH(endpoint="foo", script_path="baz")
WH(endpoint="lorem", script_path="dolor")
P.S. Sorry if this is poorly formulated question from a Node.js standpoint—it's my first time developing with Node.js.
If I understood your question correctly, what you can do is something like this:
Firstly, you need to create a function that returns a router with the specified routes (You could create this function in a different file to make the code cleaner).
const {Router} = require('express')
function WH(endpoint, scriptPath) {
const router = Router()
function fn(req, res) {
var spawn = require("child_process").spawn;
let body = JSON.stringify(req.body);
var R_shell = spawn("/usr/local/bin/Rscript", [scriptPath, body]);
res.end("Processing completed");
}
router.post(endpoint, fn);
router.get(endpoint, (req, res) => res.send(`<html><body><h1>All working!</h1></body></html>`));
return router
}
And finally you should use it like this:
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
app.use(express.json());
app.use(WH("/foo", "./baz"))
app.use(WH("/lorem", "./dolor"))

Express Router - different routes with same suffixes

I have 2 routes:
/api/system/list - list my systems
/api/system/:systemId/books/list - list books of current system
And different file for each API:
systemAPI.js:
const list = router.get('/list', Validate(Validation.list), listHandler)
return {
routes: [list]
}
bookAPI.js:
const list = router.get('/list', Validate(Validation.list), listHandler)
return {
routes: [list]
}
Finally, use the above routes:
express.use('/api/system', systemAPI.routes)
express.use('/api/system/:systemId/book', bookAPI.routes)
The problem is, when I'm entering list-books API (/api/system/:systemId/books/list), its actually calling the list-systems API (/api/system/list)
Update: Solved!
I had 2 main problems:
routes order (in app.use(..))
use different instance of Express.Router() on each API
Refer the answer below for more information.
Try reversing the order of the routes and this will probably solve your problem:
express.use('/api/system/:systemId/book', bookAPI.routes);
express.use('/api/system', systemAPI.routes);
The reason is that express evaluates routes 'greedily' and it will even resolve to partial matches. So, it is important to keep the more specific routes first before the more general ones.
Also, I think you are using the express router wrong according to the documentation the systemAPI:
const express = require('express');
const router = express.Router();
router.get('/list', Validate(Validation.list), listHandler)
module.exports = router;
The bookAPI route:
const express = require('express');
const router = express.Router();
router.get('/list', Validate(Validation.list), listHandler)
module.exports = router;
Finally import the routers and use them:
const express = require('express');
const bookRouter = require('./bookAPI'); //set the file paths based on your file structure
const systemRouter = require('./systemAPI');
const app = express();
app.use('/api/system/:systemId/book', bookRouter);
app.use('/api/system', systemRouter);

Node.js with express routing and sub routing

I am playing with node.js and I don't quite understand why something I set up is working in one instance but if I make a slight change it will not work in another instance.
in my app.js I have
app.use('/musicplayer', require('./routes/music/index'));
in my music\index.js I have
var express = require('express');
var router = express.Router();
router.use('/users', require('./users'));
module.exports = router;
in my users.js I have this - working version
var express = require('express');
var usersRouter = express.Router();
var sqllite3 = require('sqlite3').verbose();
usersRouter.get('/login', function(req, res, next) {
res.render('music/login', { title: 'Express' });
});
module.exports = usersRouter;
But I would like to encapsulate the routes I am defining into another function like this not working this just hangs the page.
Modified version of my users.js not working
var express = require('express');
var usersRouter = express.Router();
var sqllite3 = require('sqlite3').verbose();
var router = function () {
usersRouter.get('/login', function (req, res, next) {
res.render('music/login', {title: 'Express'});
});
return usersRouter;
}
module.exports = router;
In the console I can see it comes in tries the get and nevers gets routed I see this "GET /musicplayer/users/login - - ms - -".
I have even put a console.log right before the return in the anonymous function I created to know it is getting in there and that I am hooking the pathways up right from the parent routes. And I do hit that log action to the screen.
Any help or tips would be appreciated:)
PS in case you are wondering I am trying to separate out apps for different development work I want to play with. So that is why I am doing the sub routing with musicplayer/index.js instead of just putting everything in the app.js for declaring of my main routes.
Router.use() expects an instance of another Router. However your (non-working) module only returns a function.
Use this in your index.js to fix the issue:
router.use('/users', require('./users')());

How to efficiently separate routing code

Dumb/Newb question...
I am learning/working on an API in Node / Express4 and I would like to break my routes out into another module. I have it working with the following code, but it seems awkward to me to keep re-using the require('express') statement... Is there a way to move more of the code from the routes.js file into server.js and still keep my .get and .post statements in the routes module? Thanks in advance!
server.js:
'use strict';
var express = require('express');
var routes = require('./routes');
var app = express();
app.use('/api', routes);
app.listen(3000, function() {
console.log('Listening);
});
routes.js
var express = require('express'); // how do I get rid of this line?
var router = express.Router(); // can I move this to server.js?
var apiRoute = router.route('');
apiRoute.get(function (req, res) {
res.send('api GET request received');
});
module.exports = router;
Your on the right track. Its actually cool to reuse the var express = require('express'); statement each time you need it. Importing, ( requiring ), modules is a cornerstone of modular development and allows you to maintain a separation of concerns with in the files of your project.
As far as modularly adding routes is concerned: The issue is that routes.js is misleading.
In order to modularly separate out your routes you should use several modules named <yourResource>.js. Those modules would contain all of the routing code as well as any other configuration or necessary functions. Then you would attach them in app.js with:
var apiRoute = router.route('/api');
apiRoute.use('/<yourResource', yourResourceRouter);
For example, if you had a resource bikes:
In app.js or even a module api.js:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes');
apiRoute.use('/bikes', bikeRoutes);
Then in bike.js:
var express = require('express');
var router = express.Router();
var bikeRoutes = router.route('/');
bikeRoutes.get(function (req, res) {
res.send('api GET request received');
});
module.exports = bikeRoutes;
From there its easy to see that you can build many different resources and continually nest them.

How to modularize routing with Node.js Express

I'm building a web app with Express and Node and am trying to factor my routing so that I don't have hundreds of routes in the same file. This site serves different files within the projects directory, so I made a file in routes/ called projectRoutes.jsto handle the routing for project files:
var express = require('express');
module.exports = function() {
var functions = {}
functions.routeProject = function(req, res) {
res.render('pages/projects/' + req.params.string, function(err, html) {
if (err) {
res.send("Sorry! Page not found!");
} else {
res.send(html);
}
});
};
return functions;
}
Then, in my routes.js, I have this...
var projectRoutes = require("./projectRoutes");
router.get('/projects/:string', function(req, res) {
projectRoutes().routeProject(req, res);
});
Is there a better way to structure this functionality within projectRoutes.js? In other words, how can I configure projectRoutes.js so that I can write the follow line of code in index.js:
router.get('/projects/:string', projectRoutes.routeProject);
The above seems like the normal way to handle something like this, but currently the above line throws an error in Node that says the function is undefined.
Thanks for your help!
You should use the native express router, it was made to solve this exact problem! It essentially lets you create simplified nested routes in a modular way.
For each of your resources, you should separate out your routes into several modules named <yourResource>.js. Those modules would contain all of the routing code as well as any other configuration or necessary functions. Then you would attach them in index.js with:
var apiRoute = router.route('/api')
apiRoute.use('/< yourResource >', yourResourceRouter)
For example, if you had a resource bikes:
In index.js:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
apiRoute.use('/bikes', bikeRoutes)
Then in bike.js:
var express = require('express')
, router = express.Router()
, bikeRoutes = router.route('/')
bikeRoutes.get(function (req, res) {
res.send('api GET request received')
});
module.exports = bikeRoutes
From there its easy to see that you can build many different resources and continually nest them.
A larger of example of connecting the routes in index.js would be:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
, carRoutes = require('./cars')
, skateboardRoutes = require('./skateboards')
, rollerskateRoutes = require('./rollerskates')
// routes
apiRoute.use('/bikes', bikeRoutes)
apiRoute.use('/cars', carRoutes)
apiRoute.use('/skateboards', skateboardRoutes)
apiRoute.use('/rollerskates', rollerskateRoutes)
Each router would contain code similar to bikes.js. With this example its easy to see using express's router modularizes and makes your code base more manageable.
Another option is to use the Router object itself, instead of the Route object.
In Index.js:
//Load Routes
BikeRoutes = require('./routes/Bike.js');
CarRoutes = require('./routes/Car.js');
//Routers
var express = require('express');
var ApiRouter = express.Router();
var BikeRouter = express.Router();
var CarRouter = express.Router();
//Express App
var app = express();
//App Routes
ApiRouter.get('/Api', function(req, res){...});
ApiRouter.use('/', BikeRouter);
ApiRouter.use('/', CarRouter);
In Bike.js:
var express = require('express');
var router = express.Router();
router.get('/Bikes', function(req, res){...});
module.exports = router;
Similarly in Car.js

Resources