Routing System

Build powerful APIs with Khadem's intuitive routing system. Clean, modular HTTP routing with parameters, middleware, and route groups.

Express-like Routing

Familiar routing patterns with powerful features like parameter extraction, middleware chains, and nested groups for building scalable REST APIs.

Basic Routes

Define routes using HTTP methods with clean, expressive syntax. Each route accepts a path and a handler function.

dart
// Basic HTTP method routes
router.get('/users', (req, res) async {
  final users = await User.all();
  res.sendJson({'users': users});
});

router.post('/users', (req, res) async {
  final data = await req.body;
  final user = await User.create(data);
  res.statusCode(201).sendJson({'user': user});
});

router.put('/users/:id', (req, res) async {
  final userId = req.param('id');
  final data = await req.body;
  final user = await User.find(userId).update(data);
  res.sendJson({'user': user});
});

router.delete('/users/:id', (req, res) async {
  final userId = req.param('id');
  await User.find(userId).delete();
  res.statusCode(204).empty();
});

HTTP Methods

GETRetrieve resources
POSTCreate new resources
PUTUpdate resources
PATCHPartial updates
DELETERemove resources
HEADHeaders only
OPTIONSCORS preflight

Handler Signature

dart
Future Function(Request, Response)

All handlers are async functions that receive Request and Response objects.

Route Parameters

Extract dynamic values from URLs using the :param syntax. Perfect for building RESTful APIs with resource identifiers.

dart
// Route parameters with :param syntax
router.get('/users/:id', (req, res) async {
  final userId = req.param('id');
  final user = await User.find(userId);

  if (user == null) {
    return res.statusCode(404).sendJson({
      'error': 'User not found'
    });
  }

  res.sendJson({'user': user});
});

// Multiple parameters
router.get('/posts/:category/:slug', (req, res) async {
  final category = req.param('category');
  final slug = req.param('slug');

  final post = await Post.where('category', category)
    .where('slug', slug)
    .first();

  res.sendJson({'post': post});
});

// Optional query parameters
router.get('/search', (req, res) async {
  final query = req.query['q'] ?? '';
  final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
  final limit = int.tryParse(req.query['limit'] ?? '10') ?? 10;

  final results = await Search.query(query, page: page, limit: limit);
  res.sendJson({'results': results});
});

Parameter Features

  • :param syntax for named parameters
  • Automatic parameter extraction
  • Type-safe parameter access
  • Regex-based matching support

Route Groups

Organize related routes with shared prefixes and middleware. Group routes for better code organization and consistent configuration across multiple endpoints.

dart
// Route groups with shared prefix and middleware
router.group(
    prefix: '/api/v1',
    middleware: [AuthMiddleware(), ApiMiddleware()],
    routes: (r) {
        r.get('/users', UserController.index);
        r.post('/users', UserController.store);
        r.get('/users/:id', UserController.show);
        r.put('/users/:id', UserController.update);
        r.delete('/users/:id', UserController.destroy);

        // Nested groups
        r.group(
            prefix: '/admin',
            middleware: [AdminMiddleware()],
            routes: (adminRouter) {
                adminRouter.get('/stats', AdminController.stats);
                adminRouter.post('/users/:id/ban', AdminController.banUser);
            }
        );
    }
);

Group Benefits

  • Shared URL prefixes - Avoid repeating paths
  • Common middleware - Apply to all group routes
  • Better organization - Group by feature/version
  • Nested groups - Create hierarchies

Generated Routes

text
Generated routes:
GET    /api/v1/users
POST   /api/v1/users
GET    /api/v1/users/:id
PUT    /api/v1/users/:id
DELETE /api/v1/users/:id
GET    /api/v1/admin/stats
POST   /api/v1/admin/users/:id/ban

All routes inherit the group prefix and middleware

Middleware Integration

Apply middleware at global, group, or route level for authentication, logging, validation, and more.

dart
// Global middleware (apply these on the Server instance before injecting routes)
final server = Server();
server.applyMiddlewares([
    LoggingMiddleware(),
    CorsMiddleware(),
    RateLimitMiddleware(),
]);
server.injectRoutes(registerRoutes);

// Group middleware (inside a registerRoutes(ServerRouter router) function)
router.group(
    prefix: '/admin',
    middleware: [AuthMiddleware(), AdminMiddleware()],
    routes: (r) {
        r.get('/dashboard', AdminController.dashboard);
    }
);

// Route-specific middleware (inside router)
r.get('/public', PublicController.index);
r.get('/protected', ProtectedController.index,
    middleware: [AuthMiddleware()]);
r.get('/admin-only', AdminController.index,
    middleware: [AuthMiddleware(), AdminMiddleware()]);

Middleware Levels

Global

Applied to all routes in your application

Group

Shared across all routes in a group

Route

Applied to specific individual routes

Static File Serving

Serve static assets like CSS, JavaScript, images, and downloads with automatic MIME type detection.

dart
// Serve static files from public directory
server.serveStatic('public');

// Custom static directory
server.serveStatic('assets');

// Now these URLs work automatically:
// GET /css/style.css    → public/css/style.css
// GET /images/logo.png  → public/images/logo.png
// GET /js/app.js        → public/js/app.js

Static Serving Features

  • Automatic file type detection
  • Directory browsing disabled (security)
  • Configurable root directory
  • Efficient caching headers

Advanced Patterns

Query Parameters

dart
router.get('/search', (req, res) async {
  // Query parameters
  final query = req.query['q'];
  final page = req.query['page'];
  final sort = req.query['sort'];

  // Form data (for POST/PUT)
  final formData = await req.body;
  final name = formData['name'];
  final email = formData['email'];

  res.sendJson({
    'query': query,
    'page': page,
    'sort': sort,
    'form': {'name': name, 'email': email}
  });
});

Access URL query strings and form data easily

Response Types

dart
// JSON response
res.sendJson({'message': 'Hello World'});

// HTML response
await res.view('welcome');

// File download
await res.download('files/report.pdf');

// Stream response
await res.stream<String>(
  Stream.periodic(Duration(seconds: 1), (i) => 'Line $i
').take(10)
);

// Custom status
res.statusCode(201).sendJson({'created': true});

// Empty response
res.statusCode(204).empty();

JSON, HTML, files, and streaming responses

Complete API Example

Here's a full example showing how to structure a production-ready REST API with authentication, versioning, and nested resources.

dart
import 'package:khadem/khadem_dart.dart';

// Recommended startup (apply global middleware on Server, then inject routes)
final server = Server();
server.applyMiddlewares([
    LoggingMiddleware(),
    CorsMiddleware(),
    RateLimitMiddleware(),
]);
server.injectRoutes(registerRoutes);
server.serveStatic('public');
await server.start(port: 8080);

// Route registration receives a ServerRouter
void registerRoutes(ServerRouter router) {
    // Public routes
    router.group(
        prefix: '/api/v1',
        routes: (r) {
            r.get('/health', (req, res) async {
                res.sendJson({
                    'status': 'ok',
                    'timestamp': DateTime.now().toIso8601String()
                });
            });
        }
    );

    // User routes (authenticated)
    router.group(
        prefix: '/api/v1/users',
        middleware: [AuthMiddleware()],
        routes: (r) {
            r.get('', UserController.index);
            r.post('', UserController.store);
            r.get('/:id', UserController.show);
            r.put('/:id', UserController.update);
            r.delete('/:id', UserController.destroy);

            // User relationships
            r.get('/:id/posts', UserController.posts);
            r.get('/:id/followers', UserController.followers);
        }
    );

    // Post routes
    router.group(
        prefix: '/api/v1/posts',
        middleware: [AuthMiddleware()],
        routes: (r) {
            r.get('', PostController.index);
            r.post('', PostController.store);
            r.get('/:id', PostController.show);
            r.put('/:id', PostController.update);
            r.delete('/:id', PostController.destroy);

            // Comments
            r.get('/:id/comments', PostController.comments);
            r.post('/:id/comments', PostController.addComment);
        }
    );

    // Admin routes
    router.group(
        prefix: '/api/v1/admin',
        middleware: [AuthMiddleware(), AdminMiddleware()],
        routes: (r) {
            r.get('/dashboard', AdminController.dashboard);
            r.get('/stats', AdminController.stats);
            r.get('/users', AdminController.users);
            r.post('/users/:id/ban', AdminController.banUser);
        }
    );
}

Best Practices

Do's

  • Use route groups for logical organization by feature or API version
  • Apply middleware at the appropriate level (global, group, or route)
  • Use descriptive parameter names that reflect the resource
  • Keep route handlers focused - delegate to controllers/services
  • Always validate and sanitize input parameters
  • Return appropriate HTTP status codes (200, 201, 404, etc.)

Don'ts

  • Don't put business logic directly in route handlers
  • Don't create deeply nested route groups (max 2-3 levels)
  • Don't use generic parameter names like :id everywhere
  • Don't skip input validation - always validate user input
  • Don't forget error handling - use try-catch blocks
  • Don't expose sensitive information in error responses

Need Help with Routing?

Join our community to get help with routing patterns and API design.

On this page