Data Transfer Object (DTO)
Vaden uses the concept of DTOs (Data Transfer Objects) to safely transport data between client and server. DTOs are tightly integrated with the serialization system known as DSON, which handles converting objects to and from JSON.
@DTO()
Annotation
To define a DTO in Vaden, annotate the class with @DTO(). This marks the class for code generation so that Vaden can handle:
- Automatic fromJson and toJson conversion.
- Input validation (via Validator mixin).
- Schema generation for OpenAPI.
()
class Credentials with Validator<Credentials> {
final String username;
final String password;
const Credentials(this.username, this.password);
}
Additionally, you can return a DTO directly from a controller method instead of a Response. Vaden will automatically serialize it to JSON:
('/me')
UserProfile getProfile() => UserProfile('Alice');
This makes the controller cleaner while still returning a proper application/json response.
Json Key
If you want to use a different key in the JSON than the field name, you can use the @JsonKey()
annotation:
()
class Credentials with Validator<Credentials> {
('user_name')
String username;
String password;
Credentials(this.username, this.password);
}
Validation
DTOs can implement the Validator
mixin to perform validation on the data. This is useful for ensuring that the data meets certain criteria before it is processed. If validation fails, Vaden
will return a 400 Bad Request response with the validation errors.
()
class Credentials with Validator<Credentials> {
final String username;
final String password;
const Credentials(this.username, this.password);
LucidValidator<Credentials> validate(ValidatorBuilder<Credentials> builder) {
return builder
.ruleFor((c) => c.username, key: 'username').notEmpty()
.ruleFor((c) => c.password, key: 'password').minLength(6);
}
}
Complex Objects parse
DTOs can also contain other DTOs. Vaden will automatically handle the serialization and deserialization of nested objects,
but you can also use @UseParse()
to specify a custom parser for a field. This is useful for complex
types or when you want to control the serialization format:
()
class UserDocument {
final String title;
final String description;
// Custom parser for File - converts to base64 string
(FileParse)
final File? attachment;
const UserDocument(this.title, this.description, this.attachment);
}
// Custom parser for File type
class FileParse extends ParamParse<File?, String?> {
const FileParse();
String? toJson(File? param) {
if (param == null) return null;
// Convert file to base64 string for JSON transport
final bytes = param.readAsBytesSync();
return base64Encode(bytes);
}
File? fromJson(String? json) {
if (json == null) return null;
// Create temporary file from base64 string
final bytes = base64Decode(json);
final tempFile = File('temp_${DateTime.now().millisecondsSinceEpoch}');
tempFile.writeAsBytesSync(bytes);
return tempFile;
}
}
Built-in Complex Types
Vaden automatically handles serialization and deserialization for several complex types without requiring @UseParse()
:
- DateTime: Automatically converted to/from ISO 8601 strings
- Enums: Automatically serialized using enum name (e.g.,
UserType.admin
→"admin"
) - Duration: Converted to/from milliseconds
- Uri: Converted to/from string representation
()
class Event {
final String name;
final DateTime startTime; // Automatic DateTime handling
final Duration duration; // Automatic Duration handling
final EventType type; // Automatic enum handling
final Uri? website; // Automatic Uri handling
const Event(this.name, this.startTime, this.duration, this.type, this.website);
}
enum EventType { conference, workshop, meetup }
You can still use @UseParse()
to override the default behavior if you need custom serialization for these types.
Deserialization with @Body()
When a controller method uses @Body()
, Vaden will:
- Read the request JSON body.
- Look up the corresponding
@DTO()
. - Deserialize the data using DSON.
- Optionally validate the object before passing it to your method.
('/login')
String login(() Credentials credentials) {
return Response.ok(credentials.username);
}
Only @DTO()
classes are allowed in @Body()
parameters.
DSON
DSON is Vaden’s automatic serializer. It is responsible for:
- Converting DTOs from JSON (
fromJson<T>()
). - Converting objects to JSON (
toJson<T>()
). - Generating OpenAPI schemas.
You can inject it anywhere:
()
class DebugService {
final DSON dson;
DebugController(this.dson);
String serializeCredentials( Map<String, dynamic> body) {
final dto = dson.fromJson<Credentials>(body);
return Response.ok(dson.toJson(dto));
}
}
Benefits
- No need to write boilerplate serializers.
- Clean error handling via validation.
- Fully typed and OpenAPI-ready.
By using @DTO()
and DSON together, Vaden ensures reliable and clean data exchange between client and server.