Introduction
When building APIs and web services, it is common to require parameters or other data to be passed into the service request from the client in order to execute the desired logic or action. This allows the service to be generic and support multiple use cases by configuring it dynamically at request time based on the parameters provided. In this article, we will explore different approaches for accepting parameters in the request for a web service written in Node.js and Express.
Query String Parameters
A simple and common way to pass data to a web service is through query string parameters in the URL. Query string parameters are name-value pairs attached to the URL that get passed along with each request.
For example, a request may look like:
Copy
GET /orders?id=123&status=open
Here “id” and “status” are the parameter names, and “123” and “open” are the respective values.
To access these parameters in an Express route, we can use req.query:
js
Copy
app.get(‘/orders’, (req, res) => {
const id = req.query.id;
const status = req.query.status;
// use parameters…
res.send(‘Order details’);
})
Some advantages of query parameters include:
Simple to generate and parse on client and server sides
Well supported by all HTTP clients and frameworks
Parameters are visible in the URL
The downsides are things like length limitations of the URL, and the parameters being visible in logs/APIs.
Route Parameters
Route parameters allow defining dynamic segments in the route path itself that get mapped to parameter objects.
For example:
Copy
GET /orders/:id/:status
The route handler would be:
js
Copy
app.get(‘/orders/:id/:status’, (req, res) => {
const id = req.params.id;
const status = req.params.status;
//…
})
Route parameters are useful when the parameters are intrinsic to the resource being operated on. For example, a request to fetch a single order would pass the order id in the route path naturally.
Some key things to note about route params:
Only works when parameters are part of resource identity/path
Hidden from logs/APIs but visible in URL
Strict path matching – parameters must match defined pattern
Request Body Parameters
When we have multiple or large arbitrary parameters, it’s best to pass them in the request body instead of URL. The body can be encoded in different formats like JSON, form-encoded or raw text based on content-type.
In Express, we can access body parameters through req.body:
js
Copy
app.post(‘/orders’, (req, res) => {
const {name, items, customer} = req.body;
// validation, etc
res.send(‘Order created!’)
})
To parse the request body based on content-type, we need body-parsing middleware like express.json() or express.urlencoded():
js
Copy
app.use(express.json());
app.use(express.urlencoded({extended: false}));
Request body is best suited when:
Dynamic/unstructured parameters
Large payloads
Parameters are part of request “action” vs resource identity
Header Parameters
In some scenarios, certain metadata about the request needs to be passed as parameters to the service. HTTP headers provide a way to attach key-value pairs to each request.
Express exposes headers object on request which can be used:
js
Copy
app.get(‘/orders’, (req, res) => {
const authorization = req.headers.authorization;
const userAgent = req.headers.user-agent;
//…
})
Common parameters passed via headers include authentication tokens, client IDs, language/geo preferences etc.
Headers have advantages like:
Less intrusive than other locations
Standard mechanism for metadata
Hidden from APIs/logs
Drawbacks are limited size and not part of typical API contract.
Cookie Parameters
Some stateful information can be attached to the client as HTTP cookies and passed back on subsequent requests.
In Express, cookies are available under req.cookies:
js
Copy
app.get(‘/orders’, (req, res) => {
const sessionId = req.cookies.sessionId;
//…
})
To read cookies, cookie-parser middleware is needed:
js
Copy
app.use(cookieParser());
Cookies are useful when client needs to pass authentication tokens, preferences etc on repeated requests without specifying them each time.
Some key properties of cookies:
Maintain state on client side
Have size limits
Sent on every request
The appropriate choice of parameter location depends on the kind of data, size, frequency of change, security requirements etc. Best practices also apply like sanitizing inputs, validating types and values.
Additional Considerations
Parameter Validation
It is important to validate parameter values received from external requests. This prevents vulnerabilities like SQL injections, XSS attacks etc.
Some validation best practices include:
Type check (number, string, object etc)
Value range checking (length, numeric range)
Whitelist unsupported values
Sanitize values (escape strings)
Error Handling
Invalid or missing parameters should result in clear, consistent and helpful error responses. This allows clients to understand input requirements and handle errors gracefully.
For example:
js
Copy
app.get(‘/orders/:id’, (req, res, next) => {
if(!req.params.id) {
return next(new Error(‘Id is required’))
}
// other validation
next();
}, (err, req, res, next) => {
res.status(400).send({
error: err.message
});
});
Documentation
API documentation should clearly specify expected parameter names, data types, example values, optional vs required parameters etc. This helps clients integrate with the service seamlessly.
Parameter documentation improves discoverability, prevents bugs and enhances client experience.
Caching Parameter Values
For performance, parameter values extracted from different sources can be cached rather than extracting on every request. For example:
js
Copy
let userId;
app.use((req, res, next) => {
userId = req.user?.id;
next();
});
// reuse userId
app.get(‘/profile’, (req, res) => {
//..
})
This avoids costly operations like cookie parsing, auth validation etc on every inbound request. Proper cache invalidation is also important.
Conclusion
This article covered different ways like query, route, body params to pass data into express/node.js services. Validation, error handling along with param documentation are important aspects. Performance optimizations via caching were also discussed. Choosing the right mechanism based on your needs ensures clean, secure and performant APIs.
