api_contract_generator

Code generator for api_contract. Automatically generates API contract definitions from annotated Dart model classes.

Features

  • Auto-generate contracts from Dart model classes using @ApiContractSchema annotation
  • Type inference - automatically detects field types from your models
  • Built-in type support - handles DateTime, Uri, Duration, BigInt, RegExp, and primitives
  • Nested models - automatically generates contracts for referenced model classes
  • Field annotations - fine-grained control with @optional and @nullable
  • Two generation modes:
    • fromModel - infers contracts from model structure (recommended)
    • fromJson - generates from JSON schema with callback
    • fromJsonSchema - generates from JSON schema map

Installation

Add both packages to your pubspec.yaml:

dependencies:
  api_contract: ^0.2.0

dev_dependencies:
  api_contract_generator: ^0.2.0
  build_runner: ^2.4.0

Quick Start

1. Annotate Your Model

import 'package:api_contract/api_contract.dart';
import 'package:api_contract_generator/api_contract_generator.dart';

part 'user.g.dart';

@ApiContractSchema(mode: ContractMode.strict)
class User {
  final int id;
  final String name;

  @optional
  final String? email;

  final Profile profile;
}

@ApiContractSchema()
class Profile {
  final String bio;
  final DateTime createdAt;
}

2. Generate Contract

dart run build_runner build

3. Use in Your Repository

class UserRepository {
  Future<User> getUser(int id) async {
    final response = await api.get('/users/$id');

    // Validate response against generated contract
    final result = userContract.validate(response);
    result.throwIfInvalid();

    return User.fromJson(response);
  }
}

Annotations

Class-level: @ApiContractSchema

@ApiContractSchema(
  mode: ContractMode.strict,  // or ContractMode.lenient
  version: '1.0',
  model: UserModel,  // optional: for fromJson/fromJsonSchema modes
)

Parameters:

  • mode - Contract validation mode (default: ContractMode.lenient)
    • strict - Fails on extra fields in API response
    • lenient - Allows extra fields in API response
  • version - API version string (optional)
  • model - Model class for type inference in fromJson mode (optional)

Field-level Annotations

@optional  // Field can be missing from response
final String? bio;

@nullable  // Field must be present but can be null
final String? middleName;

Difference:

  • @optional - Field may not exist in JSON ({"name": "John"} - no bio field)
  • @nullable - Field exists but value is null ({"name": "John", "bio": null})

Generation Modes

Auto-detect from model structure:

@ApiContractSchema(mode: ContractMode.strict)
class Post {
  final int id;           // → required number
  final String title;     // → required string

  @optional
  final String? body;     // → optional string

  final List<String> tags; // → required list
}

Mode 2: fromJson

Generate from JSON with callback:

@ApiContractSchema(
  mode: ContractMode.strict,
  model: Post,
)
void generatePostContract(JsonContractGenerator gen) {
  gen.fromJson({
    'id': 1,
    'title': 'Sample Post',
    'tags': ['dart', 'flutter'],
  });
}

Mode 3: fromJsonSchema

Generate from schema map:

@ApiContractSchema(mode: ContractMode.strict)
void generatePostContract(JsonSchemaContractGenerator gen) {
  gen.fromJsonSchema({
    'id': {'type': 'number', 'required': true},
    'title': {'type': 'string', 'required': true},
    'body': {'type': 'string', 'optional': true},
  });
}

Supported Types

Dart Type JSON Type Generated As
int, double, num number FieldType.number
String string FieldType.string
bool boolean FieldType.boolean
List<T> array FieldType.list
Map<K,V> object FieldType.map
DateTime string (ISO 8601) FieldType.string
Uri string (URL) FieldType.string
Duration number (ms) FieldType.number
BigInt number FieldType.number
RegExp string (pattern) FieldType.string
Custom classes object FieldType.nested

Type Inference Rules

When using fromModel mode:

  1. Non-nullable fieldsrequired

    final String name;  // → ContractField.required(type: FieldType.string)
    
  2. Nullable fieldsoptional (unless @nullable specified)

    final String? email;  // → ContractField.optional(type: FieldType.string)
    
    @nullable
    final String? middleName;  // → ContractField.nullable(type: FieldType.string)
    
  3. Custom classesnested contract

    final Profile profile;  // → ContractField.nested(nestedContract: profileContract)
    
  4. Nullable custom classesoptional nested

    final Profile? profile;  // → ContractField.optional(nestedContract: profileContract)
    

Generated Output

The generator creates a .g.dart file with contract definitions:

// user.g.dart
part of 'user.dart';

final userContract = ApiContract(
  mode: ContractMode.strict,
  fields: {
    'id': ContractField.required(type: FieldType.number),
    'name': ContractField.required(type: FieldType.string),
    'email': ContractField.optional(type: FieldType.string),
    'profile': ContractField.nested(nestedContract: profileContract),
  },
);

final profileContract = ApiContract(
  fields: {
    'bio': ContractField.required(type: FieldType.string),
    'createdAt': ContractField.required(type: FieldType.string),
  },
);

Build Configuration

Add build.yaml to your project root (optional):

targets:
  $default:
    builders:
      api_contract_generator:contract:
        enabled: true

Examples

See the api_contract package for complete usage examples with repository pattern integration.

License

MIT License - see LICENSE file for details

Libraries

api_contract_generator
Annotation-based code generator for api_contract.