Skip to main content

Routing

Routes map incoming HTTP requests to controller methods or inline closures. Vania's router uses a static, fluent API — you register all routes at boot time, and the framework matches them against each incoming request.

Defining Routes​

Routes are defined inside classes that extend Route. Override the prefix getter to set a base path for the whole file, call super.register() first (it applies that prefix), then add your routes:

import 'package:vania/route.dart';
import 'package:my_app/app/http/controllers/user_controller.dart';

class ApiRoute extends Route {
@override
String? get prefix => 'api';

@override
void register() {
super.register();

Router.get('/users', userController.index);
Router.post('/users', userController.store);
Router.get('/users/{id}', userController.show).whereInt('id');
Router.put('/users/{id}', userController.update).whereInt('id');
Router.delete('/users/{id}', userController.destroy).whereInt('id');
}
}

Overriding prefix is the idiomatic way to prefix a file. super.register() calls Router.basePrefix(prefix) for you, so every route below is registered under /api.

Available HTTP Methods​

Router.get(path, action);
Router.post(path, action);
Router.put(path, action);
Router.patch(path, action);
Router.delete(path, action);
Router.options(path, action);
Router.any(path, action); // matches all methods

Additional methods for specialized protocols: Router.purge, Router.copy, Router.link, Router.unlink, Router.lock, Router.unlock, Router.propfind.

Route Actions​

A route action can be a controller method or an inline closure:

// Controller method
Router.get('/posts', postController.index);

// Inline closure returning Response
Router.get('/health', () => Response.json({'status': 'ok'}));

// Closure with Request parameter
Router.post('/echo', (Request req) {
return Response.json(req.all());
});

Route Parameters​

Use curly braces to define dynamic segments:

Router.get('/users/{id}', userController.show);
Router.get('/posts/{postId}/comments/{commentId}', commentController.show);

Parameters are passed as positional arguments to the action. The framework auto-parses int, double, and bool values based on the action's type signature.

Parameter Type Constraints​

Constrain parameter types to reject non-matching requests early:

Router.get('/users/{id}', userController.show)
.whereInt('id');

Router.get('/price/{amount}', handler)
.whereDouble('amount');

Router.get('/active/{flag}', handler)
.whereBool('flag');

// Custom regex constraint
Router.get('/slug/{slug}', handler)
.where('slug', r'[a-z0-9-]+');

Route Groups​

Group related routes to share a prefix, middleware, or domain:

Router.group(() {
Router.get('/profile', userController.profile);
Router.put('/profile', userController.updateProfile);
Router.get('/settings', userController.settings);
}, prefix: 'account', middleware: [AuthenticateMiddleware()]);

Groups can be nested. Prefixes concatenate, and middleware stacks merge:

Router.group(() {
Router.get('/stats', adminController.stats);

Router.group(() {
Router.get('/', userController.index);
Router.delete('/{id}', userController.destroy);
}, prefix: 'users');
}, prefix: 'admin', middleware: [AdminMiddleware()]);
// Produces: /admin/stats, /admin/users, /admin/users/{id}

Base Prefix​

Set a prefix that applies to all subsequent route registrations in a route file:

Router.basePrefix('api/v1');

Router.get('/users', handler); // matches /api/v1/users
Router.get('/posts', handler); // matches /api/v1/posts

Resource Routes​

Register all seven RESTful routes for a resource in a single call:

Router.resource('/posts', postController);

This registers:

MethodPathAction
GET/postsindex
GET/posts/createcreate
POST/postsstore
GET/posts/{id}show
GET/posts/{id}/editedit
PUT/posts/{id}update
DELETE/posts/{id}destroy

Middleware​

Attach middleware to individual routes or groups:

// Single route
Router.get('/dashboard', dashboardController.index)
.middleware([AuthenticateMiddleware()]);

// Multiple middleware
Router.post('/admin/settings', adminController.updateSettings)
.middleware([AuthenticateMiddleware(), AdminMiddleware()]);

See Middleware for writing custom middleware.

Named Routes​

Give a route a name for URL generation:

Router.get('/users/{id}', userController.show)
.name('user.show');

// Later, generate the URL:
String url = Router.url('user.show', {'id': 42});
// => /users/42

Domain Routing​

Bind routes to a specific domain or subdomain:

Router.group(() {
Router.get('/dashboard', adminController.dashboard);
}, domain: 'admin.example.com');

WebSocket Routes​

Register a WebSocket endpoint with event-based handlers:

Router.websocket('/ws', (WebSocketEvent event) {
event.on('message', (WebSocketClient client, dynamic data) {
client.toRoom('chat', 'room_1', data);
});

event.on('disconnect', (WebSocketClient client, dynamic data) {
print('Client disconnected');
});
});

Route Registration​

Routes are registered through a service provider. Register them in boot() (which runs after every provider's register()), so any bindings your routes depend on already exist:

class RouteServiceProvider extends ServiceProvider {
@override
Future<void> register() async {}

@override
Future<void> boot() async {
ApiRoute().register();
// ...register any other route classes here
}
}

Add this provider to your config/app.dart providers list. See Service Providers for the register / boot lifecycle.