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/modules/app/AppRoutes.ts
import { composeRouter } from 'gaman/compose';
import AppController from './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 ctx.send({ message: 'pong' }).ok();
});
import UserController from './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 './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 ctx.send({ detail: error.message }).error();
});
r.get('/risky', [AppController, 'Risky'])
.exception(CustomErrorHandler);

You can mount global middleware or exception handlers specifically for a router:

export default composeRouter((r) => {
r.mountMiddleware(LogMiddleware());
r.mountException(CustomErrorHandler);
r.get('/', [AppController, 'render']);
});
import UserRouter from '../user/UserRouter';
export default composeRouter((r) => {
r.mountRouter('/users', UserRouter);
});

Routers created with composeRouter can be called with a prefix string when being mounted in defineBootstrap:

import UserRouter from './modules/user/UserRouter';
defineBootstrap(async (app) => {
app.mount(UserRouter('/user'));
});

You can inject services into your controllers using r.mountService(). These services will be passed to the controller’s factory function.

import { composeRouter } from 'gaman/compose';
import { AppService } from './services/AppService';
import AppController from './controllers/AppController';
export default composeRouter((r) => {
// Inject services
r.mountService({
appService: AppService(),
});
r.get('/', [AppController, 'render']);
});

See Controller and Service for more details on the dependency injection pattern.

All route methods return a RouteDefinition supporting chaining:

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