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.int.positive().description('Unique user identifier'),
  'name': Ack.string.minLength(2).maxLength(50).description('User\'s full name'),
  'email': Ack.string.isEmail().description('User\'s email address'),
  'role': Ack.string.isEnum(['admin', 'user', 'guest']).defaultValue('user'),
  'isActive': Ack.boolean.defaultValue(true),
  'tags': Ack.list(Ack.string).uniqueItems().description('List of user tags').nullable(),
  'age': Ack.int.min(0).max(120).nullable().description('User\'s age'),
}, 
required: ['id', 'name', 'email'],
description: '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 ConstraintJSON Schema KeywordNotes
minLength(n)minLength: nString
maxLength(n)maxLength: nString
pattern(p)pattern: pString
isEmail()format: emailString
isUrl()format: urlString (Note: format is informational)
isDate()format: dateString
isDateTime()format: date-timeString
isEnum([...])enum: [...]String
min(n)minimum: nNumber (Int/Double)
max(n)maximum: nNumber (Int/Double)
min(n, exclusive:true)minimum: n, exclusiveMinimum: trueNumber
max(n, exclusive:true)maximum: n, exclusiveMaximum: trueNumber
multipleOf(n)multipleOf: nNumber (Int/Double)
minItems(n)minItems: nList (Array)
maxItems(n)maxItems: nList (Array)
uniqueItems()uniqueItems: trueList (Array)
nullable()nullable: trueAny Type
defaultValue(v)default: vAny Type (Note: Use .defaultValue() on schema)
description(d)description: dAny Type (Note: Use .description() on schema)
Ack.inttype: integer, format: int64Type
Ack.doubletype: number, format: doubleType
Ack.stringtype: stringType
Ack.booleantype: booleanType
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.
  • Complex Formats: Some specific formats (like UUID, achieved via pattern) don't have direct JSON Schema format equivalents and are translated based on their base type (type: string). You might add a description or use vendor extensions in your schema definition manually if needed.
  • additionalProperties: The translation of additionalProperties might need review depending on the exact JSON Schema behavior desired (e.g., additionalProperties: true vs additionalProperties: {} vs additionalProperties: <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).