Query strings in Express.js routes
Query strings in Express.js routes are used to pass data to the server via the URL. Unlike route parameters, which are part of the path, query strings are appended to the end of the URL after a question mark (?
) and are used to pass additional data or options.
How Query Strings Work
Query strings follow the ?
symbol in the URL and are composed of key-value pairs separated by &
. For example, in the URL /search?term=express&page=2
, the query string is term=express&page=2
.
Accessing Query Strings
In Express.js, query strings are accessible via the req.query
object. Each key in the query string corresponds to a property in the req.query
object.
Example:
const express = require('express');
const app = express();
// Route that uses query strings
app.get('/search', (req, res) => {
const term = req.query.term; // Access query parameter 'term'
const page = req.query.page; // Access query parameter 'page'
res.send(`Search term: ${term}, Page: ${page}`);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
In this example:
- If the URL is
/search?term=express&page=2
,req.query.term
will be'express'
, andreq.query.page
will be'2'
.
Using Query Strings
Query strings are often used to filter results, paginate data, or pass additional parameters in a request. They provide a way to send data that doesn't affect the route path but still needs to be processed by the server.
Example of Using Query Strings for Filtering and Pagination:
app.get('/products', (req, res) => {
const category = req.query.category; // Filter products by category
const page = parseInt(req.query.page) || 1; // Default to page 1 if not provided
const limit = parseInt(req.query.limit) || 10; // Default to 10 items per page if not provided
// Logic to retrieve products based on filters and pagination
res.send(`Category: ${category}, Page: ${page}, Limit: ${limit}`);
});
In this example:
category
filters the products by category.page
andlimit
are used for pagination.
Handling Optional Query Parameters
Query parameters are typically optional. If a parameter is not included in the query string, it will simply be undefined
in req.query
.
Example:
app.get('/profile', (req, res) => {
const userId = req.query.userId; // May be undefined if not provided
const showDetails = req.query.showDetails === 'true'; // Convert string to boolean
if (userId) {
res.send(`User ID: ${userId}, Show details: ${showDetails}`);
} else {
res.send('No user ID provided');
}
});
URL Encoding
Query strings must be URL-encoded, especially if they contain special characters or spaces. This ensures that characters are properly transmitted over the internet
and interpreted correctly by the server. For instance, spaces in query strings should be encoded as %20
or +
.
Example of URL Encoding:
If you want to pass a search term with spaces:
/search?term=node.js%20express
Or:
/search?term=node.js+express
In both cases, the server will receive req.query.term
as 'node.js express'
.
Summary
- Query Strings: Data appended to the URL after a
?
, used to pass additional parameters. - Accessing Query Strings: Access via
req.query
in Express.js. - Usage: Common for filtering, sorting, pagination, or passing extra data.
- Optional Parameters: Query parameters are optional; they will be
undefined
if not provided. - URL Encoding: Ensure special characters and spaces are properly encoded.
Query strings provide a flexible way to send data to the server without changing the URL path, making them useful for a variety of scenarios where additional data needs to be included in a request.
Route Matching and Priority in Express.js
In Express.js, route matching refers to the process where Express evaluates the incoming request's URL and HTTP method to determine which route handler should be executed. Route priority is determined by the order in which the routes are defined in the code.
How Route Matching Works
When a request is made, Express checks each route handler in the order they are defined to see if the route path matches the request URL and the HTTP method (GET, POST, PUT, etc.). A route handler will only be executed if:
- The HTTP method matches.
- The route path matches the request URL.
Example:
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
res.send('GET request to /users');
});
app.post('/users', (req, res) => {
res.send('POST request to /users');
});
app.get('/users/:id', (req, res) => {
res.send(`GET request to /users/${req.params.id}`);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
In this example:
- A
GET
request to/users
will match the first route. - A
POST
request to/users
will match the second route. - A
GET
request to/users/123
will match the third route, where123
is passed as a route parameter.
Route Path Matching
Express uses path-to-regexp for route matching, which supports static paths, dynamic route parameters, and wildcard patterns.
Examples:
- Exact Match:
/users
only matches/users
(but not/users/123
or/users/anythingelse
). - Dynamic Route Parameters:
/users/:id
matches/users/123
,/users/456
, etc. - Wildcard Matching:
/files/*
matches/files/docs
,/files/images/photo.jpg
, etc.
Example with Wildcards:
app.get('/files/*', (req, res) => {
res.send('Wildcard route, captures everything after /files/');
});
This route will match any request that starts with /files/
, regardless of what follows.
Route Priority
The order in which routes are defined in your Express app matters. Routes are matched in the order they appear in the code, and the first match wins. This means that more specific routes should be defined before more general ones.
Example:
app.get('/users/admin', (req, res) => {
res.send('Admin user');
});
app.get('/users/:id', (req, res) => {
res.send(`User with ID ${req.params.id}`);
});
In this case:
- A request to
/users/admin
will match the first route because it is defined before the more general/users/:id
route. - A request to
/users/123
will match the second route, as it is more general.
Route Priority with Middleware
Middleware functions that handle requests can also affect route priority. Middleware is typically defined at the top of the file, and any routes that come after it will be affected by it.
Example:
app.use((req, res, next) => {
console.log('Middleware runs for all routes');
next();
});
app.get('/users', (req, res) => {
res.send('GET request to /users');
});
Here, the middleware runs for all routes, including the /users
route, because it is defined before the route handlers.
Importance of Specificity in Route Definitions
To ensure correct route matching, it’s important to define more specific routes before more general ones. For example:
- Define
/users/admin
before/users/:id
to avoid accidental matches with the dynamic route. - Define
/files/:filename
before/files/*
if you want to match specific files first.
Example:
app.get('/files/:filename', (req, res) => {
res.send(`You requested file: ${req.params.filename}`);
});
app.get('/files/*', (req, res) => {
res.send('This matches any file path under /files/');
});
If you define /files/*
first, it would catch all requests and the more specific /files/:filename
route might never be reached.
Summary of Key Points
- Route Matching: Express checks routes in the order they are defined. A route matches if the HTTP method and URL match.
- Route Priority: Routes are prioritized based on the order of their definition in the code. More specific routes should be defined before more general ones.
- Path Matching: Express supports static paths, dynamic parameters, and wildcard paths.
- Middleware: Middleware affects the routes that come after it, so its position relative to the route handlers is crucial.
By defining your routes carefully and in the correct order, you can control how requests are handled, ensuring that the correct route handler is executed.