Handlers
Handlers are functions that process incoming requests in Relic applications. Think of them as the core logic that decides what to do when someone visits your web server.
What makes Relic handlers special is their flexibility. Unlike traditional web frameworks, where handlers only return HTTP responses, Relic handlers can:
- Send regular HTTP responses (like web pages or API data).
- Upgrade connections to WebSockets for real-time communication.
- Take direct control of the network connection for custom protocols.
Relic handlers take a Request and return a Result, which can be a Response, Hijack (for connection hijacking), or WebSocketUpgrade (for WebSocket connections).
The Handler is the foundational handler type that serves as the core building block for all Relic applications. It provides the most flexibility by accepting any incoming request and allowing you to return various types of results, making it suitable for any kind of request processing logic.
typedef Handler = FutureOr<Result> Function(Request req);
Request reqis the incoming HTTP request.FutureOr<Result>is a result that can be aResponse,Hijack, orWebSocketUpgrade.
Example:
Response myHelloWorldHandler(final Request req) {
return Response.ok(body: Body.fromString('Hello from Relic!'));
}
How to define handlers
Synchronous handlers
For simple, fast operations:
Response mySyncHandler(final Request req) {
return Response.ok(body: Body.fromString('Fast response'));
}
Asynchronous handlers
For operations that need to wait (database calls, file I/O, etc.):
Future<Response> myAsyncHandler(final Request req) async {
final data = await Future<String>.delayed(
const Duration(milliseconds: 250),
() => 'Hello from Relic!',
);
return Response.ok(body: Body.fromString(data));
}
Using request data
Handlers receive request information including method, URL, headers, and query parameters:
Response myContextInfoHandler(final Request req) {
final method = req.method;
final url = req.url;
final userAgent = req.headers.userAgent;
return Response.ok(
body: Body.fromString(
'Method: ${method.name}, Path: ${url.path} User-Agent: ${userAgent ?? 'unknown'}',
),
);
}
Handling WebSocket connections
For real-time bidirectional communication, you can upgrade connections to WebSockets by returning a WebSocketUpgrade:
WebSocketUpgrade webSocketHandler(final Request req) {
return WebSocketUpgrade((final webSocket) async {
log('WebSocket connection established');
// Send initial greeting to the connected client.
webSocket.sendText('Welcome to Relic WebSocket!');
// Listen for messages and echo them back to the client.
await for (final event in webSocket.events) {
switch (event) {
case TextDataReceived(text: final message):
log('Received: $message');
webSocket.sendText('Echo: $message');
case CloseReceived():
log('WebSocket connection closed');
break;
default:
// Handle binary data, ping/pong, and other WebSocket events.
break;
}
}
});
}
Hijacking connections
For advanced use cases like Server-Sent Events (SSE) or custom protocols, you can hijack the connection:
Hijack mySseHandler(final Request req) {
return Hijack((final channel) async {
// Send Server-Sent Events.
channel.sink.add(utf8.encode('data: Connected'));
// Send periodic updates.
final timer = Timer.periodic(
const Duration(seconds: 1),
(_) => channel.sink.add(utf8.encode('data: ${DateTime.now()}')),
);
// Wait for client disconnect, then cleanup.
await channel.sink.done;
timer.cancel();
});
}
Examples & further reading
Examples
- Handler example - The complete working example from this guide.
API documentation
- Handler typedef - Core handler function signature.
- Result class - Base class for handler return values.
- Response class - HTTP response result.
- WebSocketUpgrade class - WebSocket upgrade result.
- Hijack class - Connection hijacking result.