Lunyx
Lunyx

Reputation: 51

ExpressJS: Best way to separate routes and accepting params?

I made a Express.js system where the files in the /routes folder are acting as a classic route (but with one file per route)

Example: /routes/get/user.js will be accessible with http://localhost:8080/user (the /get is to separate methods, it can be /post, /put...)

Here's my entire index.js file: https://pastebin.com/ALtSeHXc

But actually, my problem is that I can't pass params into the url like https://localhost:8080/user/random_id_here.

With this system, I think the best idea is to find a way to pass params on separated files too, but I don't know how can it be done...

Here's an example of one of my separated file:

module.exports = class NameAPI {
    constructor(client) {
        this.client = client
    }

    async run(req, res) {
        // Code here
    }
}

Maybe you'll have a better system, or a solution for this. Thanks.

Upvotes: 2

Views: 1331

Answers (3)

timsntech
timsntech

Reputation: 216

How to seperate routes and parse request parameter in express API

You can just put all your different API methods for each model in a seperated folder, and then just parse the API routes in your main file.

Let's say we have one main file called app.js, and you can organize your API routes/endpoints in subfolders.

folder structure:

├── app.js
└── routes
    └── api
        └── users.js

users.js in the folder routes/api in this case contains all operations for your users endpoint, and you import this in your app.js file.

Based on the route examples defined below, this will let you parse your express API with these endpoints:

GET YOUR_API:PORT/users           // fetch all users
GET YOUR_API:PORT/users/:userId   // fetch single user by id

app.js

// this is just a demo app.js, please adapt to your needs
const express = require("express");

// express app
const app = express();
app.use(express.json());

// api routes
// endpoint No. 1, this will create the endpoint /users
// and will enable you to use all methods defined in file users.js
app.use("/users", require("./routes/api/users"));

// add more endpoints, example endpoint No. 2
app.use("/ENDPOINT_NAME", require("./routes/api/FILE_NAME"));

// handle everything else you need to handle in your main file

// run server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));

in subfolder routes/api you add your api route files, like so:

routes/api/users.js

const express = require('express');
const router = express.Router();

// here we only GET all users and user by id, 
// but you can add any endpoints required for your API.

// get all users
router.get('/', async (req, res) => {
    try {
        // do something to get all users
        const allUsers = // fetch all users, depends on how you want to do it
        return res.status(200).json(allUsers)
    } catch (err) {
        console.log(err)
        return res.status(400).json({ msg: 'Bad request.' })
    }
})

// get a specific user by Id from request parameters
router.get('/:userId', async (req, res) => {
    try {
        // user id from params
        const userId = req.params.userId
        // do something with this userId, for example look up in DB
        return res.status(200).json({userId: `Requested userId is ${userId}`})
        )        
    } catch (err) {
        console.log(err)
        return res.status(400).json({ msg: 'Bad request.' })
    }
})

// add more user endpoints here
// with router.post, router.put, router.delete, whatever you need to do

module.exports = router

Upvotes: 0

jfriend00
jfriend00

Reputation: 707198

You can get the optional params from the module object you already have so each module specifies its own params. This example below shows just adding new params on after the module name, but you could extend this feature to be richer if you needed to.

In a simple implementation, in your loader, you can change this:

   posts.forEach((post) => {
        const module = new (require(`./routes/post/${post}`))(this);
        this.api.post(`/${post}`, async (req, res) => await module.run(req, res))
    })

to this:

   posts.forEach((post) => {
        const module = new (require(`./routes/post/${post}`))(this);
        const urlParams = module.params || "";
        this.api.post(`/${post}${urlParams}`, async (req, res) => module.run(req, res))
    });

So, if a given route wanted the extra URL param /:id added to it, then it would just define the .urlParams property on its exported module object to be `"/:id" and that would be automatically included in the route definition.


P.S. Most of the code in each of the branches of your switch statement in _loadHttpMethode() is identical. With a little factoring into a common function and one or two parameters passed to that function, you can eliminate all the copied code among those different branches of the switch so all each switch does is call one function and pass it a few arguments.

Upvotes: 1

BGPHiJACK
BGPHiJACK

Reputation: 1397

I generally would setup my express to handle this way in that case you want a dynamic insert. This is personal code so do make the adjustments necessary or observe the behavior! :)

WEBAPP.get('/room/:name', (req, res) => {
  // Check if URL ends with / (in my case I don't want that)
    if (req.url.endsWith('/')) return res.redirect('/');
  // Check if URL param "name" matches my regex ex. Username1920 or redirect them
    if (req.params.name.match(/^[a-zA-Z0-9]{3,24}$/) === null) return res.redirect('/');
  // render the room (sending EJS)
    res.render('room', {
        title: req.params.name.toUpperCase()
    });
});
/*

/*This example accepts one param and must follow my regex/rules*/

So if you were handed /room/test12345 your req.params.name would return a value. Notice the colon to define a param, so you could have /:room/:user/:request and it'd return: req.params.room, req.params.user, req.params.request all defined! :)

Upvotes: 0

Related Questions