AzuraJS Logo
AzuraJSFramework
v2.2 Beta

Routing

Learn about route patterns, parameters, and matching

Routing 🛣️

AzuraJS provides a powerful and flexible routing system. You can use decorators with TypeScript or the functional API with JavaScript.

Route Basics 📍

TypeScript

Routes are defined using HTTP method decorators on controller methods:

import { Controller, Get, Param } from "azurajs/decorators";

@Controller("/api/users")
export class UserController {
  @Get()  // Matches: GET /api/users
  getAllUsers() {}

  @Get("/:id")  // Matches: GET /api/users/123
  getUser(@Param("id") id: string) {}
}

JavaScript

Routes are defined using functional methods:

const { AzuraClient } = require("azurajs");

const app = new AzuraClient();

app.get("/api/users", (req, res) => {
  // GET /api/users
  res.json({ users: [] });
});

app.get("/api/users/:id", (req, res) => {
  // GET /api/users/123
  const { id } = req.params;
  res.json({ id });
});

Route Paths 🗺️

Static Paths

TypeScript

@Get("/users")  // Exact match only
getUsers() {}

@Get("/users/active")  // Exact match
getActiveUsers() {}

JavaScript

app.get("/users", (req, res) => {
  res.json({ users: [] });
});

app.get("/users/active", (req, res) => {
  res.json({ activeUsers: [] });
});

Dynamic Parameters

Use :paramName for route parameters:

TypeScript

@Get("/:id")  // Matches /123, /abc, etc.
getOne(@Param("id") id: string) {}

@Get("/:userId/posts/:postId")  // Multiple parameters
getPost(
  @Param("userId") userId: string,
  @Param("postId") postId: string
) {}

JavaScript

app.get("/:id", (req, res) => {
  const { id } = req.params;  // Matches /123, /abc, etc.
  res.json({ id });
});

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

Query Parameters

Query parameters are not part of the route path:

TypeScript

import { Get, Query } from "azurajs/decorators";

// Route: GET /search
// URL: GET /search?q=john&limit=10
@Get("/search")
search(@Query("q") query: string, @Query("limit") limit: string) {
  return { query, limit };
}

JavaScript

// Route: GET /search
// URL: GET /search?q=john&limit=10
app.get("/search", (req, res) => {
  const { q, limit } = req.query;
  res.json({ query: q, limit });
});

Route Patterns 🎨

Combining Static and Dynamic

@Controller("/api")
export class ApiController {
  @Get("/users/:id/profile")  // GET /api/users/123/profile
  getProfile(@Param("id") id: string) {}

  @Get("/posts/:id/comments/:commentId")  // Multiple params
  getComment(
    @Param("id") postId: string,
    @Param("commentId") commentId: string
  ) {}
}

Nested Routes

import { Controller, Get, Param } from "azurajs/decorators";

@Controller("/api/organizations")
export class OrgController {
  @Get("/:orgId/teams/:teamId/members")
  getMembers(
    @Param("orgId") orgId: string,
    @Param("teamId") teamId: string
  ) {
    return { orgId, teamId };
  }
}

Route Priority ⚡

Routes are matched in the order they're registered. More specific routes should be defined first:

@Controller("/api/users")
export class UserController {
  // ✅ Correct order
  @Get("/me")  // Specific route first
  getCurrentUser() {}

  @Get("/:id")  // Generic route after
  getUser(@Param("id") id: string) {}

  // ❌ Wrong order
  @Get("/:id")  // This would match /me
  getUser2(@Param("id") id: string) {}

  @Get("/me")  // Never reached!
  getCurrentUser2() {}
}

HTTP Methods 🔧

All standard HTTP methods are supported:

TypeScript

@Controller("/api/posts")
export class PostController {
  @Get()  // Read all
  getAll() {}

  @Get("/:id")  // Read one
  getOne(@Param("id") id: string) {}

  @Post()  // Create
  create(@Body() data: any) {}

  @Put("/:id")  // Update (full)
  update(@Param("id") id: string, @Body() data: any) {}

  @Patch("/:id")  // Update (partial)
  patch(@Param("id") id: string, @Body() data: any) {}

  @Delete("/:id")  // Delete
  remove(@Param("id") id: string) {}

  @Head("/:id")  // Check existence
  exists(@Param("id") id: string, @Res() res: ResponseServer) {
    res.status(200).end();
  }

  @Options()  // CORS preflight
  options(@Res() res: ResponseServer) {
    res.set("Allow", "GET, POST").end();
  }
}

JavaScript

// Read all
app.get("/api/posts", (req, res) => {
  res.json({ posts: [] });
});

// Read one
app.get("/api/posts/:id", (req, res) => {
  const { id } = req.params;
  res.json({ id });
});

// Create
app.post("/api/posts", (req, res) => {
  const data = req.body;
  res.status(201).json({ data });
});

// Update (full)
app.put("/api/posts/:id", (req, res) => {
  const { id } = req.params;
  const data = req.body;
  res.json({ id, data });
});

// Update (partial)
app.patch("/api/posts/:id", (req, res) => {
  const { id } = req.params;
  const data = req.body;
  res.json({ id, data });
});

// Delete
app.delete("/api/posts/:id", (req, res) => {
  const { id } = req.params;
  res.status(204).send();
});

// Check existence
app.head("/api/posts/:id", (req, res) => {
  res.status(200).end();
});

// CORS preflight
app.options("/api/posts", (req, res) => {
  res.set("Allow", "GET, POST").end();
});

Programmatic Routes 🔨

You can also define routes programmatically without decorators:

const app = new AzuraClient();

app.get("/api/health", (req, res) => {
  res.json({ status: "ok" });
});

app.post("/api/webhook", (req, res) => {
  console.log("Webhook received:", req.body);
  res.status(200).send("OK");
});

app.put("/api/items/:id", (req, res) => {
  res.json({ id: req.params.id });
});

Route Helpers 🛠️

Multiple Routes per Method

// Not directly supported, but you can create a wrapper
@Controller("/api/posts")
export class PostController {
  private getPostsLogic() {
    return { posts: [] };
  }

  @Get()
  getPosts() {
    return this.getPostsLogic();
  }

  @Get("/all")
  getAllPosts() {
    return this.getPostsLogic();
  }
}

Route Groups

Use controllers to group related routes:

// Auth routes
@Controller("/auth")
export class AuthController {
  @Post("/login")
  login() {}

  @Post("/register")
  register() {}

  @Post("/logout")
  logout() {}
}

// Public API routes
@Controller("/api/public")
export class PublicController {}

// Admin routes
@Controller("/api/admin")
export class AdminController {}

RESTful Routing 🎯

Follow REST conventions for resource-based APIs:

@Controller("/api/users")
export class UserController {
  @Get()  // GET /api/users - List all users
  index() {}

  @Post()  // POST /api/users - Create user
  store(@Body() data: any) {}

  @Get("/:id")  // GET /api/users/:id - Get one user
  show(@Param("id") id: string) {}

  @Put("/:id")  // PUT /api/users/:id - Update user
  update(@Param("id") id: string, @Body() data: any) {}

  @Delete("/:id")  // DELETE /api/users/:id - Delete user
  destroy(@Param("id") id: string) {}
}

Route Testing 🧪

Test your routes:

import { AzuraClient, applyDecorators } from "azurajs";
import { UserController } from "./controllers/UserController";

const app = new AzuraClient();
applyDecorators(app, [UserController]);

// Start server
await app.listen(3000);

// Test endpoints
const response = await fetch("http://localhost:3000/api/users/123");
const data = await response.json();
console.log(data);

Best Practices ✨

Use consistent naming: Follow REST conventions (index, show, store, update, destroy)

Keep routes flat: Avoid deeply nested routes (max 2-3 levels)

Use plural nouns: /users instead of /user, /posts instead of /post

Watch route order: Specific routes before generic ones

Next Steps 📖

On this page