Skip to main content

Routing & Parameters

HeliosJS provides powerful routing capabilities with intuitive decorators. Let's explore all the ways you can define routes and extract data from requests.

Route Decorators

HeliosJS supports all standard HTTP methods:

DecoratorMethodDescription
@Get(path)GetRetrieve data
@Post(path)PostCreate new resources
@Put(path)PutUpdate entire resources
@Patch(path)PatchUpdate partial resources
@Delete(path)DeleteRemove resources
@Options(path)OptionsGet allowed methods
@Head(path)HeadGet headers only

Basic Routes

import { Controller, Get, Post, Put, Patch, Delete } from "@heliosjs/core";

@Controller("/users")
export class UserController {
@Get("/")
findAll() {
return [{ id: 1, name: "Alice" }];
}

@Post("/")
create(@Body() data: any) {
return { id: 2, ...data };
}

@Put("/:id")
update(@Param("id") id: string, @Body() data: any) {
return { id: parseInt(id), ...data };
}

@Patch("/:id")
partialUpdate(@Param("id") id: string, @Body() data: any) {
return { id: parseInt(id), ...data };
}

@Delete("/:id")
remove(@Param("id") id: string) {
return { deleted: true, id: parseInt(id) };
}
}

Route Parameters

URL Parameters (@Param)

Extract dynamic segments from the URL:

import { Controller, Get, Param } from "@heliosjs/core";

@Controller("/users")
export class UserController {
// Match: /users/123
@Get("/:id")
getUserById(@Param("id") id: string) {
return { userId: parseInt(id) };
}

// Match: /users/123/posts/456
@Get("/:userId/posts/:postId")
getUserPost(
@Param("userId") userId: string,
@Param("postId") postId: string,
) {
return {
userId: parseInt(userId),
postId: parseInt(postId),
};
}
}

Query Parameters (@Query)

Extract query string parameters:

import { Controller, Get, Query } from "@heliosjs/core";

@Controller("/users")
export class UserController {
// Match: /users?page=1&limit=10&search=john
@Get("/")
findAll(
@Query("page") page?: string,
@Query("limit") limit?: string,
@Query("search") search?: string,
) {
return {
page: page ? parseInt(page) : 1,
limit: limit ? parseInt(limit) : 10,
search: search || "",
};
}

// Get all query parameters as an object
@Get("/filter")
filter(@Query() query: any) {
return { filters: query };
}
}

Request Body (@Body)

Extract the request body:

import { Controller, Post, Body } from "@heliosjs/core";

interface CreateUserDto {
name: string;
email: string;
age?: number;
}

@Controller("/users")
export class UserController {
@Post("/")
create(@Body() userData: CreateUserDto) {
// userData contains the parsed JSON body
return { id: 1, ...userData };
}

// Extract specific fields
@Post("/validate")
validate(@Body("email") email: string) {
return { email };
}
}

Validation with DTOs and class-validator

You can add validation to your request bodies using DTOs (Data Transfer Objects) and decorators from the class-validator package. This helps ensure that incoming data meets your requirements before your controller logic runs.

Here's an example of how to define DTOs and use them in a controller:

import { Controller, Post, Body, HttpError } from "@heliosjs/core";
import { IsString, IsBoolean, IsOptional, MinLength } from "class-validator";

// DTO for creating a task
class CreateTaskDto {
@IsString()
@MinLength(3)
title!: string;
}

// DTO for updating a task
class UpdateTaskDto {
@IsOptional()
@IsString()
@MinLength(3)
title?: string;

@IsOptional()
@IsBoolean()
completed?: boolean;
}

interface Task {
id: number;
title: string;
completed: boolean;
}

let tasks: Task[] = [];
let nextId = 1;

@Controller("/tasks")
export class TasksController {
@Post("/")
createTask(@Body(CreateTaskDto) body: CreateTaskDto): Task {
const newTask: Task = {
id: nextId++,
title: body.title.trim(),
completed: false,
};

tasks.push(newTask);
return newTask;
}

@Post("/:id")
updateTask(
@Body(UpdateTaskDto) body: UpdateTaskDto,
): Task | { error: string } {
// Example update logic here
// This is just a placeholder
return { error: "Not implemented" };
}
}

This example shows how to use DTOs with validation decorators to enforce rules on incoming data.

Request Headers (@Headers)

Extract HTTP headers:

import { Controller, Get, Headers } from "@heliosjs/core";

@Controller("/auth")
export class AuthController {
@Get("/profile")
getProfile(
@Headers("authorization") auth: string,
@Headers("user-agent") userAgent: string,
) {
return {
token: auth?.replace("Bearer ", ""),
userAgent,
};
}

// Get all headers
@Get("/headers")
getAllHeaders(@Headers() headers: any) {
return { headers };
}
}

Request Object (@Req)

Access the raw request object:

import { Controller, Get, Req, Request } from "@heliosjs/core";
import { IncomingMessage } from "http";

@Controller("/request")
export class RequestController {
@Get("/info")
getRequestInfo(@Req() req: Request) {
return {
method: req.method,
url: req.url,
headers: req.headers,
httpVersion: req.httpVersion,
};
}
}

Response Object (@Res)

Access the raw response object for manual control:

import { Controller, Get, Res, Response } from "@heliosjs/core";

@Controller("/response")
export class ResponseController {
@Get("/custom")
sendCustomResponse(@Res() res: Response) {
res.statusCode = 201;
res.setHeader("X-Custom-Header", "Hello");
}
}

Combined Example

Here's a complete controller showing all parameter types:

import {
Controller,
Get,
Post,
Put,
Delete,
Param,
Query,
Body,
Headers,
Req,
Res,
} from "@heliosjs/core";

interface Product {
id: number;
name: string;
price: number;
}

@Controller("/products")
export class ProductController {
private products: Product[] = [];
private nextId = 1;

// Get /products?category=electronics&minPrice=10
@Get("/")
findAll(
@Query("category") category?: string,
@Query("minPrice") minPrice?: string,
) {
let filtered = [...this.products];

if (category) {
filtered = filtered.filter((p) => p.name.includes(category));
}

if (minPrice) {
const price = parseInt(minPrice);
filtered = filtered.filter((p) => p.price >= price);
}

return filtered;
}

// Get /products/123
@Get("/:id")
findOne(@Param("id") id: string) {
const product = this.products.find((p) => p.id === parseInt(id));
if (!product) {
throw new Error("Product not found");
}
return product;
}

// Post /products
@Post("/")
create(
@Body() productData: Omit<Product, "id">,
@Headers("authorization") auth: string,
) {
// Check authentication
if (!auth) {
throw new Error("Unauthorized");
}

const newProduct: Product = {
id: this.nextId++,
...productData,
};

this.products.push(newProduct);
return newProduct;
}

// Put /products/123
@Put("/:id")
update(@Param("id") id: string, @Body() productData: Partial<Product>) {
const productId = parseInt(id);
const index = this.products.findIndex((p) => p.id === productId);

if (index === -1) {
throw new Error("Product not found");
}

this.products[index] = { ...this.products[index], ...productData };
return this.products[index];
}

// Delete /products/123
@Delete("/:id")
remove(@Param("id") id: string, @Req() req: any, @Res() res: any) {
const productId = parseInt(id);
const index = this.products.findIndex((p) => p.id === productId);

if (index === -1) {
res.statusCode = 404;
return { error: "Product not found" };
}

this.products.splice(index, 1);
return { deleted: true, id: productId };
}
}

Parameter Summary

DecoratorSourceUsage
@Param(name?)URL path/users/:id 12; @Param('id')
@Query(name?)Query string?page=1 12; @Query('page')
@Body(name?)Request bodyJSON payload
@Headers(name?)HTTP headersAuthorization header
@Files(name?)Multipart data
@Req()Raw requestFull Incoming Message object
@Res()Raw responseFull Server Response object

Multipart Form Data (@Files)

Extract multipart form data from requests, optionally by field name:

import { Controller, Post, Files } from "@heliosjs/core";

@Controller("/upload")
export class UploadController {
@Post("/")
uploadFile(@Files("file") file: any) {
// file contains the uploaded file data
return { uploaded: true, file };
}
}
DecoratorSourceUsage
@Files(name?)Multipart form dataExtract multipart field by name or all multipart data

This corresponds to the @Multipart parameter decorator used in the codebase as @Files.

Route Priority

Routes are matched in the order they are defined. More specific routes should come before general ones:

@Controller("/users")
export class UserController {
// Specific route first
@Get("/profile")
getProfile() {
return { page: "profile" };
}

// General route last
@Get("/:id")
getUserById(@Param("id") id: string) {
return { userId: id };
}
}