# URL Parameters vs Query Strings in Express.js

Every URL tells a story. Part of that story is built into the path itself. Another part is hidden in the query string. Learn the difference and you'll write cleaner, more predictable APIs.

This is about URL parameters, query strings, how to use them in Express, and when to pick one over the other.

* * *

## What Are URL Parameters?

URL parameters are placeholders in the path itself. They're part of the route structure.

```plaintext
/users/123
        ^^^
        This is a URL parameter
```

The `123` is the parameter. It's the actual data embedded in the path.

### How They Look

```plaintext
/users/123
/posts/42
/products/coffee-maker
/users/john/posts/15
```

The value changes, but the structure stays the same. You're identifying a specific resource.

### In Express

```javascript
app.get("/users/:userId", (req, res) => {
  const userId = req.params.userId;
  res.json({ userId });
});
```

When someone hits `/users/123`, `req.params.userId` equals `"123"`.

* * *

## What Are Query Strings?

Query strings come after the `?` in the URL. They're optional filters or modifiers.

```plaintext
/users?role=admin&active=true
      ^^^^^^^^^^^^^^^^^^^^^^^^
      This is the query string
```

Query strings don't change the resource you're asking for. They modify how you get it.

### How They Look

```plaintext
/users?role=admin
/posts?sort=date&order=desc
/products?category=electronics&price=100-500
/search?q=javascript&limit=10
```

Multiple query parameters are joined with `&`. Order doesn't matter.

### In Express

```javascript
app.get("/users", (req, res) => {
  const role = req.query.role;
  const active = req.query.active;
  res.json({ role, active });
});
```

When someone hits `/users?role=admin&active=true`, `req.query` contains:

```javascript
{
  role: "admin",
  active: "true"
}
```

* * *

## URL Structure Breakdown

Here's what a complete URL looks like:

```plaintext
https://example.com/users/123/posts/42?sort=date&limit=10
        |            |      |   |     |    |       |
        |            |      |   |     |    |       Query parameter
        |            |      |   |     |    Query parameter
        |            |      |   |     Query string (starts with ?)
        |            |      |   URL parameter
        |            |      URL parameter
        |            Path
        Domain
```

Breaking it down:

- **Domain:** `example.com`
- **Path:** `/users/123/posts/42`
- **URL Parameters:** `123` and `42`
- **Query String:** `sort=date&limit=10`
- **Query Parameters:** `sort` and `limit`

* * *

## URL Parameters vs Query Strings

### URL Parameters

For identifying a specific resource or navigating a hierarchy.

```plaintext
/users/123              <- Get user with ID 123
/posts/42               <- Get post with ID 42
/users/123/posts/42     <- Get post 42 belonging to user 123
/products/electronics/phones  <- Get phones in electronics category
```

**Use URL parameters when:**

- You're identifying a specific resource
- The parameter is essential to the request
- You're navigating a hierarchy
- The value is part of the resource's unique identity

### Query Strings

For filtering, sorting, pagination, or optional modifications.

```plaintext
/users?role=admin              <- Get users, filter by role
/posts?sort=date&order=desc    <- Get posts, sort by date
/products?page=2&limit=20      <- Get products, pagination
/search?q=javascript           <- Search for "javascript"
```

**Use query strings when:**

- You're filtering or searching
- The parameter is optional
- You're modifying how data is presented
- Multiple parameters can apply simultaneously

### Quick Comparison

| Aspect | URL Parameters | Query Strings |
| --- | --- | --- |
| Location | In the path | After `?` |
| Purpose | Identify resource | Filter or modify |
| Essential | Usually required | Optional |
| Example | `/users/123` | `/users?role=admin` |
| Multiple | `/users/123/posts/42` | `?a=1&b=2&c=3` |
| Order | Fixed | Doesn't matter |
| Best for | IDs and hierarchy | Filters and options |

* * *

## Accessing URL Parameters in Express

### Single Parameter

```javascript
app.get("/users/:userId", (req, res) => {
  const userId = req.params.userId;
  res.json({ userId });
});
```

Request: `GET /users/123`
Result: `userId = "123"`

### Multiple Parameters

```javascript
app.get("/users/:userId/posts/:postId", (req, res) => {
  const userId = req.params.userId;
  const postId = req.params.postId;
  res.json({ userId, postId });
});
```

Request: `GET /users/123/posts/42`
Result: `userId = "123"`, `postId = "42"`

### Named Parameters

```javascript
app.get("/products/:category/:productId", (req, res) => {
  const { category, productId } = req.params;
  res.json({ category, productId });
});
```

Request: `GET /products/electronics/phone-456`
Result: `category = "electronics"`, `productId = "phone-456"`

### Accessing All Parameters

```javascript
app.get("/users/:userId/posts/:postId/comments/:commentId", (req, res) => {
  console.log(req.params);
  // { userId: "123", postId: "42", commentId: "789" }
  res.json(req.params);
});
```

* * *

## Accessing Query Strings in Express

### Single Query Parameter

```javascript
app.get("/users", (req, res) => {
  const role = req.query.role;
  res.json({ role });
});
```

Request: `GET /users?role=admin`
Result: `role = "admin"`

### Multiple Query Parameters

```javascript
app.get("/posts", (req, res) => {
  const sort = req.query.sort;
  const order = req.query.order;
  const limit = req.query.limit;
  res.json({ sort, order, limit });
});
```

Request: `GET /posts?sort=date&order=desc&limit=10`
Result: `sort = "date"`, `order = "desc"`, `limit = "10"`

### Using Destructuring

```javascript
app.get("/search", (req, res) => {
  const { q, category, page } = req.query;
  res.json({ q, category, page });
});
```

Request: `GET /search?q=javascript&category=tutorials&page=2`
Result: `q = "javascript"`, `category = "tutorials"`, `page = "2"`

### Accessing All Query Parameters

```javascript
app.get("/filter", (req, res) => {
  console.log(req.query);
  // All query parameters as an object
  res.json(req.query);
});
```

Request: `GET /filter?a=1&b=2&c=3`
Result:
```javascript
{
  a: "1",
  b: "2",
  c: "3"
}
```

### Handling Missing Parameters

Query parameters might not exist. Always check:

```javascript
app.get("/products", (req, res) => {
  const page = req.query.page || 1;
  const limit = req.query.limit || 10;
  
  res.json({ page, limit });
});
```

Request: `GET /products`
Result: `page = 1`, `limit = 10`

* * *

## Real-World Examples

### Example 1: User Profile

```javascript
// Get a specific user
app.get("/users/:userId", (req, res) => {
  const userId = req.params.userId;
  const user = findUserById(userId);
  res.json(user);
});
```

Request: `GET /users/123`
Purpose: Get user 123's profile

### Example 2: Search with Filters

```javascript
// Search users with optional filters
app.get("/users/search", (req, res) => {
  const { q, role, active } = req.query;
  
  const results = searchUsers(q, {
    role,
    active
  });
  
  res.json(results);
});
```

Request: `GET /users/search?q=john&role=admin&active=true`
Purpose: Search for "john", filter by role and active status

### Example 3: Nested Resources

```javascript
// Get posts by a specific user
app.get("/users/:userId/posts", (req, res) => {
  const userId = req.params.userId;
  const posts = getPostsByUser(userId);
  res.json(posts);
});

// Get a specific post by a specific user
app.get("/users/:userId/posts/:postId", (req, res) => {
  const { userId, postId } = req.params;
  const post = getPost(userId, postId);
  res.json(post);
});
```

Requests:
- `GET /users/123/posts` → Get all posts by user 123
- `GET /users/123/posts/42` → Get post 42 by user 123

### Example 4: Pagination and Sorting

```javascript
// Get all products with optional pagination and sorting
app.get("/products", (req, res) => {
  const page = req.query.page || 1;
  const limit = req.query.limit || 20;
  const sort = req.query.sort || "name";
  const order = req.query.order || "asc";
  
  const products = getProducts({
    page,
    limit,
    sort,
    order
  });
  
  res.json(products);
});
```

Requests:
- `GET /products` → Default pagination
- `GET /products?page=2&limit=10` → Page 2, 10 items
- `GET /products?sort=price&order=desc` → Sort by price, descending

### Example 5: Combined Parameters and Query

```javascript
// Get posts by a user with optional sorting and pagination
app.get("/users/:userId/posts", (req, res) => {
  const userId = req.params.userId;
  const page = req.query.page || 1;
  const sort = req.query.sort || "date";
  
  const posts = getPostsByUser(userId, {
    page,
    sort
  });
  
  res.json(posts);
});
```

Requests:
- `GET /users/123/posts` → Get user 123's posts, default sorting
- `GET /users/123/posts?sort=likes&page=2` → User 123's posts, sorted by likes, page 2

* * *

## When to Use URL Parameters vs Query Strings

### Use URL Parameters When

You're identifying a specific resource:

```javascript
// Get specific user
app.get("/users/:userId", (req, res) => {
  // userId is essential
});

// Get specific post
app.get("/posts/:postId", (req, res) => {
  // postId is essential
});

// Hierarchical navigation
app.get("/users/:userId/posts/:postId", (req, res) => {
  // Both are essential to identify the resource
});
```

### Use Query Strings When

You're filtering, sorting, or modifying results:

```javascript
// Filter by role
app.get("/users", (req, res) => {
  // role is optional
});

// Sort results
app.get("/posts", (req, res) => {
  // sort is optional
});

// Pagination
app.get("/products", (req, res) => {
  // page and limit are optional
});

// Search
app.get("/search", (req, res) => {
  // q is optional but the endpoint makes sense without it
});
```

### Decision Tree

```plaintext
Is this parameter essential to identify the resource?
├─ YES  → Use URL parameter
│   /users/123
│   /posts/42
│   /products/electronics/phones
│
└─ NO   → Use query string
    /users?role=admin
    /posts?sort=date
    /products?page=2
```

* * *

## Complete Example: Blog API

Here's a real blog API using both URL parameters and query strings:

```javascript
const express = require("express");
const app = express();

// Get all posts with optional filters
app.get("/posts", (req, res) => {
  const { author, status, page, limit, sort } = req.query;
  
  const posts = filterPosts({
    author,
    status,
    page: page || 1,
    limit: limit || 10,
    sort: sort || "date"
  });
  
  res.json(posts);
});

// Get a specific post
app.get("/posts/:postId", (req, res) => {
  const { postId } = req.params;
  const post = getPost(postId);
  
  if (!post) {
    return res.status(404).json({ error: "Post not found" });
  }
  
  res.json(post);
});

// Get comments on a post
app.get("/posts/:postId/comments", (req, res) => {
  const { postId } = req.params;
  const { page, limit, sort } = req.query;
  
  const comments = getComments(postId, {
    page: page || 1,
    limit: limit || 20,
    sort: sort || "date"
  });
  
  res.json(comments);
});

// Get a specific comment on a specific post
app.get("/posts/:postId/comments/:commentId", (req, res) => {
  const { postId, commentId } = req.params;
  const comment = getComment(postId, commentId);
  
  if (!comment) {
    return res.status(404).json({ error: "Comment not found" });
  }
  
  res.json(comment);
});

// Get posts by a specific author
app.get("/authors/:authorId/posts", (req, res) => {
  const { authorId } = req.params;
  const { page, status } = req.query;
  
  const posts = getPostsByAuthor(authorId, {
    page: page || 1,
    status
  });
  
  res.json(posts);
});

// Create a new post
app.post("/posts", (req, res) => {
  const { title, content, author } = req.body;
  const newPost = createPost({ title, content, author });
  res.status(201).json(newPost);
});

app.listen(3000);
```

### Example Requests

```plaintext
GET /posts
→ Get all posts, default pagination

GET /posts?author=john&status=published
→ Get published posts by john

GET /posts?page=2&limit=5&sort=views
→ Get posts on page 2, 5 per page, sorted by views

GET /posts/42
→ Get post 42

GET /posts/42/comments
→ Get comments on post 42

GET /posts/42/comments?page=1&sort=likes
→ Get comments on post 42, sorted by likes

GET /posts/42/comments/789
→ Get comment 789 on post 42

GET /authors/john/posts
→ Get all posts by author john

GET /authors/john/posts?status=draft&page=1
→ Get draft posts by john, page 1
```

* * *

## Practice Assignment

**1. Create a user endpoint with URL parameters:**

```javascript
const express = require("express");
const app = express();

// Create this endpoint
app.get("/users/:userId", (req, res) => {
  // Get the userId from params
  // Return the user object
});

app.listen(3000);
```

**2. Add a search endpoint with query strings:**

```javascript
// Create this endpoint
app.get("/users/search", (req, res) => {
  // Get query parameters: q, role, active
  // Return filtered results
});
```

**3. Create a nested resource endpoint:**

```javascript
// Get all posts by a user
app.get("/users/:userId/posts", (req, res) => {
  // Use userId from params
  // Use page and limit from query
});
```

**4. Combine parameters and query:**

```javascript
// Get a specific comment on a specific post by a specific user
app.get("/users/:userId/posts/:postId/comments/:commentId", (req, res) => {
  // Use all three parameters
  // Return the comment
});
```

**5. Add filtering to a list endpoint:**

```javascript
// Get all products with filters
app.get("/products", (req, res) => {
  // Query parameters: category, minPrice, maxPrice, inStock
  // Return filtered products
});
```

* * *

## Common Mistakes

### Mistake 1: Confusing Parameters and Query

```javascript
// WRONG - this looks like a parameter but it's a query value
app.get("/users/:userId", (req, res) => {
  // What if you want to filter by role?
  // You'd have to add another parameter
});

// RIGHT - separate the identifier from the filter
app.get("/users/:userId/posts", (req, res) => {
  // userId identifies the user
  // use query for filtering: ?sort=date&page=1
});
```

### Mistake 2: Optional Parameters in the Path

```javascript
// WRONG - optional parameters don't work well in paths
app.get("/users/:userId/:postId", (req, res) => {
  // What if someone hits /users/123? postId is missing
});

// RIGHT - use query strings for optional values
app.get("/users/:userId/posts", (req, res) => {
  // userId is required
  // ?page=2&limit=10 are optional
});
```

### Mistake 3: Too Many Path Parameters

```javascript
// WRONG - path becomes unreadable
app.get("/a/:b/:c/:d/:e/:f", (req, res) => {
  // Hard to understand what this endpoint does
});

// RIGHT - limit path parameters, use query for the rest
app.get("/users/:userId/posts/:postId/comments", (req, res) => {
  // Clear hierarchy
  // use query for filtering: ?sort=date&page=1
});
```

### Mistake 4: Not Validating Parameter Types

```javascript
// WRONG - userId could be anything
app.get("/users/:userId", (req, res) => {
  const userId = req.params.userId;
  // What if userId is "abc"? Should be a number
});

// RIGHT - validate the parameter
app.get("/users/:userId(\\d+)", (req, res) => {
  const userId = req.params.userId;
  // Now userId is guaranteed to be a number
});
```

* * *

## Quick Recap

- **URL parameters** are in the path. They identify a specific resource.
  - Example: `/users/123`, `/posts/42`
  
- **Query strings** come after `?`. They filter or modify results.
  - Example: `/users?role=admin`, `/posts?sort=date`
  
- Use `req.params` to access URL parameters.
  - Example: `req.params.userId`
  
- Use `req.query` to access query strings.
  - Example: `req.query.role`
  
- **Use parameters when:** The value is essential and identifies a resource.
  
- **Use query strings when:** The value is optional and filters or modifies results.
  
- Parameters go in order: `/users/:userId/posts/:postId`
  
- Query strings don't need to be in order: `?page=2&sort=name` is the same as `?sort=name&page=2`
  
- Always validate parameter types and check if query parameters exist.
  
- A good API uses both: parameters for hierarchy and identity, query strings for options.

Get the distinction right and your API routes become clear, predictable, and easy to use.

Happy coding! 🚀

* * *

> If you enjoyed this article, check out my other blogs on this profile. 🔗 Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)

