zmodel_to_dart_builder 1.1.1 copy "zmodel_to_dart_builder: ^1.1.1" to clipboard
zmodel_to_dart_builder: ^1.1.1 copied to clipboard

A Dart builder to generate DTO classes from ZModel schema files

zmodel_to_dart_builder #

A Dart builder that generates DTO classes from .zmodel files.

For an overview of the parser, renderer, CLI flow, and RPC generation internals, see the repository architecture guide.

This package is the generator side of the split. Generated code depends on zmodel_to_dart_runtime; the original zmodel_to_dart pub package name is left for the legacy 0.x package.

Features #

  • Parses model, abstract model, and enum declarations
  • Generates DTO classes with fromJson
  • Generates static model metadata for dynamic selects, filters, includes, omits, and ordering
  • Generates local validators from schema metadata
  • Generates sparse mutation payload classes with toJson and serialization metadata
  • Supports inherited fields from extends
  • Handles scalar values, enums, bytes, arrays, and nested model references
  • Optionally generates schema-scoped RPC clients backed by runtime payload contracts
  • Automatically unwraps superjson RPC responses before model parsing

Usage #

  1. Add to your pubspec.yaml:
dependencies:
    zmodel_to_dart_runtime: ^1.1.0

dev_dependencies:
    zmodel_to_dart_builder: ^1.1.0
    build_runner: ^2.7.0
  1. Create a .zmodel file:
enum role {
    admin
    reader
}

model user {
    id String @id
    email String @email
    name String @db.VarChar(80) @length(2, 80)
    age Int @gte(18)
    role role
}
  1. Optionally create zmodel_to_dart.yaml in the package root:
input_globs:
  - schema/*.zmodel
output_suffix: .zmodel.dart
output_dir: lib/data/dtos
banner: |
  // AUTO GENERATED FILE, DO NOT EDIT
  // ignore_for_file: type=lint
  // cspell: disable
generate_rpc_clients: true
rpc_base_path: /api/model
rpc_client_class_name: AppRpcClient
  1. Run:
dart run build_runner build

This generates a sibling file like user.zmodel.dart.

The generated library imports the runtime package. DTOs extend ZModel, and payload builders extend runtime contracts such as ZModelCreate, ZModelWhere, ZModelSelect, ZModelInclude, ZModelOrderBy, and ZModelDistinct.

Generated models expose static metadata for dynamic query construction:

final select = UserSelect().setEntries([
  User.metadata.id.select(),
  User.metadata.email.select(),
]);

final where = UserWhere().setEntries([
  User.metadata.email.contains('example.com'),
]);

final orderBy = UserOrderBy([
  User.metadata.createdAt.desc(),
  User.metadata.id.asc(),
]);

Generated models also expose local validators:

final result = User.validators.validateMap({
  'id': 'user_1',
  'name': null,
  'role': 'admin',
});

TextFormField(
  validator: User.validators.name.required(),
);

Validation errors are structured and can be translated globally by the app:

ZValidationMessages.resolver = (error) {
  return switch (error.code) {
    ZValidationErrorCode.required => 'Campo obrigatório',
    ZValidationErrorCode.invalidType => 'Valor inválido',
    ZValidationErrorCode.minLength => 'Valor muito curto',
    ZValidationErrorCode.maxLength =>
      "Use no máximo ${error.metadata['maxLength']} caracteres",
    ZValidationErrorCode.email => 'E-mail inválido',
    _ => 'Valor inválido',
  };
};

Generated local validators support ZenStack input validation attributes such as @length, @startsWith, @endsWith, @contains, @email, @url, @datetime, @regex, @lt, @lte, @gt, and @gte. Constraints that require database state, like @unique, remain metadata for app-managed checks.

Use validator composition for form-specific rules without changing the .zmodel schema:

final formValidator = User.validators.model.withFields({
  'name': User.validators.name.required(),
});

When generate_rpc_clients is enabled, the generated library also contains a schema-scoped RPC client extending ZRpcClient. Each concrete model is exposed as a ZModelRpcApi<[Model]> getter, and operation data is passed through named parameters with generated payload builders:

final client = AppRpcClient(transport);

final users = await client.user.findMany(
  where: UserWhere().id('user_1'),
  orderBy: UserOrderBy().id.order(false),
  take: 20,
);

final created = await client.user.create(
  data: UserCreate().name('New user'),
);

final updated = await client.user.update(
  data: UserUpdate().name('Updated name').role(null),
  where: UserWhereUnique().id('user_1'),
);

Each concrete model gets generated create and update payload classes. A field method that is not called is omitted from the request body, a field method called with null sends JSON null, and a field method called with a value sends that value. For dynamic field names, use set(name, value). Payloads keep raw Dart values until RPC serialization, so DateTime, BigInt, Uint8List, enums, nested payloads, lists, and maps are encoded with ZenStack-compatible meta.serialization.

Relation fields expose nested payload helpers:

final usersWithPosts = await client.user.findMany(
  include: UserInclude().postsWith(
    select: PostSelect().id().title(),
    where: PostWhere().published(true),
  ),
);

final where = UserWhere().posts.some(
  PostWhere().published(true),
);

Example using Dio:

import 'package:dio/dio.dart';
import 'package:zmodel_to_dart_runtime/zmodel_to_dart_runtime.dart';

class DioZRpcTransport implements ZRpcTransport {
  DioZRpcTransport(this._dio);

  final Dio _dio;

  @override
  Future<ZHttpResponse> send(
    ZHttpMethod method,
    String path, {
    Map<String, String>? queryParameters,
    Object? body,
  }) async {
    late final Response<dynamic> response;

    switch (method) {
      case ZHttpMethod.get:
        response = await _dio.get<dynamic>(
          path,
          queryParameters: queryParameters,
        );
        break;
      case ZHttpMethod.patch:
        response = await _dio.patch<dynamic>(
          path,
          queryParameters: queryParameters,
          data: body,
        );
        break;
      case ZHttpMethod.delete:
        response = await _dio.delete<dynamic>(
          path,
          queryParameters: queryParameters,
          data: body,
        );
        break;
      case ZHttpMethod.post:
        response = await _dio.post<dynamic>(
          path,
          queryParameters: queryParameters,
          data: body,
        );
        break;
      case ZHttpMethod.put:
        response = await _dio.put<dynamic>(
          path,
          queryParameters: queryParameters,
          data: body,
        );
        break;
    }

    return ZHttpResponse(
      statusCode: response.statusCode ?? 0,
      statusMessage: response.statusMessage,
      headers: response.headers.map,
      data: response.data,
    );
  }
}

final dio = Dio(BaseOptions(baseUrl: 'http://localhost:3000'));
final transport = DioZRpcTransport(dio);
final client = AppRpcClient(transport);

final users = await client.user.findMany(
  where: UserWhere().id('user_1'),
  take: 20,
);

The builder registration is shipped by this package. Consumers usually only need zmodel_to_dart.yaml for input filtering, output naming, banners, and RPC options.

Suggestions #

  • Keep your .zmodel files close to the modules that consume the generated DTOs.
  • Use the builder when you want generated files to stay synchronized automatically.

To run standalone #

dart run zmodel_to_dart_builder:zmodel_to_dart <path_to_zmodel_file> [output_dir]

If you omit the CLI arguments, the command will try to resolve a single source file from input_globs in zmodel_to_dart.yaml and will use output_dir from that file.

License #

MIT

0
likes
160
points
287
downloads

Documentation

API reference

Publisher

verified publisheraquarino.com.br

Weekly Downloads

A Dart builder to generate DTO classes from ZModel schema files

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

build, glob, path, yaml

More

Packages that depend on zmodel_to_dart_builder