I am using Restify for creating REST APIs. I have created the route in such format /ConfigDetails/:name/:id where name and id are optional.
If none of the two params is given, all configdetails will be fetched;
If name is given, config details for that name will be returned;
If id is given, config details for that id will be returned;
My question is, how do I make them optional? With the current route I've defined, unless all two parameters are present, it will not be able to be resolved and will fall into the default route.
As per my understanding restify don't support '?'
Can anyone help me on this.
I think you can't do this in restify. I have a work around which my work for you.
router.get('/ConfigDetails/:name/:id', handler)
router.get('/ConfigDetails/:name', handler)
router.get('/ConfigDetails', handler)
function handler(req, res, next) {
const {name = '', id = ''} = req.params
// your code here
}
As for REST API best practices, you should not put optional path parameters. It should be either in query params or request body for non GET requests.
Related
i have a node/express app, and need to account for if users don't enter a stationId param in the Url. Ive been looking at all sorts of regex built in to express, adding express-validator but couldnt get it to work for middleware. what would be the best way to do this? is the something in express or a third party validator?
The stationId is an mix of letters and numbers 1491TH it is used to call a second API to get information about the station entered in the Url. Im trying to work out how to use a validator to check if the param is blank, or if its not in the format i want.
import express from 'express'
import axios from 'axios'
import { cleanseLocation } from './utils.js'
const PORT = 3000
const app = express()
app.get('/:stationId/asset',(req, res) => {
const stationId = cleanseLocation(req.params.stationId)
const resp = await axios.get(`https://online-api/id/stations/${stationId}`)
res.send(resp)
})
app.listen(PORT, () =>
console.log(`The node API is running on: http://localhost:${PORT}.`)
)
When you define a route like:
app.get('/:stationId/asset', ...)
You are using a wildcard for the first path segment. It will match ANYTHING in the first path segment. So, it will match all of these:
/play/asset
/1238576/asset
/%20/asset
It will not match:
/asset
because that's just one path segment.
If you have specific rules for what is and isn't a valid stationId, you can implement a check for those inside the route handler:
app.get('/:stationId/asset',(req, res) => {
if (some logic to check req.param.stationId) {
res.send('working');
} else {
res.status(404).send("Invalid stationId");
}
});
If you need further help with how to implement stationId checking, you will have to disclose exactly how you would tell if it's a valid stationId or not.
is the something in express or a third party validator?
What mechanism to use for implementing validation depends entirely upon how you determine whether it is or isn't a valid stationId. You would have to explain that algorithm or method for us to help further.
The stationId is an mix of letters and numbers 1491TH it is used to call a second API to get information about the station entered in the Url. Im trying to work out how to use a validator to check if the param is blank, or if its not in the format i want
It already can't be blank. It won't match the route if there's no stationId at all.
So, if you are looking for a sequence of letters and numbers, you can just use a regex and define the route such that it will only match the route definition if you get a legal format for a stationId:
app.get('/:stationId([A-Za-z0-9]+)/asset', ...)
FYI, here's a helpful article with good example on using regex in Express routes. The full doc for what you can do with a regex in a route definition is here in the path-to-regexp documentation which is the module Express uses for this feature.
Which is more correct, and why? Does it depend on the scenario? Is there a standard?
router.post("/user", function(req, res) {
let thisUserId = req.body.userId;
});
router.post("/user/:userId", function(req, res) {
let thisUserId = req.params.userId;
}
Thanks!
This question is more about RESTful API conventions than node or express. Based on generally accepted REST conventions, this is basic CRUD operation:
/* fetch all users */
GET /users
/* fetch specific user */
GET /users/:userId
/* create new user */
POST /users
/* edit specific user */
PUT /users/:userId
/* delete specific user */
DELETE /users/:userId
So in your case I would say req.body is more appropriate, considering you want to create a user.
EDIT: another useful resource that supports this case: 10 best practices for better RESTful API.
req.body is used to access actual form data that you 'posted'.
req.params is used for route parameters, in your case userId which is passed in the parameters:
router.post("/user/:userId", function(req, res) {
let thisUserId = req.params.userId;
}
The official docs:
req.body
Contains key-value pairs of data submitted in the request
body. By default, it is undefined, and is populated when you use
body-parsing middleware such as body-parser and multer.
Link to req.body docs
req.params
This property is an object containing properties mapped to
the named route “parameters”. For example, if you have the route
/user/:name, then the “name” property is available as req.params.name.
This object defaults to {}.
Link to req.params docs
If you want to implement guards or any other logic in your route that relies on that id (of an existing user), you pass the userID in the params.
Let's say you are submitting a form where a new user registers.. You don't want to send the credentials in the parameters since it's confidential data and easily accessible this way. Here it makes sense to put those values in the request-body and use therefore req.body..
As Haris Bouchlis already mentioned in his answer it depends on your CRUD-ops that you like to perform.
In this case I advice to use req.params.userId.
Normal API entries have this standard. To get all the users the path will be '/users'.
To retrieve or update a specific user, the path will be '/users/1'.
API example
Yes there are completely different and used for the different purpose
1.req.params comes from path segments of the URL that match a parameter in the route definition such a /song/:songid. So, with a route using that designation and a URL such as /song/48586, then req.params.songid === "48586".
2.req.body properties come from a form post where the form data (which is submitted in the body contents) has been parsed into properties of the body tag.
If I have two REST endpoints:
app.get('/something/:id', ...handlers);
app.get('/something/else', ...handlers);
And I send a request to http://host:port/something/else
Is there a way to make Express router execute the endpoint with absolute path first (/something/else) before executing the one that matches the query params (/something/:id)?
I understand that I can reverse the order of invocation and specify the endpoint with query params last. But logically speaking, absolute path should take priority over query params and I believe that's the default behaviour for Koa.js
Just put the more specific route first and the wildcard route second. Routes are matched in order and the first one that matches handles the request and the others are not then processed. So, put the more specific route for /something/else before the /something/:id and you will see the /something/else route work properly when that's the URL.
// put wildcard route last and more specific route definitions first
// routes are matched in the order they are defined
app.get('/something/else', ...handlers);
app.get('/something/:id', ...handlers);
This does raise the question why you have designed this potential conflict into your URL scheme in the first place. You've essentially overloaded the id namespace and have reserved at least one id value for your own use. This can be managed by careful ordering of the route definitions, but it would generally be better if you didn't have this conflict in your URL design in the first place.
Is there a way to make Express router execute the endpoint with absolute path first ('/something/else') before executing the one that matches the query params ('/something/:id')?
Yes, define the more specific route first.
I understand that I can reverse the order of invocation and specify the endpoint with query params last. But logically speaking, absolute path should take priority over query params and I believe that's the default behaviour for Koa.js
You asked about Express. It matches routes in the order you've defined them. It doesn't try to guess which route it "thinks" you want to match first. It lets you define that exactly via the order of your route definitions.
I don't know Koa.js well, but there is this in the doc for Koa2: Middleware is now always run in the order declared by .use() (or .get(), etc.), which matches Express 4 API.
There are no specific route matching rules for express.js to match the routes.It goes and try to match every registered route with incoming request path and calls route handlers for all matched paths. Thus the following code will work.
app.get('/something/:id', (req, res, next) => {
console.log(`Calling with param ${req.params.id}`);
next(); // if you remove next from here it will not call the rest of the handlers
});
app.get('/something/else', (req, res, next) => {
console.log(`Calling with else`);
next();
});
Output:
Thus the only way to make sure the routes match exactly, define routes in their specific order.
app.get('/something/else', ...handlers);
app.get('/something/:id', ...handlers);
I'm using koa2 and koa-router together with sequelize on top. I want to be able to control user access based on their roles in the database, and it's been working somewhat so far. I made my own RBAC implementation, but I'm having some trouble.
I need to quit execution BEFORE any endpoint is hit if the user doesn't have access, considering endpoints can do any action (like inserting a new item etc.). This makes perfect sense, I realize I could potentially use transactions with Sequelize, but I find that would add more overhead and deadline is closing in.
My implementation so far looks somewhat like the following:
// initialize.js
initalizeRoutes()
initializeServerMiddleware()
Server middleware is registered after routes.
// function initializeRoutes
app.router = require('koa-router')
app.router.use('*', access_control(app))
require('./routes_init')
routes_init just runs a function which recursively parses a folder and imports all middleware definitions.
// function initializeServerMiddleware
// blah blah bunch of middleware
app.server.use(app.router.routes()).use(app.router.allowedMethods())
This is just regular koa-router.
However, the issue arises in access_control.
I have one file (access_control_definitions.js) where I specify named routes, their respective sequelize model name, and what rules exists for the route. (e.g. what role, if the owner is able to access their own resource...) I calculate whether the requester owns a resource by a route param (e.g. resource ID is ctx.params.id). However, in this implementation, params don't seem to be parsed. I don't think it's right that I have to manually parse the params before koa-router does it. Is anyone able to identify a better way based on this that would solve ctx.params not being filled with the actual named parameter?
edit: I also created a GitHub issue for this, considering it seems to me like there's some funny business going on.
So if you look at router.js
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
ctx.routerName = layer.name;
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
What it does is that for every route function that you have, it add its own capturing layer to generate the params
Now this actually does make sense because you can have two middleware for same url with different parameters
router.use('/abc/:did', (ctx, next) => {
// ctx.router available
console.log('my request came here too', ctx.params.did)
if (next)
next();
});
router.get('/abc/:id', (ctx, next) => {
console.log('my request came here', ctx.params.id)
});
Now for the first handler a parameter id makes no sense and for the second one parameter did doesn't make any sense. Which means these parameters are specific to a handler and only make sense inside the handler. That is why it makes sense to not have the params that you expect to be there. I don't think it is a bug
And since you already found the workaround
const fromRouteId = pathToRegexp(ctx._matchedRoute).exec(ctx.captures[0])
You should use the same. Or a better one might be
var lastMatch = ctx.matched[ctx.matched.length-1];
params = lastMatch.params(ctx.originalUrl, lastMatch.captures(ctx.originalUrl), {})
How can i get the URI and use it as params in nodejs. I am using express.
http://localhost:3000/getParams/param1/param2/param3/paramN
I want to get "/param1/param2/param3/paramN".
This is my current code:
app.get("/getParams/:params", test.params);
Thanks!
You can access the full path as one param, or you can access each segment as a separate param. To get as one param:
app.get('/mysvc/:input(*)', function(req, res)
{
console.log(req.params.input);
// ...
});
Notice the route which says everything (regex match of *) after /mysvc/ will be mapped to the input reques param. Then you can reference it via req.params
In this example, a request to /mysvc/foo/bar will output foo/bar
If you want to get each segment as a separate param then:
app.get('/mysvc/:param1/:param2'
access via req.params.param1, req.params.param2, etc...
In express parameters are available in the req object req.params.parameterName so in your case you can access it within the route like this req.params.params.
The params that you can access from the handler depends upon the route definitions. If you route is like
/my/:one/:two/:three
then you can access them as
req.params.one
req.params.two
req.params.three