How to write Dynamic routes for express server ( nodejs) - node.js

I have written crud routes
`
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const db = require('./user_crud_queries')
const port = 3000
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true,
})
)
app.get('/', (request, response) => {
response.json({
info: 'Node.js, Express, and Postgres API'
})
})
app.get('/users', db.getUsers)
app.get('/users/:id', db.getUserById)
app.post('/users', db.createUser)
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
`
if i have 100 tables do i have to include 100 files? Can it be imported dynamically based on call?
I did create generic crud but i dont want to give that to client because they expect it to be detailed. So 100 tables means 100 separate file for each table. if sever include all 100 tables , will the memory is enough?

So what you can instead do is create a file called users.routes.js/ts and include all of it like this :
import { Router } from "express";
router
.route("/users")
.get(db.getUsers)
.post(db.createUser)
router
.route("/users/:id")
.get('/users/:id', db.getUserById)
.delete('/users/:id', db.deleteUser)
export default router;
and then in the app.js/ts you can user it like this :
import userRouter from "./routes/user.routes";
app.use("/api", userRouter);
So now you can use the following endpoint :
/api/users or /api.users/:id
But yes, if there are 100 tables you will have to use 100 different files for all the CRUD operation that is there, only this will make the code readability easy. Also, you can use different microservice for the same job and reduce the tables for a single server to handle.

Related

Increase body limit for specific route with ExpressJS

We have a REST API built with ExpressJS. One of our routes is getting files as binary data in the JSON request body. Therefore we want to increase the limit of the body.
Simple version of our starting index.js file
const start = async () => {
try {
// Connect to database
await connectDB(process.env.DATABASE_URL);
// Create app
const app = express();
// Body parser
app.use(express.json()); //100kb default
app.use(express.urlencoded({ extended: true }));
// Mount routes
app.use(`/${process.env.API_VERSION}`, authRoutes);
app.use(`/${process.env.API_VERSION}`, profileRoutes);
app.use(`/${process.env.API_VERSION}`, filesRoutes);
app.use(`/${process.env.API_VERSION}`, ticketRoutes);
// Error handler
app.use(errorHandler);
// Not found handler
app.use(notFoundHandler);
// Start server
const server = app.listen(process.env.PORT, () => {
console.log(`Server running in ${process.env.NODE_ENV} mode on http://${process.env.HOST}:${process.env.PORT}/${process.env.API_VERSION}`)
});
} catch(error) {
console.log(error);
}
}
start();
At this point the limit for all routes is 100kb.
Inside our filesRoutes we have 1 specific upload route where we want to increase this limit to 200mb.
import express from 'express';
import { uploadFiles, getFiles, downloadFile, deleteFile } from '../controllers/filesController.js';
import authorize from '../middleware/authorizeHandler.js';
const router = express.Router();
router.get('files', authorize, getFiles);
router.get('files/:id/download', authorize, downloadFile);
router.post('files/upload', [authorize, express.json({ limit: '200mb' })], uploadFile);
router.delete('files/:id', authorize, deleteFile);
export default router;
I added express.json({ limit: '200mb' }) to the upload route but I'm getting an error back request entity too large.
Based on #vighnesh153 comment we could fix the issue by changing app.use(express.json()); to app.use(/^(?!\/api\/files\/upload$)/, express.json()); and add express.json({ limit: '200mb' }) as middleware to the api/files/upload route

Use Auth0 API protected route with Express route handlers

I implemented protected API routes in Express using the Auth0 quick start. Protected routes are handled with a middleware function called checkJwt, provided by Auth0. It runs whenever one of the GET methods is called. This process works well if I manage all my routes in server.js.
How can I separate out the route handling and still preserve the protected API routes?
server.js working code with protected routes.
import colors from 'colors'
import cors from 'cors'
import express from 'express'
import morgan from 'morgan'
import dotenv from 'dotenv'
import connectDB from './db.js'
import checkJwt from './middleware/auth.middleware.js'
import { getStudents, getStudent } from './controllers/students.controller.js'
dotenv.config()
connectDB()
export const app = express()
app.use(cors())
app.use(express.json({ limit: '50mb' }))
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
}
//handle routing internally
app.get('/api/students/:id', checkJwt, getStudent)
app.get('/api/students', checkJwt, getStudents)
const PORT = process.env.PORT || 6000
app.listen(PORT, () =>
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
)
I want to divorce out the route handling as my code grows. I can't figure out where to put checkJwt once I separate out the routing.
server.js desired code structure is:
import colors from 'colors'
import cors from 'cors'
import express from 'express'
import morgan from 'morgan'
import dotenv from 'dotenv'
import connectDB from './db.js'
import studentsRouter from './routes/students.routes.js'
dotenv.config()
connectDB()
const app = express()
app.use(cors())
app.use(express.json({ limit: '50mb' }))
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
}
// handle routing externally
const apiRouter = express.Router()
app.use('/api', apiRouter)
apiRouter.use('/students', studentsRouter)
const PORT = process.env.PORT || 6000
app.listen(PORT, () =>
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
)
Routes are implemented in the students.routes.js Route handler.
import express from 'express'
import { getStudent, getStudents } from '../controllers/students.controller.js'
const router = express.Router()
// where can I implement checkJwt?
router.route('/').get(getStudents)
router.route('/:id').get(getStudent)
export default router
Is it possible to simplify the code by moving the routing, and still protect my routes?
you can use any middleware like this in your router:
const router = express.Router();
router.route('/').get([checkJwt, secondMiddleware, ...] , getStudents);
based on documentation:
express-routing
You can provide multiple callback functions that behave like middleware to handle a request. The only exception is that these callbacks might invoke next('route') to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there’s no reason to proceed with the current route.

How to organize express endpoints properly

I am having trouble wrapping my head around how to use the /:variable notation effectively. In my setup I have this layout.
router.route("/").get()
router.route("/:id").get().put().post().delete()
router.route("/auth").get().put().post()
When I call /auth it fails to go there, instead it triggers the method under the /:id. How can I make sure when I say /auth it does not goto that /:id path.
You need to adjust the order of route definition. Move /auth route to /:id before route.
E.g.
import express, { Router } from 'express';
const app = express();
const port = 3000;
const router = Router();
router.route('/auth').get((req, res) => {
res.send('auth');
});
router.route('/:id').get((req, res) => {
res.send({ id: req.params.id });
});
app.use('/user', router);
app.listen(port, () => console.log(`HTTP server is listening on http://localhost:${port}`));
Test and output:
⚡ curl http://localhost:3000/user/auth
auth%
⚡ curl http://localhost:3000/user/123
{"id":"123"}%

CORS in node blocks POSTMAN to get data?

I'm using POSTMAN dev tool to test this API :
http://localhost:3000/users
and I have this express.js script :
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const mongoConfig = require("./config/mongodb.config");
var bodyParser = require("body-parser");
// Routers
const userRouter = require("./core/user/user.route");
const app = express();
const port = process.env.PORT || 3000;
const URI = mongoConfig.mongoURI;
const connectionOptions = mongoConfig.connectionOption;
// Connect Mongo Atlas database
mongoose
.connect(URI, connectionOptions)
.then(() => {
console.log("Connection is established successfully ...");
})
.catch((err) => {
console.error(err);
});
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.json());
app.use(cors);
app.use("/users", userRouter);
app.listen(port, () => {
console.log("The server is running on port", `${port}`);
});
The problem is when I remove CORS :
// app.use(cors);
POSTMAN can get data but when I add CORS it blocks and returns this stack trace :
Could not get any response
There was an error connecting to http://localhost:3000/users.
Why this might have happened:
The server couldn't send a response:
Ensure that the backend is working properly
Self-signed SSL certificates are being blocked:
Fix this by turning off 'SSL certificate verification' in Settings > General
Proxy configured incorrectly
Ensure that proxy is configured correctly in Settings > Proxy
Request timeout:
Change request timeout in Settings > General
As far as I know, POSTMAN is dev tool and CORS is related only with a browser but I did not understand the reason behind it.
After debugging my server.js I found the bug was inside my server and it's not related to POSTMAN.
The problem is using cors before user using my routers will block the server ( unlimited looping ).
So when I changed this code :
app.use(cors);
app.use(express.json());
app.use("/users", userRouter);
To
app.use("/users", userRouter);
app.use(cors);
app.use(express.json());
The issue is solved but I did not understand why and what happens exactly inside the server.

How to attach a router to an express app based on a path specified by a regular expression?

With this very simple app:
const express = require('express');
const app = express();
const router = express.Router();
const port = 8080;
router.get('/test', (req, res) => {
res.send('Test was hit')
})
// binds router to express app
app.use('/root', router);
app.listen(port, () => logger.info(`Listening on port: ${port}`));
After running curl http://localhost:8080/root/test, unsurprisingly, the response is Test was hit
I am to make this much more generic and wish for the consumer to be able to hit curl http://localhost:8080/<whatever-the-consumer-specifies>/test and still hit the routers /test endpoint.
However, if I replace the binding to be as follows:
app.use('/*',router);
After a subsequent hit the response is Cannot GET /root/test
How can I instead achieve this?
* EDIT: *
Of course I could do:
router.get('*/test', (req, res) => {
res.send('Test was hit')
})
However - This is not the answer that I seek, I only want this configuration take place once in the project.
Express allows the use of regular expression in the router middleware as mentioned in the docs here.
app.use(/\/\w+/, router);
You could replace the string and place a regex in the first argument.

Resources