Skip to main content

Controller-Based Routing

Alapa provides a streamlined approach to implement controller-based routing, enabling developers to define routes in a structured and maintainable manner. This method leverages class methods to automatically generate routes based on naming conventions, reducing boilerplate code and enhancing readability.

Creating Controller-Based Routing

Controller-based routing in Alapa is designed to be intuitive. By following camel-case naming conventions for methods within a controller class, the framework automatically interprets the method names to generate corresponding routes. The first part of the method name specifies the HTTP verb, while the remaining parts define the route path, segmented by uppercase letters.

Example:

Below is an example of a controller class with method-based routing:

src/app/users/Controller.ts
import { Controller, Request, Response, Params } from "alapa";

export class UserController extends Controller {
// Handles GET /users
getIndex(req: Request, res: Response) {
res.send("Welcome to the user dashboard");
}

// Handles GET /users/user/:userId
@Params("userId")
getUser(req: Request, res: Response) {
res.send(`User ID: ${req.params.userId}`);
}
}

Registering the Controller with Routes

To integrate the controller with the application's routing system, use the controller method provided by the Router class. This method maps the controller methods to their respective routes.

src/app/users/router.ts
import { Router } from "alapa";
import { UserController } from "./Controller";

const userRoute = new Router();
userRoute.controller("users", UserController);

export default userRoute;

The above setup generates the following routes:

HTTP MethodRoute PathRoute Name
GET/usersusers.index
GET/users/user/:userIdusers.user

Supported HTTP Verbs

Alapa supports a variety of HTTP verbs, each corresponding to a specific HTTP method. Below is a list of available verbs and their associated HTTP methods:

  • get: Generates a GET route.
  • post: Generates a POST route.
  • put: Generates a PUT route.
  • patch: Generates a PATCH route.
  • delete: Generates a DELETE route.
  • head: Generates a HEAD route.
  • use: Generates a middleware route.
  • all: Generates a route that matches all HTTP methods.

Best Practice

When using verbs with the Index suffix, the Index part is omitted from the route path. For example, getIndex generates a route for /users instead of /users/index.

Controller Configuration Options

Alapa provides several options to customize controller-based routing. These options allow you to tailor the routing behavior to suit your application's needs.

Option NameTypeOptionalDefault ValueDescription
namePrefixstringYesundefinedPrefix added to the route names.
docPrefixstringYesGlobal docPrefix from configPrefix added to the API documentation path.
middlewareAllarrayYes[]Middleware applied to all routes in the controller.
middlewareobjectYes{}Middleware applied to specific routes, keyed by method name.

Example: Using namePrefix

src/app/users/router.ts
import { Router } from "alapa";
import { UserController } from "./Controller";

const userRoute = new Router();
userRoute.controller("users", UserController, { namePrefix: "app-users" });

export default userRoute;

This configuration modifies the route names as follows:

HTTP MethodRoute PathRoute Name
GET/usersapp-users.index
GET/users/user/:userIdapp-users.user

Example: Using docPrefix

The docPrefix option is particularly useful for versioning your API documentation. It prepends the specified prefix to the API paths in the generated Swagger documentation.

src/app/users/router.ts
import { Router } from "alapa";
import { UserController } from "./Controller";

const userRoute = new Router();
userRoute.controller("users", UserController, { docPrefix: "api/v1" });

export default userRoute;

This results in the following API documentation paths:

HTTP MethodRoute PathDocumentation Path
GET/usersapi/v1/users
GET/users/user/:userIdapi/v1/users/{userId}

Alternatively, you can define the docPrefix directly within the controller class:

src/app/users/Controller.ts
import { Controller, Request, Response, Params } from "alapa";

export class UserController extends Controller {
docPrefix: string = "api/v1/";
}

Example: Applying Middleware

Middleware can be applied globally to all routes within a controller or selectively to specific routes.

Global Middleware (middlewareAll)

src/app/users/router.ts
import { Router } from "alapa";
import { UserController } from "./Controller";
import { userMiddleware } from "../middleware";

const userRoute = new Router();
userRoute.controller("users", UserController, {
middlewareAll: [userMiddleware],
});

export default userRoute;

Route-Specific Middleware (middleware)

src/app/users/router.ts
import { Router } from "alapa";
import { UserController } from "./Controller";
import { indexMiddleware } from "../middleware";

const userRoute = new Router();
userRoute.controller("users", UserController, {
middleware: {
getIndex: indexMiddleware,
},
});

export default userRoute;

Defining Middleware Within the Controller

Middleware can also be defined directly within the controller class. This approach centralizes route-specific logic, making it easier to manage.

src/app/users/Controller.ts
import { Controller, Request, Response, NextFunction } from "alapa";

export class UserController extends Controller {
// Middleware for /users
useIndex(req: Request, res: Response, next: NextFunction) {
console.log("Middleware for /users");
next();
}

// Middleware for /users/user
useUser(req: Request, res: Response, next: NextFunction) {
console.log("Middleware for /users/user");
next();
}
}

Adding Path Parameters

Path parameters can be added using the @Params decorator. This decorator allows you to define dynamic segments in your routes.

src/app/posts/Controller.ts
import { Controller, Request, Response, Params } from "alapa";

export class PostController extends Controller {
// Handles GET /posts/post/:postId/:userId
@Params("postId", "userId")
getPost(req: Request, res: Response) {
res.send(`Post ID: ${req.params.postId}, User ID: ${req.params.userId}`);
}
}

Manipulating Route Paths

The @Params decorator can also be used to customize route paths. By using forward slashes (/), you can structure the URL as needed.

src/app/posts/Controller.ts
import { Controller, Request, Response, Params } from "alapa";

export class PostController extends Controller {
// Handles GET /posts/post/:postId/user/:userId
@Params("postId/user", "userId")
getPost(req: Request, res: Response) {
res.send(`Post ID: ${req.params.postId}, User ID: ${req.params.userId}`);
}

// Handles GET /posts/:postId/user/:userId/all-posts
@Params("postId/user", "userId/all-posts")
getIndex(req: Request, res: Response) {
res.send("All posts for the user");
}
}

Handling Method Name Conflicts

In scenarios where method names conflict (e.g., multiple middlewares with the same name handling different operations but pointing to the same path), you can use underscores (_) in the method names. Alapa ignores underscores when generating routes

src/app/posts/Controller.ts
import { Controller, Request, Response, Params } from "alapa";

export class PostController extends Controller {
// Handles GET /posts/:postId/active
@Params("postId/active")
get_Index(req: Request, res: Response) {
res.send("Active posts");
}

// Handles GET /posts/:postId/draft
@Params("postId/draft")
get_Index_(req: Request, res: Response) {
res.send("Draft posts");
}

// Handles GET /posts/:postId/trashed
@Params("postId/trashed")
get___Index___(req: Request, res: Response) {
res.send("Trashed posts");
}
}

note

The @Params decorator is a powerful tool for customizing route paths. It allows you to dynamically structure URLs, making your routing more flexible and expressive.