Access via:
context.response.body
The response body contains the data sent to the client. Revali automatically handles serialization and wraps your data in a consistent structure.
Default Wrapping#
Revali automatically wraps your response data in a data key for consistency:
@Controller('api')
class ApiController {
@Get('count')
int getCount() {
return 42;
}
}
Response:
{
"data": 42
}
Supported Types#
Primitives#
@Controller('api')
class ApiController {
@Get('message')
String getMessage() {
return 'Hello World';
}
@Get('count')
int getCount() {
return 42;
}
@Get('active')
bool isActive() {
return true;
}
}
Responses:
{"data": "Hello World"}
{"data": 42}
{"data": true}
Custom Objects#
Return custom objects with toJson() method:
class User {
const User({required this.name, required this.email});
final String name;
final String email;
Map<String, dynamic> toJson() {
return {
'name': name,
'email': email,
};
}
}
@Controller('api')
class ApiController {
@Get('user')
User getUser() {
return const User(name: 'John Doe', email: 'john@example.com');
}
}
Response:
{
"data": {
"name": "John Doe",
"email": "john@example.com"
}
}
Collections#
@Controller('api')
class ApiController {
@Get('users')
List<User> getUsers() {
return [
const User(name: 'John', email: 'john@example.com'),
const User(name: 'Jane', email: 'jane@example.com'),
];
}
@Get('settings')
Map<String, dynamic> getSettings() {
return {
'theme': 'dark',
'notifications': true,
};
}
}
Responses:
{
"data": [
{ "name": "John", "email": "john@example.com" },
{ "name": "Jane", "email": "jane@example.com" }
]
}
Raw Content#
Plain Text#
Use StringContent to return raw text without JSON wrapping:
@Controller('api')
class ApiController {
@Get('text')
StringContent getText() {
return StringContent('Hello World');
}
}
Response:
Hello World
HTML Content#
@Controller('api')
class ApiController {
@Get('page')
StringContent getPage() {
return StringContent('''
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
''');
}
}
File Responses#
Local Files#
Return File objects for file downloads:
import 'dart:io';
@Controller('files')
class FileController {
@Get('download')
File downloadFile() {
return File('path/to/file.pdf');
}
}
Memory Files#
Create files from content in memory:
@Controller('files')
class FileController {
@Get('generate')
MemoryFile generateFile() {
return MemoryFile.from(
'Generated content',
mimeType: 'text/plain',
basename: 'document',
extension: 'txt',
);
}
}
Stream Files#
Stream large files efficiently:
@Controller('files')
class FileController {
@Get('stream')
Stream<List<int>> streamFile() {
return File('large-file.zip').openRead();
}
}
Error Responses#
Using Exceptions#
Throw exceptions for error responses:
@Controller('api')
class ApiController {
@Get('user/:id')
User getUser(@Param() String id) {
final user = userService.findById(id);
if (user == null) {
throw NotFoundException('User not found');
}
return user;
}
}
Setting Body in Lifecycle Components#
Interceptors#
Modify response body in interceptors:
class ResponseProcessor implements LifecycleComponent {
InterceptorPostResult processResponse(Response response) {
final data = response.body.data;
// Add metadata to response
response.body = {
'data': data,
'timestamp': DateTime.now().toIso8601String(),
'version': '1.0.0',
};
return const InterceptorPostResult.next();
}
}
Middleware#
class LoggingMiddleware implements LifecycleComponent {
MiddlewareResult processRequest(Response response) {
// Set default response if needed
if (response.body.data == null) {
response.body = {'message': 'No data available'};
}
return const MiddlewareResult.next();
}
}
Best Practices#
Use Return Values#
// ✅ Good - Clean and simple
@Get('users')
List<User> getUsers() {
return userService.getAllUsers();
}
// ❌ Avoid - Unnecessary complexity
@Get('users')
void getUsers(Response response) {
response.body = userService.getAllUsers();
}
Implement toJson#
// ✅ Good - Proper serialization
class User {
const User({required this.name, required this.email});
final String name;
final String email;
Map<String, dynamic> toJson() {
return {'name': name, 'email': email};
}
}
// ❌ Avoid - No serialization method
class User {
const User({required this.name, required this.email});
final String name;
final String email;
}
Throw Exceptions for Error Responses#
@Get('user/:id')
User getUser(@Param() String id) {
final user = userService.findById(id);
if (user == null) {
throw NotFoundException('User not found');
}
return user;
}
What's Next?#
- Learn about response headers for HTTP headers
- Explore status codes for HTTP status codes
- See cookies for session management
- Check out WebSockets for real-time communication