JSON Schema Integration
Ack schemas can be automatically converted into JSON Schema objects, allowing you to generate API documentation directly from your validation schemas.
Generating JSON Schemas
Use the toJsonSchema()
extension method available on any AckSchema
instance (requires importing package:ack/json_schema.dart
).
import 'package:ack/ack.dart';
import 'package:ack/json_schema.dart'; // Required for toJsonSchema() extension
import 'dart:convert';
// Example User Schema
final userSchema = Ack.object({
'id': Ack.integer().positive().withDescription('Unique user identifier'),
'name': Ack.string().minLength(2).maxLength(50).withDescription('User\'s full name'),
'email': Ack.string().email().withDescription('User\'s email address'),
'role': Ack.enumString(['admin', 'user', 'guest']).withDefault('user'),
'isActive': Ack.boolean().withDefault(true),
'tags': Ack.list(Ack.string()).unique().withDescription('List of user tags').nullable(),
'age': Ack.integer().min(0).max(120).nullable().withDescription('User\'s age'),
}).withDescription('Represents a user in the system');
void main() {
// Convert the AckSchema to a JSON Schema Object Map
final jsonSchemaMap = userSchema.toJsonSchema();
// Pretty print the JSON representation of the JSON Schema
final jsonEncoder = JsonEncoder.withIndent(' ');
print(jsonEncoder.convert(jsonSchemaMap));
}
Output JSON (JSON Schema Object):
{
"type": "object",
"description": "Represents a user in the system",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"description": "Unique user identifier",
"minimum": 0,
"exclusiveMinimum": true
},
"name": {
"type": "string",
"description": "User\'s full name",
"minLength": 2,
"maxLength": 50
},
"email": {
"type": "string",
"format": "email",
"description": "User\'s email address"
},
"role": {
"type": "string",
"enum": [
"admin",
"user",
"guest"
],
"default": "user"
},
"isActive": {
"type": "boolean",
"default": true
},
"tags": {
"type": "array",
"description": "List of user tags",
"items": {
"type": "string"
},
"uniqueItems": true,
"nullable": true
},
"age": {
"type": "integer",
"format": "int64",
"description": "User\'s age",
"minimum": 0,
"maximum": 120,
"nullable": true
}
},
"required": [
"id",
"name",
"email"
]
}
How Constraints Map to JSON Schema
Ack attempts to map its built-in constraints to corresponding JSON Schema keywords:
Ack Constraint | JSON Schema Keyword | Notes |
---|---|---|
minLength(n) | minLength: n | String |
maxLength(n) | maxLength: n | String |
matches(p) | pattern: p | String |
email() | format: email | String |
date() | format: date | String |
datetime() | format: date-time | String |
time() | format: time | String |
uri() | format: uri | String |
uuid() | format: uuid | String |
ipv4() | format: ipv4 | String |
ipv6() | format: ipv6 | String |
hostname() | format: hostname | String (not currently implemented) |
enumString([...]) | enum: [...] | String |
min(n) | minimum: n | Number (Int/Double) |
max(n) | maximum: n | Number (Int/Double) |
min(n, exclusive:true) | minimum: n , exclusiveMinimum: true | Number (exclusive not currently supported) |
max(n, exclusive:true) | maximum: n , exclusiveMaximum: true | Number (exclusive not currently supported) |
multipleOf(n) | multipleOf: n | Number (Int/Double) |
minLength(n) | minItems: n | List (Array) |
maxLength(n) | maxItems: n | List (Array) |
unique() | uniqueItems: true | List (Array) |
nullable() | nullable: true | Any Type |
withDefault(v) | default: v | Any Type (Note: Use .withDefault() on schema) |
withDescription(d) | description: d | Any Type (Note: Use .withDescription() on schema) |
Ack.integer | type: integer , format: int64 | Type |
Ack.double | type: number , format: double | Type |
Ack.string | type: string | Type |
Ack.boolean | type: boolean | Type |
Ack.list(...) | type: array , items: {...} | Type |
Ack.object(...) | type: object , properties: {...} , required: [...] | Type |
Limitations:
- Custom Constraints:
SchemaConstraint
instances added via.constrain()
are not translated to JSON Schema as there's no standard way to represent arbitrary logic. additionalProperties
: The translation ofadditionalProperties
might need review depending on the exact JSON Schema behavior desired (e.g.,additionalProperties: true
vsadditionalProperties: {}
vsadditionalProperties: <Schema>
).
Integrating into API Documentation
You can use the generated JSON Schema map within a larger API documentation structure.
// Assume you have a function to build the full API spec
Map<String, dynamic> buildApiSpecification() {
final userJsonSchema = userSchema.toJsonSchema();
return {
'schemas': {
'User': userJsonSchema
},
'endpoints': {
'/users': {
'post': {
'summary': 'Create a new user',
'requestBody': {
'required': true,
'content': {
'application/json': {
// Reference the generated schema
'schema': {
'\$ref': '#/schemas/User'
}
}
}
}
}
}
}
};
}
// Usage
final fullApiSpec = buildApiSpecification();
print(JsonEncoder.withIndent(' ').convert(fullApiSpec));
This allows you to maintain your validation logic and API documentation source in one place (your Ack schemas).
Schema Descriptions and Metadata
Add descriptions and metadata to your schemas for better documentation:
final userSchema = Ack.object({
'id': Ack.string().uuid().withDescription('Unique user identifier'),
'name': Ack.string().minLength(1).withDescription('User\'s full name'),
'email': Ack.string().email().withDescription('User\'s email address'),
'age': Ack.integer().min(0).max(150).withDescription('User\'s age in years').optional(),
}).withDescription('Represents a user in the system');
final jsonSchema = userSchema.toJsonSchema();
// Generated schema will include description fields
Default Values in JSON Schema
Schemas with default values will include them in the generated JSON Schema:
final configSchema = Ack.object({
'theme': Ack.enumString(['light', 'dark']).withDefault('light'),
'notifications': Ack.boolean().withDefault(true),
'maxItems': Ack.integer().min(1).max(100).withDefault(10),
});
final jsonSchema = configSchema.toJsonSchema();
// Will include "default" properties in the JSON Schema
Complex Schema Patterns
JSON Schema generation works with all Ack schema types:
// Union types
final mixedValueSchema = Ack.anyOf([
Ack.string(),
Ack.integer(),
Ack.boolean(),
]);
// Discriminated unions
final shapeSchema = Ack.discriminated(
discriminatorKey: 'type',
schemas: {
'circle': Ack.object({
'type': Ack.literal('circle'),
'radius': Ack.double().positive(),
}),
'rectangle': Ack.object({
'type': Ack.literal('rectangle'),
'width': Ack.double().positive(),
'height': Ack.double().positive(),
}),
},
);
// Nested arrays and objects
final complexSchema = Ack.object({
'users': Ack.list(userSchema).minLength(1),
'metadata': Ack.object({
'version': Ack.string(),
'tags': Ack.list(Ack.string()).unique(),
}).optional(),
});
// All generate valid JSON Schema
final mixedJson = mixedValueSchema.toJsonSchema();
final shapeJson = shapeSchema.toJsonSchema();
final complexJson = complexSchema.toJsonSchema();