Reputation: 789
I want to create route handling for these example routes:
GET /users
GET /users/:userid
GET /users/:userid/groups
GET /users/:userid/groups/:groupid
GET /groups
GET /groups/:groupid
GET /groups/:groupid/users
GET /groups/:groupid/users/:userid
Contrived code example of this setup. Imagine a directory structure as:
# index.js
# routes/users.js
# routes/groups.js
# lib/users.js
# lib/groups.js
And contents of each being:
index.js
express = require 'express'
UsersRouter = require './routes/users'
GroupsRouter = require './routes/groups'
app = express()
app.use '/users', UsersRouter
app.use '/groups', GroupsRouter
app.use (err, req, res, next) -> res.sendStatus(404)
app.listen '3000', () ->
console.log "Listening on port #{3000}"
module.exports = app
routes/users.js
express = require 'express'
Users = require '../lib/users'
GroupsRouter = require './groups'
router = express.Router()
router.param 'userid', (req, res, next, userid) ->
req.userid = userid
next()
router.get '/', (req, res) ->
Users.list req, (err, users) ->
return next err if err
res.status(200).send(users)
router.get '/:userid', (req, res, next) ->
Users.find req, (err, user) ->
return next err if err
res.status(200).send(user)
router.use '/:userid/groups', GroupsRouter
module.exports = router
routes/groups.js
express = require 'express'
Groups = require '../lib/groups'
UsersRouter = require './users'
router = express.Router()
router.param 'groupid', (req, res, next, groupid) ->
req.groupid = groupid
next()
router.get '/', (req, res, next) ->
Groups.list req, (err, groups) ->
return next err if err
res.status(200).send(groups)
router.get '/:groupid', (req, res, next) ->
Groups.find req, (err, group) ->
return next err if err
res.status(200).send(group)
router.use '/:groupid/users', UsersRouter
module.exports = router
lib/users.js
module.exports =
list: (req, cb) ->
if req.groupid
return cb null, "List of all users in group #{req.groupid}"
else
return cb null, "List of all users"
find: (req, cb) ->
if req.groupid and req.userid
return cb null, "User #{req.userid} if in #{req.groupid}"
else
return cb null, "The user #{req.userid}"
lib/groups.js
module.exports =
list: (req, cb) ->
if req.userid
return cb null, "List of all groups for #{req.userid}"
else
return cb null, "List of all groups"
find: (req, cb) ->
if req.userid and req.groupid
return cb null, "Group #{req.groupid} if it has member #{req.userid}"
else
return cb null, "The group #{req.groupid}"
Problem is, I am getting an espress.js error for doing that cyclical require of the routers. Is it possible to do that?
If I instead just include one router in the other, and not vice versa, it get the expected response from my request.
An example of a longer route use case, say I want to see if a group has a user in it and if so return all the other groups that user belongs too:
GET /groups/:groupid/users/:userid/groups
Error I've received:
TypeError: Router.use() requires middleware function but got a Object
Upvotes: 1
Views: 1947
Reputation: 789
I was able to achieve the routing I wanted by exporting a middleware route from one router that I could assign to the route I want to match in another router.
routes/users.js
express = require 'express'
Users = require '../lib/users'
router = express.Router()
router.param 'userid', (req, res, next, userid) ->
req.userid = userid
next()
router.pseudo = router.use (req, res, next) -> next()
router.get '/', (req, res) ->
Users.list req, (err, users) ->
return next err if err
res.status(200).send(users)
router.get '/:userid', (req, res, next) ->
Users.find req, (err, user) ->
return next err if err
res.status(200).send(user)
module.exports = router
router.use '/:userid/groups', (require '../groups').pseudo
routes/groups.js
express = require 'express'
Groups = require '../lib/groups'
router = express.Router()
router.param 'groupid', (req, res, next, groupid) ->
req.groupid = groupid
next()
router.pseudo = router.use (req, res, next) -> next()
router.get '/', (req, res, next) ->
Groups.list req, (err, groups) ->
return next err if err
res.status(200).send(groups)
router.get '/:groupid', (req, res, next) ->
Groups.find req, (err, group) ->
return next err if err
res.status(200).send(group)
module.exports = router
router.use '/:groupid/users', (require '../users).pseudo
This routing setup allows for requests like:
GET /groups/:groupid/users
The route will set the req.groupid value from the group route middleware and then be routed to the users /
route to list the users.
Same if you keep on daisy chaining them like:
GET /groups/:groupid/users/:userid/groups
The route will first hit groups, then users, then back to groups and giving me the set values for req.groupid and req.userid.
Upvotes: 0
Reputation: 9330
This is the possible way to tackle this cyclic situation
keep the handler code in a separate module, and re-use in the route modules. (This is the point what jfriend00 was also trying to make in comments)
for eg.
// userHandler.js
module.exports = {
browse: (req, res) => {
// return user list
},
find: (req, res) => {
// return user
}
}
// userRouter
// ...
const handler = require('userHandler');
router.get('/', handler.browse);
// ...
// groupsRouter
// ...
const uHandler = require('userHandler');
router.get('/:groupid/users', uHandler.browse);
router.get('/:groupid/users/:userid', uHandler.find);
// ...
Upvotes: 0
Reputation: 3798
users.js
relies on groups.js
, but then groups.js
requres users.js
while it is being executed. This means that it will go in a loop:
users.js
calls groups.js
groups.js
calls users.js
which calls groups.js
etc.
Upvotes: 1