Lesson 11 of 5022 min read

How to Handle Route Params, Query Params and Request Body in NestJS

A complete guide to extracting and using route parameters, query strings, and request bodies in NestJS with real CRUD examples.

Author: CodersNexus

How to Handle Route Params, Query Params and Request Body in NestJS

Every single API endpoint you will ever build depends on reading data the client sends, whether that is an ID embedded in the URL, a filter passed as a query string, or a full JSON payload in a POST request. NestJS gives you three purpose-built decorators for exactly these three sources of input, and knowing precisely when to use each one is one of the most practical, interview-relevant skills a backend developer can have.

This lesson goes deeper than a quick syntax overview. You will see how route params, query params, and the request body behave differently, when to combine more than one in a single route, and how to avoid the subtle bugs that come from mixing them up.

NestJS Route Params Query Params Request Body: Learning Objectives

  • Distinguish clearly between route parameters, query parameters, and the request body.
  • Use the @Param(), @Query(), and @Body() decorators correctly in real routes.
  • Combine multiple sources of input in a single route handler.
  • Understand default type behavior (strings) for params and query values.
  • Design realistic REST endpoints that use the right input source for the right job.

NestJS Route Params Query Params Request Body: Key Terms and Definitions

  • Route parameter: A named, dynamic segment of a URL path, declared with a colon (e.g. :id), used to identify a specific resource.
  • Query parameter: A key-value pair appended to a URL after a question mark, typically used for optional filtering, sorting, or pagination.
  • Request body: The payload sent by a client, typically as JSON, in POST, PUT, or PATCH requests, used to submit new or updated data.
  • @Param() decorator: Extracts one or all route parameters from the current request's URL path.
  • @Query() decorator: Extracts one or all query string parameters from the current request's URL.
  • @Body() decorator: Extracts the parsed request body, either as a whole object or a specific field.

How NestJS Route Params Query Params Request Body Works: Detailed Explanation

Route parameters answer the question 'which specific resource am I talking about?' A route like GET /orders/:orderId clearly identifies exactly one order by its ID, and this ID is essential to the route's meaning, not optional. If it were missing, the route itself would not make sense. This is why route parameters are declared directly inside the path string with a colon prefix and extracted using @Param('orderId').

Query parameters, by contrast, answer the question 'how do you want this data shaped or filtered?' A route like GET /orders?status=pending&page=2 is still fundamentally about listing orders, but the query string refines that request: only pending orders, and specifically the second page of results. Query parameters are inherently optional; a well-designed API should behave sensibly even if none are provided, typically falling back to sensible defaults. These are extracted using @Query('status') or @Query('page').

The request body answers a completely different question: 'what data am I sending you to create or update?' Unlike route and query parameters, which are always strings embedded in the URL, the request body can carry complex, deeply nested JSON structures, such as a new order containing multiple line items. Extracted using @Body(), this is the appropriate mechanism for POST, PUT, and PATCH requests where the client needs to transmit structured data rather than simply identify or filter a resource.

A critical detail beginners often miss is that both route parameters and query parameters always arrive as strings, even if they look numeric in the URL. A request to /orders/42 gives you the string '42' via @Param('id'), not the number 42. If your application logic expects a number, for comparisons, database lookups, or calculations, you must explicitly convert it, commonly with Number(id) or a dedicated NestJS pipe (covered in a later lesson) that automates this conversion and validation.

Many real-world routes combine more than one of these sources in a single handler. Updating a specific order, for example, needs the order's ID from the route path and the updated fields from the request body simultaneously: PUT /orders/:orderId with a handler signature like update(@Param('orderId') orderId: string, @Body() updateData: UpdateOrderDto).

Interview-Friendly Explanation

A strong interview or viva answer for nestjs route params query params request body must go beyond a one-line definition. Interviewers evaluating BCA, MCA, B.Tech, and self-taught backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters in production Node.js applications. Use the four-point framework below when answering under time pressure.

  • Definition point: Route parameter: A named, dynamic segment of a URL path, declared with a colon (e.g. :id), used to identify a specific resource.
  • Working point: Use the @Param(), @Query(), and @Body() decorators correctly in real routes.
  • Example point: E-commerce order tracking pages use route parameters to load a specific order (/orders/:id) while using query parameters to control display options like /orders/:id?view=summary.
  • Conclusion point: Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.

How to Answer This in a Technical Interview

  1. Give a two-to-three sentence definition using correct NestJS and Node.js terminology.
  2. Add one specific example drawn from a real backend scenario such as an e-commerce, fintech, or SaaS API.
  3. Mention the request lifecycle stage this concept operates at (pipe, guard, middleware, filter, interceptor) since interviewers often test whether you know the execution order.
  4. Close with one benefit, trade-off, or production use case.
  5. Avoid vague answers like "it just validates stuff" — interviewers filter these out immediately.

Practical Scenario

Imagine you are hardening a production API for a SaaS product, similar to systems used at companies like Razorpay, Freshworks, or Postman. Every lesson in this module maps directly to a decision you will make while making that backend secure, predictable, and resilient to bad input: validating what comes in, transforming it safely, catching failures gracefully, and protecting routes from unauthorized access.

NestJS Route Params Query Params Request Body: Architecture and Flow Diagram

Visualize a single incoming request broken into three labeled arrows pointing at different parts of a URL and body:

PUT /orders/42?notify=true Body: { "status": "shipped" }
| | |
v v v
@Param('orderId') @Query('notify') @Body('status')
→ '42' (string) → 'true' (string) → 'shipped'

Add a note: 'All three decorators can be used together in the same route handler.'

NestJS Route Params Query Params Request Body: Input Source Reference Table

Input SourceDecoratorTypical Use CaseAlways Present?
Route parameter@Param('key')Identifying a specific resource by ID or slugYes, required by route design
Query parameter@Query('key')Filtering, sorting, pagination, optional flagsNo, typically optional
Request body@Body('key') or @Body()Submitting new or updated structured dataRequired for POST/PUT/PATCH with data

NestJS Route Params Query Params Request Body: NestJS Code Example

import { Controller, Get, Put, Param, Query, Body } from '@nestjs/common';

interface UpdateOrderInput {
  status?: string;
  notes?: string;
}

@Controller('orders')
export class OrdersController {
  // GET /orders?status=pending&page=2
  @Get()
  findAll(@Query('status') status?: string, @Query('page') page = '1') {
    return {
      message: 'Fetching orders',
      filters: { status: status || 'all', page: Number(page) },
    };
  }

  // GET /orders/42
  @Get(':orderId')
  findOne(@Param('orderId') orderId: string) {
    return { message: `Fetching order with ID ${orderId}` };
  }

  // PUT /orders/42?notify=true  Body: { "status": "shipped" }
  @Put(':orderId')
  update(
    @Param('orderId') orderId: string,
    @Query('notify') notify: string,
    @Body() updateData: UpdateOrderInput,
  ) {
    return {
      message: `Order ${orderId} updated`,
      shouldNotifyCustomer: notify === 'true',
      changes: updateData,
    };
  }
}

The findAll method demonstrates optional query parameters with sensible defaults, using page = '1' as a fallback when no page is specified. The findOne method shows a route parameter used to identify a single resource, which is mandatory by the route's own structure. The update method shows all three input sources combined in one handler: orderId identifies which order to update, notify is an optional flag read from the query string, and updateData carries the actual fields being changed, submitted in the request body. This single example mirrors the shape of nearly every real-world 'update a resource' endpoint you will build.

Real-World NestJS Route Params Query Params Request Body Industry Examples

  • E-commerce order tracking pages use route parameters to load a specific order (/orders/:id) while using query parameters to control display options like /orders/:id?view=summary.
  • Admin panels rely heavily on query parameters for table filtering and pagination, such as /users?role=admin&sortBy=createdAt&page=3, without ever needing route parameters for a list view.
  • Payment webhooks and update endpoints combine a route parameter (the transaction ID) with a request body carrying the new payment status, mirroring the update example shown in this lesson.
  • Search features across nearly every SaaS product use query parameters exclusively, such as /products?search=laptop&category=electronics, since search terms are optional refinements, not resource identifiers.

NestJS Route Params Query Params Request Body Interview Questions and Answers

Q1. What is the core difference between a route parameter and a query parameter?

Short answer: A route parameter is a required, structural part of the URL path used to identify a specific resource, such as an ID in /orders/:id. A query parameter is an optional key-value pair appended after a question mark, used to filter, sort, or refine a request, such as ?status=pending, and a well-designed endpoint should still function sensibly if query parameters are omitted.

Detailed explanation: Route parameters answer the question 'which specific resource am I talking about?' A route like GET /orders/:orderId clearly identifies exactly one order by its ID, and this ID is essential to the route's meaning, not optional. If it were missing, the route itself would not make sense. This is why route parameters are declared directly inside the path string with a colon prefix and extracted using @Param('orderId'). Query parameters, by contrast, answer the question 'how do you want this data shaped or filtered?' A route like GET /orders?status=pending&page=2 is still fundamentally about listing orders, but the query string refines that request: only pending orders, and specifically the second page of results. Query parameters are inherently optional; a well-designed API should behave sensibly even if none are provided, typically falling back to sensible defaults. These are extracted using @Query('status') or @Query('page'). The request body answers a completely different question: 'what data am I sending you to create or update?' Unlike route and query parameters, which are always strings embedded in the URL, the request body can carry complex, deeply nested JSON structures, such as a new order containing multiple line items. Extracted using @Body(), this is the appropriate mechanism for POST, PUT, and PATCH requests where the client needs to transmit structured data rather than simply identify or filter a resource. A critical detail beginners often miss is that both route parameters and query parameters always arrive as strings, even if they look numeric in the URL. A request to /orders/42 gives you the string '42' via @Param('id'), not the number 42. If your application logic expects a number, for comparisons, database lookups, or calculations, you must explicitly convert it, commonly with Number(id) or a dedicated NestJS pipe (covered in a later lesson) that automates this conversion and validation. Many real-world routes combine more than one of these sources in a single handler. Updating a specific order, for example, needs the order's ID from the route path and the updated fields from the request body simultaneously: PUT /orders/:orderId with a handler signature like update(@Param('orderId') orderId: string, @Body() updateData: UpdateOrderDto).

Practical example: E-commerce order tracking pages use route parameters to load a specific order (/orders/:id) while using query parameters to control display options like /orders/:id?view=summary.

Interview tip: Route parameters identify a specific resource and are required by the route's own structure; extracted with @Param().

Revision hook: Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.

Q2. Why are route and query parameters always received as strings in NestJS?

Short answer: URL paths and query strings are inherently plain text; there is no native way to encode a number or boolean directly in a URL. NestJS's @Param() and @Query() decorators simply extract these text values as-is, so any necessary type conversion, such as turning '42' into the number 42, must be done explicitly or through a validation pipe.

Detailed explanation: The findAll method demonstrates optional query parameters with sensible defaults, using page = '1' as a fallback when no page is specified. The findOne method shows a route parameter used to identify a single resource, which is mandatory by the route's own structure. The update method shows all three input sources combined in one handler: orderId identifies which order to update, notify is an optional flag read from the query string, and updateData carries the actual fields being changed, submitted in the request body. This single example mirrors the shape of nearly every real-world 'update a resource' endpoint you will build.

Practical example: Admin panels rely heavily on query parameters for table filtering and pagination, such as /users?role=admin&sortBy=createdAt&page=3, without ever needing route parameters for a list view.

Interview tip: Query parameters refine or filter a request and are optional by convention; extracted with @Query().

Revision hook: Real-world routes frequently combine all three input sources in a single handler, and this is a completely normal pattern.

Q3. When would you use @Body() instead of @Query() to receive data?

Short answer: @Body() should be used whenever the client needs to send structured or complex data to create or update a resource, typically in POST, PUT, or PATCH requests. @Query() is appropriate for simple, optional key-value refinements to a GET request, not for submitting new data, since query strings have practical length and structure limitations.

Detailed explanation: Route parameters, query parameters, and the request body serve three distinct purposes in a NestJS application: identifying a specific resource, optionally refining a request, and carrying structured data for creation or updates, respectively. NestJS provides the @Param(), @Query(), and @Body() decorators to cleanly extract each of these directly within a route handler's method signature. Understanding when to use each, and remembering that both route and query values arrive as strings, prevents a wide range of common bugs and forms the foundation for building well-designed, predictable REST APIs.

Practical example: Payment webhooks and update endpoints combine a route parameter (the transaction ID) with a request body carrying the new payment status, mirroring the update example shown in this lesson.

Interview tip: Request body carries structured data for creating or updating resources; extracted with @Body().

Revision hook: Always assume string types from @Param() and @Query() until you explicitly convert them.

Q4. How would you design a route to update a specific resource using both an ID and new data?

Short answer: You would combine a route parameter to identify the resource with a request body carrying the updated fields, for example @Put(':id') update(@Param('id') id: string, @Body() updateDto: UpdateDto), which cleanly separates 'which resource' from 'what changes' within a single, well-structured route handler.

Detailed explanation: Route parameters answer the question 'which specific resource am I talking about?' A route like GET /orders/:orderId clearly identifies exactly one order by its ID, and this ID is essential to the route's meaning, not optional. If it were missing, the route itself would not make sense. This is why route parameters are declared directly inside the path string with a colon prefix and extracted using @Param('orderId'). Query parameters, by contrast, answer the question 'how do you want this data shaped or filtered?' A route like GET /orders?status=pending&page=2 is still fundamentally about listing orders, but the query string refines that request: only pending orders, and specifically the second page of results. Query parameters are inherently optional; a well-designed API should behave sensibly even if none are provided, typically falling back to sensible defaults. These are extracted using @Query('status') or @Query('page'). The request body answers a completely different question: 'what data am I sending you to create or update?' Unlike route and query parameters, which are always strings embedded in the URL, the request body can carry complex, deeply nested JSON structures, such as a new order containing multiple line items. Extracted using @Body(), this is the appropriate mechanism for POST, PUT, and PATCH requests where the client needs to transmit structured data rather than simply identify or filter a resource. A critical detail beginners often miss is that both route parameters and query parameters always arrive as strings, even if they look numeric in the URL. A request to /orders/42 gives you the string '42' via @Param('id'), not the number 42. If your application logic expects a number, for comparisons, database lookups, or calculations, you must explicitly convert it, commonly with Number(id) or a dedicated NestJS pipe (covered in a later lesson) that automates this conversion and validation. Many real-world routes combine more than one of these sources in a single handler. Updating a specific order, for example, needs the order's ID from the route path and the updated fields from the request body simultaneously: PUT /orders/:orderId with a handler signature like update(@Param('orderId') orderId: string, @Body() updateData: UpdateOrderDto).

Practical example: Search features across nearly every SaaS product use query parameters exclusively, such as /products?search=laptop&category=electronics, since search terms are optional refinements, not resource identifiers.

Interview tip: Both @Param() and @Query() values are always strings, even for numeric-looking input.

Revision hook: Well-designed query parameters should have sensible fallback defaults so a route works correctly even when they're omitted.

NestJS Route Params Query Params Request Body MCQs and Practice Questions

1. Which decorator would you use to extract the value of :userId from the route /users/:userId?

  1. @Query('userId')
  2. @Body('userId')
  3. @Param('userId')
  4. @Headers('userId')

Answer: C. @Param('userId')

Explanation: @Param() extracts dynamic segments defined directly in the route path, such as :userId, making it the correct choice for identifying a specific resource embedded in the URL.

Concept link: Route parameters identify a specific resource and are required by the route's own structure; extracted with @Param().

Why this matters: Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.

2. What data type does @Query('page') return when the URL is /items?page=3?

  1. The number 3
  2. The string '3'
  3. A boolean
  4. An array

Answer: B. The string '3'

Explanation: Query parameters are always returned as strings by default in NestJS, regardless of how they appear in the URL, so explicit conversion is required if a number is needed.

Concept link: Query parameters refine or filter a request and are optional by convention; extracted with @Query().

Why this matters: Real-world routes frequently combine all three input sources in a single handler, and this is a completely normal pattern.

3. For a POST request creating a new product with a name and price, which decorator should primarily be used to receive this data?

  1. @Param()
  2. @Query()
  3. @Body()
  4. @Headers()

Answer: C. @Body()

Explanation: @Body() is designed to extract structured payload data sent in the request body, which is the appropriate mechanism for submitting new resource data in POST requests.

Concept link: Request body carries structured data for creating or updating resources; extracted with @Body().

Why this matters: Always assume string types from @Param() and @Query() until you explicitly convert them.

4. In the route PUT /orders/:id, which decorator would correctly extract the order's ID?

  1. @Body('id')
  2. @Query('id')
  3. @Param('id')
  4. @Session('id')

Answer: C. @Param('id')

Explanation: Since :id is defined directly in the route path, it must be extracted using @Param('id'), not @Query() or @Body(), which read from entirely different parts of the request.

Concept link: Both @Param() and @Query() values are always strings, even for numeric-looking input.

Why this matters: Well-designed query parameters should have sensible fallback defaults so a route works correctly even when they're omitted.

Common NestJS Route Params Query Params Request Body Mistakes to Avoid

  • Trying to read a route parameter using @Query() or vice versa, resulting in undefined values because the decorator is looking in the wrong part of the request.
  • Forgetting that numeric-looking route or query values are actually strings, causing bugs in comparisons like id === 42 (string vs number) that silently fail.
  • Overloading query parameters with data that should really be in the request body, such as trying to pass a large nested object through the query string.
  • Not providing sensible default values for optional query parameters, causing downstream logic to break when a parameter is omitted entirely.

NestJS Route Params Query Params Request Body: Interview Notes and Exam Tips

  • Route parameters identify a specific resource and are required by the route's own structure; extracted with @Param().
  • Query parameters refine or filter a request and are optional by convention; extracted with @Query().
  • Request body carries structured data for creating or updating resources; extracted with @Body().
  • Both @Param() and @Query() values are always strings, even for numeric-looking input.

Key NestJS Route Params Query Params Request Body Takeaways

  • Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.
  • Real-world routes frequently combine all three input sources in a single handler, and this is a completely normal pattern.
  • Always assume string types from @Param() and @Query() until you explicitly convert them.
  • Well-designed query parameters should have sensible fallback defaults so a route works correctly even when they're omitted.

NestJS Route Params Query Params Request Body: Summary

Route parameters, query parameters, and the request body serve three distinct purposes in a NestJS application: identifying a specific resource, optionally refining a request, and carrying structured data for creation or updates, respectively. NestJS provides the @Param(), @Query(), and @Body() decorators to cleanly extract each of these directly within a route handler's method signature. Understanding when to use each, and remembering that both route and query values arrive as strings, prevents a wide range of common bugs and forms the foundation for building well-designed, predictable REST APIs.

Frequently Asked Questions

Yes, this is a very common pattern, especially for update operations where you need a resource's ID from the route path and the new data from the request body, sometimes alongside optional flags from the query string, all within the same method signature. In interviews, tie this back to: Route parameters identify a specific resource and are required by the route's own structure; extracted with @Param(). In real applications, consider this example: E-commerce order tracking pages use route parameters to load a specific order (/orders/:id) while using query parameters to control display options like /orders/:id?view=summary. Key revision takeaway: Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.

Route parameters are always returned as strings, so comparing '42' === 42 in JavaScript returns false due to strict type comparison. Convert the value explicitly using Number(id) or a validation pipe before comparing it to a numeric value. In interviews, tie this back to: Query parameters refine or filter a request and are optional by convention; extracted with @Query(). In real applications, consider this example: Admin panels rely heavily on query parameters for table filtering and pagination, such as /users?role=admin&sortBy=createdAt&page=3, without ever needing route parameters for a list view. Key revision takeaway: Real-world routes frequently combine all three input sources in a single handler, and this is a completely normal pattern.

It is technically possible but not recommended. Query strings have practical length limitations imposed by browsers and servers, and are meant for simple, optional filtering values, not for transmitting large or deeply nested structured data, which belongs in the request body. In interviews, tie this back to: Request body carries structured data for creating or updating resources; extracted with @Body(). In real applications, consider this example: Payment webhooks and update endpoints combine a route parameter (the transaction ID) with a request body carrying the new payment status, mirroring the update example shown in this lesson. Key revision takeaway: Always assume string types from @Param() and @Query() until you explicitly convert them.

The value will be undefined unless you provide a default using JavaScript's default parameter syntax, such as @Query('status') status = 'all', which ensures your logic has a sensible fallback value to work with. In interviews, tie this back to: Both @Param() and @Query() values are always strings, even for numeric-looking input. In real applications, consider this example: Search features across nearly every SaaS product use query parameters exclusively, such as /products?search=laptop&category=electronics, since search terms are optional refinements, not resource identifiers. Key revision takeaway: Well-designed query parameters should have sensible fallback defaults so a route works correctly even when they're omitted.

Yes. Calling @Param() without a specific key returns an object containing all route parameters defined in the path, such as { orderId: '42', itemId: '7' } for a route like /orders/:orderId/items/:itemId. In interviews, tie this back to: Route parameters identify a specific resource and are required by the route's own structure; extracted with @Param(). In real applications, consider this example: E-commerce order tracking pages use route parameters to load a specific order (/orders/:id) while using query parameters to control display options like /orders/:id?view=summary. Key revision takeaway: Choosing the right input source (param, query, or body) is a design decision, not just a syntax choice.

It is strongly discouraged and considered non-standard REST practice. GET requests are meant to be safe and idempotent, retrieving data without side effects, and many HTTP clients, proxies, and caching layers do not reliably support or forward a body on GET requests. In interviews, tie this back to: Query parameters refine or filter a request and are optional by convention; extracted with @Query(). In real applications, consider this example: Admin panels rely heavily on query parameters for table filtering and pagination, such as /users?role=admin&sortBy=createdAt&page=3, without ever needing route parameters for a list view. Key revision takeaway: Real-world routes frequently combine all three input sources in a single handler, and this is a completely normal pattern.