ワンクリックで
relic-routing
// 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.
// 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.
Bootstrap a Relic web server, configure RelicApp, start serving, and enable hot reload. Use when creating a new Relic project, setting up a server, or configuring the development workflow.
Create and apply middleware for auth, CORS, logging, and other cross-cutting concerns. Use context properties for type-safe request-scoped data. Use when adding middleware, request/response transformations, or passing data between middleware and handlers.
Handle HTTP requests and create responses with Body, headers, and status codes in Relic. Use when reading request data, parsing JSON, building API responses, working with headers, or streaming content.
Migrate a Dart web server from Shelf to Relic. Side-by-side reference for every API change. Use when converting Shelf code to Relic, replacing shelf/shelf_router/shelf_web_socket imports, or upgrading an existing server.
Serve static files and directories with caching and cache busting in Relic. Use when serving assets, images, CSS, JS, favicons, or configuring HTTP cache control headers.
Handle WebSocket connections and hijack connections for SSE or custom protocols in Relic. Use when implementing real-time communication, WebSocket endpoints, or server-sent events.
| 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. |
Routes map incoming requests to handlers based on path and HTTP method. Register routes on RelicApp (which implements RelicRouter).
typedef Handler = FutureOr<Result> Function(Request req);
A handler returns a Result: either a Response, WebSocketUpgrade, or Hijack.
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')));
add methodThe convenience methods call add() internally:
app.add(Method.patch, '/api', (req) {
return Response.ok(body: Body.fromString('PATCH /api'));
});
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
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'));
});
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.
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);
* matches any single segment without capturing:
app.get('/files/*/download', (req) {
return Response.ok(body: Body.fromString('Downloading...'));
});
/** 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.
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.
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.