Request & Response
Learn how to handle HTTP requests and generate responses in Aim.
Request Object
Access request information through c.req:
Method and Path
app.notFound((c) async {
final method = c.req.method; // GET, POST, etc.
final path = c.req.path; // /users/123
return c.json({
'method': method,
'path': path,
});
});Headers
app.get('/headers', (c) async {
final headers = c.req.headers; // Map<String, String>
final userAgent = c.req.headers['user-agent'];
final auth = c.req.headers['authorization'];
return c.json({
'userAgent': userAgent,
'auth': auth,
});
});Query Parameters
app.get('/search', (c) async {
final queryParameters = c.req.queryParameters; // Map<String, String>
final q = c.req.queryParameters['q'];
final page = c.req.queryParameters['page'] ?? '1';
return c.json({
'query': q,
'page': int.parse(page),
});
});Example: GET /search?q=dart&page=2. queryParameters (from Uri.queryParameters) holds one string per key; a repeated key keeps only its last value.
Request Body
JSON Body
app.post('/users', (c) async {
final body = await c.req.json(); // Map<String, dynamic>
final name = body['name'];
final email = body['email'];
return c.json({
'created': {
'name': name,
'email': email,
},
}, statusCode: 201);
});Raw Body
import 'dart:convert';
import 'dart:typed_data';
app.post('/webhook', (c) async {
final chunks = await c.req.read().toList();
final raw = Uint8List.fromList(chunks.expand((chunk) => chunk).toList());
final text = utf8.decode(raw);
return c.text('Received ${raw.length} bytes');
});c.req.raw is the platform-specific request object (a field, not a callable), not the body. Read the body bytes with c.req.read(), a Stream<List<int>>.
Form Data
Use aim_server_form's formData() extension method on Request — no middleware to register:
import 'package:aim_server/aim_server.dart';
import 'package:aim_server_form/aim_server_form.dart';
app.post('/login', (c) async {
final form = await c.req.formData();
final username = form['username'];
final password = form['password'];
return c.json({'username': username});
});Response Methods
JSON Response
app.get('/api/users', (c) async {
return c.json({
'users': [
{'id': 1, 'name': 'Alice'},
{'id': 2, 'name': 'Bob'},
],
});
});
// With status code
app.post('/users', (c) async {
return c.json({'created': true}, statusCode: 201);
});Text Response
app.get('/hello', (c) async {
return c.text('Hello, World!');
});
// With status code
app.get('/not-found', (c) async {
return c.text('Not Found', statusCode: 404);
});HTML Response
app.get('/page', (c) async {
return c.html('''
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body><h1>Hello!</h1></body>
</html>
''');
});Binary Response
There is no c.bytes() method; use c.stream() for binary content:
import 'dart:io';
app.get('/download', (c) async {
final file = File('assets/document.pdf');
return c.stream(
file.openRead(),
headers: {
'content-type': 'application/pdf',
'content-disposition': 'attachment; filename="document.pdf"',
},
);
});Redirect
app.get('/old-path', (c) async {
return c.redirect('/new-path');
});
// With status code (positional, not named)
app.get('/temp-redirect', (c) async {
return c.redirect('/new-path', 302);
});Stream Response
app.get('/stream', (c) async {
final stream = Stream.periodic(
Duration(seconds: 1),
(count) => 'Event $count\n',
).take(10);
return c.stream(stream);
});For Server-Sent Events, use the aim_server_sse package. See SSE middleware.
Response Headers
Set headers before sending the response:
app.get('/api', (c) async {
c.header('X-API-Version', '1.0');
c.header('X-Request-ID', generateId());
c.header('Cache-Control', 'no-cache');
return c.json({'data': 'value'});
});Common Patterns
Content Negotiation
app.get('/data', (c) async {
final accept = c.req.headers['accept'];
if (accept?.contains('application/json') ?? false) {
return c.json({'message': 'JSON response'});
}
return c.text('Text response');
});File Download
app.get('/export', (c) async {
final data = generateCSV();
c.header('Content-Type', 'text/csv');
c.header('Content-Disposition', 'attachment; filename="export.csv"');
return c.text(data);
});CORS Headers
app.get('/api', (c) async {
c.header('Access-Control-Allow-Origin', '*');
c.header('Access-Control-Allow-Methods', 'GET, POST');
return c.json({'data': 'value'});
});Or use the CORS middleware for full CORS support.
Cache Control
app.get('/static', (c) async {
c.header('Cache-Control', 'public, max-age=31536000');
return c.text('Cached content');
});Best Practices
Set headers before response
dart// ✅ Good c.header('X-Version', '1.0'); return c.json({'data': 'value'}); // ❌ Bad - Headers after response won't work final response = c.json({'data': 'value'}); c.header('X-Version', '1.0'); return response;Validate request data
dartapp.post('/users', (c) async { final body = await c.req.json(); if (body['email'] == null) { return c.json({'error': 'Email required'}, statusCode: 400); } // Process request... });Use appropriate status codes
dart// 201 for creation return c.json({'created': true}, statusCode: 201); // 400 for client errors return c.json({'error': 'Bad request'}, statusCode: 400); // 404 for not found return c.json({'error': 'Not found'}, statusCode: 404); // 500 for server errors return c.json({'error': 'Internal error'}, statusCode: 500);Handle errors gracefully
dartapp.post('/api', (c) async { try { final body = await c.req.json(); // Process... return c.json({'success': true}); } catch (e) { return c.json({'error': 'Invalid JSON'}, statusCode: 400); } });
Next Steps
- Learn about Context for the full API
- Explore Middleware for request processing
- Check out Form and Multipart for file uploads
