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
