🚀 Kickstart your backend engineering career as a Node.js developer with our ultimate zero-to-hero roadmap at learnbackend.dev.
Being able to parse information from your users, such as an identifier in the URL or an access token in the payload is crucial when building an API.
In this article we'll focus on three ways to parse the data contained in GET and POST requests using the Express framework, starting with query string parameters.
Parsing query string parameters
The first way to pass values to a GET endpoint is to use what’s called: query string parameters.
Query string parameters are an extension of a website’s URL consisting of a list of key-value pairs concatenated by an ampersand and separated from the base URL by a question mark.
https://www.mywebsite.com/user?firstName=John&lastName=Doe
In Express, these values are automatically populated in the request’s query
property (i.e. req.query
) and are accessible from within the endpoint's controller.
We can, to demonstrate this, create a basic application that implements a GET
route called /hello
that will simply respond with the string “Hello” followed by the name of the user — if present as a query string — or "Hello stranger” otherwise.
const express = require('express');
const app = express();app.get('/hello', (req, res) => {
const { name } = req.query; res.send(`Hello ${name || 'stranger'}`);
});app.listen(3000);
We can now test this endpoint by first calling it without any query string parameters, which indeed results in the server responding with the “Hello World” string, and then calling it a second time with the name
variable, which results in the server responding with “Hello John”.
$ curl '127.0.0.1:3000/hello'
=> Hello stranger$ curl '127.0.0.1:3000/hello?name=John'
=> Hello John
Although this method is quite convenient for passing arbitrary data to an endpoint, you have to keep in mind that assuming the existence or the type of query string parameters can be quite dangerous and eventually lead to your application not responding correctly or even crashing.
As an example, if we slightly change the logic of our endpoint to apply the toUpperCase()
method on the name
query string...
app.get('/hello', (req, res) => {
const { name } = req.query; res.send(`Hello ${name.toUpperCase() || 'world'}`);
});
…and run the following cURL command where the name
variable is defined twice..
$ curl '127.0.0.1:3000/hello?name=John&name=Jack'
…we can see that our application ends up throwing the following TypeError: name.toUpperCase is not a function
as indeed, in this context, the type of name
variable changed and is not a string anymore but rather an array of strings.
So in order to avoid this problem and a bunch a similar ones, make sure to always check the existence but also the type of variable of these query strings using either the typeof
operator or a schema validation packages such as Joi.
That being said, if you don’t need this parsing feature, I encourage you to disable it using the set()
method of the application object, which will result in the query string parameters being ignored by Express and the request’s query variable being defined as an empty object.
const express = require('express');
const app = express();app.set('query parser', false);// The rest of your application...app.listen(3000);
Parsing route parameters
The second way to pass values to a GET endpoint, which is also preferred to the first one, is to use what’s called: route parameters.
A route parameter is a named URL segment that is used to capture the value specified at its position in the URL.
127.0.0.1:3000/hello/:name
When using Express, this value will automatically be populated in the request’s params
property (i.e. req.params
), under the same key as its name, consequently allowing you to defined multiple ones to create more complex URLs such as this one for instance.
127.0.0.1:3000/streaming/:showId/:season/:episode
That being said, if we wanted to achieve the same result as in the previous example but this time using a route parameter, we could explicitly define the name
variable in the URN of our endpoint and extract it from the params
object instead of the query
object like so.
const express = require('express');
const app = express();app.get('/hello/:name', (req, res) => {
const { name } = req.params; res.send(`Hello ${name}`);
});app.listen(3000);
If we now run the following cURL command, we can see that the server successfully responds with the “Hello John” string.
$ curl '127.0.0.1:3000/hello/John'
=> Hello John
Now, the great thing about route parameters is that they help reducing the required amount of validation, since they are never null
or undefined
and are always strings of characters with a positive length.
Now that we’ve seen how to parse values from the request’s URL, let’s see how we can parse them from the request’s body.
Parsing requests’ payloads
Unlike route and query string parameters, the data contained within the body of a request is not automatically parsed by the Express framework.
Indeed, since the data sent through a POST
or a PUT
request — just to name two of these methods — can be of several different types, like for example, text
or json
, Express is giving the developers the liberty to choose the most appropriate body parsing method when building their API.
In other words, since version 4, Express comes pre-packages with parsing functions called middlewares, that are accessible through the top-level function exported by the module, which are: text()
, json()
, urlencoded()
and raw()
.
Whenever used, each of these functions will first look at the incoming request’s Content-Type
header and then parse the body of this request as a string, if only it matches its own internal type, which is by default:
plain/text
fortext()
application/json
forjson()
application/x-www-form-urlencoded
forurlencoded()
application/octet-stream
forraw()
This parsing will then result in the addition of a new property to the request object called body
(i.e. req.body
) that will contain the parsed data or an empty object (i.e. {}
) if there was nothing to be parsed, if the Content-Type
header was not matched or if an error occurred.
For example, to enable our application to parse the body of requests coming from an HTML form, which usually has the following Content-Type
: application/x-www-form-urlencoded
, we can declare and execute the urlencoded()
function at the application level like so.
app.use(express.urlencoded());
To illustrate this, we can create a POST /hello
route that will once again respond with “Hello” followed by the user name or “Hello stranger” otherwise, but this time, using the name variable from the request’s body
object rather than the query
or params
objects.
const express = require('express');
const app = express();app.use(express.urlencoded());app.post('/hello', (req, res) => {
const { name } = req.body; res.send(`Hello ${name || 'stranger'}`);
});app.listen(3000);
We can now test it using the following cURL command, which will indeed result in the “Hello John” string being returned by the server.
$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'name=John' '127.0.0.1:3000'
What’s next?
🚀 Kickstart your backend engineering career as a Node.js developer with our ultimate zero-to-hero roadmap at learnbackend.dev.