relic-routing

star 124

Define routes with path parameters, wildcards, tail segments, and typed params in Relic. Use when adding endpoints, handling dynamic URLs, setting up URL patterns, or forwarding requests.

serverpod By serverpod schedule Updated 2/26/2026

name: relic-routing description: Define routes with path parameters, wildcards, tail segments, and typed params in Relic. Use when adding endpoints, handling dynamic URLs, setting up URL patterns, or forwarding requests.

Relic Routing

Routes map incoming requests to handlers based on path and HTTP method. Register routes on RelicApp (which implements RelicRouter).

Handler signature

typedef Handler = FutureOr<Result> Function(Request req);

A handler returns a Result: either a Response, WebSocketUpgrade, or Hijack.

Registering routes

final app = RelicApp()
  ..get('/', (req) => Response.ok(body: Body.fromString('Hello World!')))
  ..post('/', (req) => Response.ok(body: Body.fromString('Got POST')))
  ..put('/user', (req) => Response.ok(body: Body.fromString('PUT /user')))
  ..delete('/user', (req) => Response.ok(body: Body.fromString('DELETE /user')));

Core add method

The convenience methods call add() internally:

app.add(Method.patch, '/api', (req) {
  return Response.ok(body: Body.fromString('PATCH /api'));
});

Multiple methods

app.anyOf({Method.get, Method.post}, '/admin', (req) {
  return Response.ok(body: Body.fromString('Admin - ${req.method.name}'));
});

app.any('/wildcard', handler); // all HTTP methods

Path parameters

Use :name to capture a segment. Access with Symbol-based key:

app.get('/users/:id', (Request req) {
  final userId = req.pathParameters.raw[#id];
  return Response.ok(body: Body.fromString('User $userId'));
});

Typed path parameters

Define typed accessors for automatic parsing:

const idParam = IntPathParam(#id);
const latParam = DoublePathParam(#lat);
const lonParam = DoublePathParam(#lon);

app.get('/users/:id', (Request req) {
  final userId = req.pathParameters.get(idParam); // int
  return Response.ok(body: Body.fromString('User $userId'));
});

app.get('/location/:lat/:lon', (Request req) {
  final lat = req.pathParameters.get(latParam); // double
  final lon = req.pathParameters.get(lonParam); // double
  return Response.ok(body: Body.fromString('Location: $lat, $lon'));
});

Nullable variant (returns null if missing):

final userId = req.pathParameters(idParam); // int?

Built-in accessors: IntPathParam, DoublePathParam, NumPathParam.

Custom path parameter type

const statusParam = PathParam<Status>(#status, Status.parse);
const dateParam = PathParam<DateTime>(#date, DateTime.parse);

Reusable specialization:

final class DateTimePathParam extends PathParam<DateTime> {
  const DateTimePathParam(Symbol key) : super(key, DateTime.parse);
}

const dateParam = DateTimePathParam(#date);

Wildcards

* matches any single segment without capturing:

app.get('/files/*/download', (req) {
  return Response.ok(body: Body.fromString('Downloading...'));
});

Tail segments

/** captures the entire remaining path, exposed via request.remainingPath:

app.get('/static/**', (req) {
  final path = req.remainingPath.toString();
  return Response.ok(body: Body.fromString('Serve $path'));
});

Required when serving directories so the handler knows which file was requested.

Route priority

  1. Literal segments take priority over dynamic segments.
  2. Backtracking ensures the best match if a literal leads to a dead end.
  3. Registration order does not affect matching.
app.get('/:entity/:id', entityHandler);
app.get('/users/:id/profile', profileHandler);

Request for /users/789: tries literal users path first (Route 2), needs third segment /profile but path ends -- backtracks to :entity parameter (Route 1) with entity=users, id=789.

Tail segments with specific routes:

app.get('/files/**', catchAllHandler);
app.get('/files/special/report', reportHandler);

/files/special/report matches the exact route; /files/special/other falls back to catch-all.

Request forwarding

Re-route a request through the same router with copyWith + forwardTo:

app.get('/v1/users', (req) {
  final newReq = req.copyWith(url: req.url.replace(path: '/v2/users'));
  return req.forwardTo(newReq);
});

app.get('/v2/users', (req) {
  return Response.ok(body: Body.fromString('Users list'));
});

forwardTo sends the new request through the full routing pipeline including middleware. It throws StateError if the request was not routed through a RelicRouter.

Install via CLI
npx skills add https://github.com/serverpod/relic --skill relic-routing
Repository Details
star Stars 124
call_split Forks 14
navigation Branch main
article Path SKILL.md
More from Creator