Skip to content

Routing

GamanJS uses composeRouter to declaratively define all application routes. The router supports all HTTP methods, route grouping, per-route middleware, and per-route exception handlers.

src/router.ts
import { composeRouter } from 'gaman/compose';
import AppController from './module/controllers/AppController';
export default composeRouter((r) => {
r.get('/', [AppController, 'HelloWorld']);
r.post('/users', [AppController, 'CreateUser']);
});

All standard HTTP methods are available:

export default composeRouter((r) => {
r.get('/resource', handler);
r.post('/resource', handler);
r.put('/resource/:id', handler);
r.patch('/resource/:id', handler);
r.delete('/resource/:id', handler);
r.head('/resource', handler);
r.options('/resource', handler);
r.all('/resource', handler); // GET, POST, PUT, DELETE, PATCH
});
r.match(['GET', 'POST'], '/custom', handler);

A handler can be a function directly or a Controller tuple:

r.get('/ping', (ctx) => {
return Res.message('pong');
});
import UserController from './module/controllers/UserController';
r.get('/users', [UserController, 'GetAll']);
r.get('/users/:id', [UserController, 'GetById']);
r.post('/users', [UserController, 'Create']);

Format: [ControllerFactory, 'MethodName']

Use :name for dynamic parameters:

r.get('/users/:id', [UserController, 'GetById']);
r.get('/posts/:postId/comments/:commentId', [PostController, 'GetComment']);

Access in handler via ctx.param('id') or ctx.params.

Group routes with a shared prefix:

export default composeRouter((r) => {
r.group('/api/v1', (api) => {
api.get('/users', [UserController, 'GetAll']);
api.post('/users', [UserController, 'Create']);
api.group('/admin', (admin) => {
admin.get('/dashboard', [AdminController, 'Dashboard']);
});
});
});

Resulting routes:

  • GET /api/v1/users
  • POST /api/v1/users
  • GET /api/v1/admin/dashboard

Add per-route middleware using .middleware() chaining:

import AuthMiddleware from './module/middlewares/AuthMiddleware';
export default composeRouter((r) => {
// Route without middleware
r.get('/public', [AppController, 'Public']);
// Route with middleware
r.get('/protected', [AppController, 'Protected'])
.middleware(AuthMiddleware());
// Multiple middleware
r.post('/admin/action', [AdminController, 'DoAction'])
.middleware(AuthMiddleware())
.middleware(RateLimitMiddleware());
});

Automatically applied to all routes in the group:

r.group('/admin', (admin) => {
admin.get('/dashboard', [AdminController, 'Dashboard']);
admin.get('/users', [AdminController, 'Users']);
}).middleware(AuthMiddleware());

Override error handling for specific routes:

import { composeException } from 'gaman/compose';
const CustomErrorHandler = composeException((error, ctx) => {
return Res.send({ detail: error.message }, 500);
});
r.get('/risky', [AppController, 'Risky'])
.exception(CustomErrorHandler);

Give routes a name for reference:

r.get('/users/:id', [UserController, 'GetById'])
.name('users.show');

All route methods return a RouteDefinition supporting chaining:

r.post('/users', [UserController, 'Create'])
.middleware(AuthMiddleware())
.middleware(ValidateMiddleware())
.exception(CustomErrorHandler)
.name('users.create');