Search code examples
node.jsrestapiexpressrestful-url

REST API urls and their distribution among routers and controllers


I am working on a project using relational mysql DB. My tables are:

  • users
  • locations
  • crops

Using Node/Express

Simplified code structure:

/routers
/routers/User.js
/routers/Location.js
/routers/Crop.js
/controlers
/controlers/UserController.js
/controlers/LocationController.js
/controlers/CropController.js
/models
/models/UserModel.js
/models/LocationModel.js
/models/CropModel.js
app.js

The relation is that user can have many locations (farms) and on a location there may be many crops planted.

The DB relation stuff is done , no problem with that. My question is about how to logically structure my code and in the same time keep the REST principle in tact!

I am wondering from API point of view about the endpoint urls.

Currently they are:

hostname/api/user/:id - user account

hostname/api/user/:id/locations - user locations

hostname/api/user/:id/locations/:locationid - information about specific location belonging to specific user

hostname/api/user/:id/locations/:locationid/crops - list all crops which are planted on specific location , which belong to specific user

Is this url structure OK from rest perspective?

And after request is received in the API i cant wrap my head arround ... which router and controller should be responsible for my routes, ex:

hostname/api/user/:id/locations/:locationid

Should this url be handled from user router and controller? Or from locations router and controller?

The url clearly says: "For user with :id, find me location with :locationid which belongs to him.". So, me as unexperienced developer automatically assume that this route should be inside User router:

UserRouter.get(/:id/locations/:locationid, function(....){
    UserController.getUserLocationById(:id, :locationid);
})
app.use('/user', UserRouter)

But this way, i think i will en up with User router containing all possible urls:

/user
/user/:id
/user/:id/locations
...
...
/user/:id/crops
/user/:id/crops/:cropid
...

And nothing in other routers and controllers. And i this fact only is making me think that i am thinking wrong about REST API code structure.

Currently every route that starts with /user.... is handled by my User Express router and then passed to UserController.getUserLocationById in the above example url.

But i think i make a mistake this way.

If someone understood my headbanging , can you please help me solve that out?

Best Regards


Solution

  • Your routing looks clear to me and conforms to REST. Reading the pseudo urls, to me it results comprehensible what result you are going to get using them.

    My opinion about the actual implementation is that each router should be responsible to handle the route based on the first token (user, location, crop...), and the inner mapping to the responsible Controller should be done based on the data you are about to return.

    To me you can follow this:

    URL: hostname/api/user/:id
    ROUTE: user
    DATA: user
    
    URL: hostname/api/user/:id/locations
    ROUTE: user
    DATA: location
    
    URL: hostname/api/user/:id/locations/:locationid
    ROUTE: user
    DATA: location
    
    URL: hostname/api/user/:id/locations/:locationid/crops 
    ROUTE: user
    DATA: crop
    
    URL: hostname/api/locations/:locationid
    ROUTE: location
    DATA: location
    
    URL: hostname/api/locations/:locationid/crops 
    ROUTE: location
    DATA: crop
    
    URL: hostname/api/crops/:cropid/locations 
    ROUTE: crop
    DATA: location
    

    Route, will be the Router responsible to handler the incoming request. Data, will be the Controller responsible to provide the data, do validation and so on.

    Doing so each controller will be responsible only to serve the data it is the owner for, no matter which url you use to get there. Of course, the routing (URL) will be fundamental to understand which filter to apply and therefore which subset of data to return.