Access via:
context.meta
Metadata allows you to attach custom information to endpoints and access it during request processing. This is useful for storing configuration, annotations, or any data that needs to be shared between different parts of your application.
Creating Metadata Classes#
To create metadata, implement the MetaData interface:
import 'package:revali_router_annotations/revali_router_annotations.dart';
class Public implements MetaData {
const Public();
}
class Role implements MetaData {
const Role(this.name);
final String name;
}
Basic Operations#
Annotating with Metadata#
Metadata is stored by annotating controllers and methods:
@Controller('api')
class ApiController {
@Public()
@Get('health')
String healthCheck() {
return 'OK';
}
@Role('admin')
@Get('admin-data')
String adminData() {
return 'Admin only data';
}
}
Retrieving Metadata#
Get metadata using get() - returns a list of all instances:
// Get all instances of a type
List<Role> roles = context.meta.get<Role>();
// Returns: [Role('admin')] for the admin-data endpoint
Checking for Metadata#
Check if metadata exists using has():
if (context.meta.has<Public>()) {
// This endpoint is public
}
if (context.meta.has<Role>()) {
// This endpoint has role metadata
}
Direct vs Inherited Metadata#
The MetaScope provides access to both direct and inherited metadata:
@Controller('api')
class ApiController {
@Role('admin') // This will be inherited by all methods
@Get('users')
String getUsers() {
// Direct metadata - attached to this specific method
List<Role> directRoles = context.meta.direct.get<Role>();
// Inherited metadata - from parent controller
List<Role> inheritedRoles = context.meta.inherited.get<Role>();
return 'Users data';
}
}
Real-World Examples#
1. Public Endpoint Marking#
Mark endpoints as public to bypass authentication:
class Public implements MetaData {
const Public();
}
@Controller('api')
class ApiController {
@Public()
@Get('health')
String healthCheck() {
return 'OK';
}
}
// In your auth guard
class AuthGuard implements Guard {
Future<GuardResult> protect(GuardContext context) async {
if (context.meta.has<Public>()) {
return const GuardResult.pass();
}
// Check authentication...
}
}
2. Route Configuration#
Store route-specific configuration:
class CodeName implements MetaData {
const CodeName(this.name);
final String name;
}
@Controller('api')
class ApiController {
@CodeName('API-1234')
@Get('data')
String getData(Meta meta) {
final codeName = meta.get<CodeName>()?.single;
return codeName?.name ?? 'No code name';
}
}
What's Next?#
- Learn about data sharing for storing runtime data
- Explore reflection for accessing metadata on types
- See lifecycle components for using metadata in guards and interceptors