Skip to content

moinsen-dev/appwrite_generator

Repository files navigation

Appwrite Generator

coverage style: very good analysis License: MIT

A powerful CLI tool that generates type-safe, production-ready Dart and Python models from Appwrite configuration JSON files. Streamline your Appwrite development workflow by automatically generating models, enums, and attribute constants directly from your database schema.

Features ✨

Multi-Language Support

  • 🐍 Python/Pydantic Models: Generate production-ready Pydantic v2 models
  • 🎯 Dart Models: Generate type-safe Dart classes with full null safety
  • πŸ”€ Generate Both: Create Dart and Python models simultaneously

Code Generation Features

  • πŸ”„ Complete Serialization: Automatic JSON serialization/deserialization
  • πŸ“ Enum Support: Creates proper enums from enum-type columns
  • πŸ”‘ Attribute Constants: Type-safe property access with generated constants
  • βœ… Schema Validation: Respects required, max_length, min/max constraints
  • πŸ”— Relationship Support: Handles one-to-one, one-to-many, many-to-one, and many-to-many relationships
  • πŸ“¦ Well-Organized Output: Models organized by collection with clear structure
  • ⚑ Fast & Efficient: Quickly generates all models with a single command

Advanced Features (v0.2.0+)

  • πŸ†” Appwrite Metadata: Include $id, $createdAt, $updatedAt and more
  • βš–οΈ Equatable Support (Dart): Optional Equatable mixin for easy comparisons
  • βœ”οΈ Validation Helpers: Schema-based validation methods and decorators
  • πŸ‘€ Watch Mode: Auto-regenerate on file changes
  • 🎨 Auto-Formatting: Format Dart (dart format) and Python (black) automatically

Getting Started πŸš€

Installation

From Source (Currently Available)

Clone the repository and activate locally:

git clone https://github.com/moinsen-dev/appwrite_generator.git
cd appwrite_generator
dart pub global activate --source=path .

From Pub (Coming Soon)

Once published on pub.dev:

dart pub global activate appwrite_generator

Prerequisites

  • Dart SDK 3.9.0 or higher
  • An appwrite.json configuration file from your Appwrite project

Usage

Basic Commands

# Generate Dart models from appwrite.json (default)
$ appwrite_generator generate

# Generate Python/Pydantic models
$ appwrite_generator generate --config appwrite_generator.yaml  # with target: python

# Generate both Dart and Python models
$ appwrite_generator generate --config appwrite_generator.yaml  # with target: both

# Specify custom input and output paths
$ appwrite_generator generate --input my_config.json --output lib/generated

# Watch for changes and auto-regenerate (v0.2.0)
$ appwrite_generator watch

# Show CLI version
$ appwrite_generator --version

# Show usage help
$ appwrite_generator --help

# Update the CLI to the latest version
$ appwrite_generator update

Configuration File

Create an appwrite_generator.yaml file to configure multi-language generation:

# Target language: dart, python, or both
target: both

# Input Appwrite configuration
input: appwrite.json

# Dart output directory
output: lib/models

# Python configuration
python:
  output: python/models
  pydantic_version: v2

# Features
features:
  validation: true
  metadata_fields: true
  equatable: false  # Dart only

What Gets Generated

From your appwrite.json configuration, the generator creates:

Dart Output

  • Type-safe model classes with fromJson, toJson, and copyWith methods
  • Enums for all enum-type columns
  • Attribute constants for type-safe property access
  • Null-safe code based on the required flag in your schema

Python Output

  • Pydantic BaseModel classes with automatic validation
  • Type hints using Python's typing module
  • String-based Enums for enum-type columns
  • Field validators using @field_validator decorators
  • Field constants for type-safe property access

Example: Dart Generation

Given this appwrite.json:

{
  "tables": [{
    "$id": "users",
    "name": "Users",
    "columns": [
      {
        "key": "handle",
        "type": "string",
        "required": true,
        "size": 20
      },
      {
        "key": "age_band",
        "type": "string",
        "required": false,
        "elements": ["18-24", "25-34", "35-44", "45+"],
        "format": "enum"
      }
    ]
  }]
}

The generator creates:

// lib/models/users/user.dart
class User {
  const User({required this.handle, this.ageBand});

  final String handle;
  final AgeBand? ageBand;

  factory User.fromJson(Map<String, dynamic> json) { ... }
  Map<String, dynamic> toJson() { ... }
  User copyWith({String? handle, AgeBand? ageBand}) { ... }
}

// lib/models/users/user_enums.dart
enum AgeBand {
  value18to24('18-24'),
  value25to34('25-34'),
  value35to44('35-44'),
  value45Plus('45+');

  final String value;
  const AgeBand(this.value);
  factory AgeBand.fromString(String value) { ... }
}

// lib/models/users/user_attributes.dart
class UserAttributes {
  static const String handle = 'handle';
  static const String ageBand = 'age_band';
}

Example: Python/Pydantic Generation

The same schema generates Python/Pydantic models:

# python/models/users/user.py
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field, ConfigDict, field_validator
from .user_enums import AgeBand

class User(BaseModel):
    """Users model from Appwrite"""

    # Appwrite metadata fields
    id: Optional[str] = Field(None, alias=r'$id', description='Appwrite document ID')
    created_at: Optional[datetime] = Field(None, alias=r'$createdAt')

    # Schema fields
    handle: str = Field(alias='handle', description='Column: handle', max_length=20)
    age_band: Optional[AgeBand] = Field(None, alias='age_band', description='Column: age_band')

    # Pydantic configuration
    model_config = ConfigDict(
        populate_by_name=True,
        use_enum_values=True,
        validate_assignment=True,
    )

    @field_validator("handle")
    @classmethod
    def validate_handle_length(cls, v: str) -> str:
        """Validate handle length"""
        if v and len(v) > 20:
            raise ValueError(f"handle must be 20 characters or less, got {len(v)}")
        return v

# python/models/users/user_enums.py
from enum import Enum

class AgeBand(str, Enum):
    """AgeBand enum from Appwrite"""

    value_18_24 = '18-24'
    value_25_34 = '25-34'
    value_35_44 = '35-44'
    value_45_plus = '45+'

    @classmethod
    def from_string(cls, value: str) -> "AgeBand":
        """Create enum from string value"""
        try:
            return cls(value)
        except ValueError:
            raise ValueError(f'Invalid AgeBand: {value}')

# python/models/users/user_fields.py
class UserFields:
    """Field name constants for Users"""

    HANDLE = 'handle'
    AGE_BAND = 'age_band'

v0.2.0 Features

Appwrite Metadata Fields

Include Appwrite document metadata in your models:

# appwrite_generator.yaml
features:
  metadata_fields: true  # enabled by default

Generated models will include:

class User {
  final String? id;           // $id
  final DateTime? createdAt;  // $createdAt
  final DateTime? updatedAt;  // $updatedAt
  final List<String>? permissions;  // $permissions
  final String? collectionId;  // $collectionId
  final String? databaseId;    // $databaseId

  // ... your columns
}

Equatable Support

Enable for easy equality comparisons (great for state management):

features:
  equatable: true  # disabled by default
class User extends Equatable {
  // ... fields

  @override
  List<Object?> get props => [id, handle, ageBand, ...];
}

// Now you can compare instances easily
final user1 = User(handle: 'john');
final user2 = User(handle: 'john');
print(user1 == user2);  // true!

Validation Helpers

Generate schema-based validation methods:

features:
  validation: true  // disabled by default

Generated validation class:

// lib/models/users/user_validation.dart
class UserValidation {
  static String? validateHandle(String value) {
    if (value.isEmpty) return 'Handle is required';
    if (value.length > 20) return 'Handle must be 20 characters or less';
    return null;
  }

  static String? validateEmail(String? value) {
    if (value != null && !RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
      return 'Invalid email format';
    }
    return null;
  }
}

Watch Mode

Auto-regenerate models during development:

$ appwrite_generator watch
πŸ‘€ Watching appwrite.json for changes...
Press Ctrl+C to stop watching

πŸ“ Change detected in appwrite.json
πŸ”„ Regenerating models...
βœ… Models regenerated successfully

Supported Attribute Types

The generator supports all Appwrite attribute types:

Appwrite Type Dart Type Notes
string String Includes support for enum format
integer int With min/max validation support
double double Floating-point numbers
boolean bool True/false values
datetime DateTime ISO 8601 format
email String Email validation in schema
ip String IP address validation
url String URL validation
enum Custom Enum Generates dedicated enum class
relationship N/A Documented but not directly generated

Real-World Example

Check out the example directory for a complete Flutter application with a comprehensive to-do list schema featuring:

  • 5 interconnected tables: Projects, Todos, Tags, TodoTags (junction table), and Comments
  • Multiple enum types: Project status, todo priority, todo status
  • Various attribute types: strings, integers, datetimes, enums
  • Relationships: One-to-many and many-to-many patterns
  • Complete Flutter app: Working example showing generated models in action

Development πŸ› οΈ

Running Tests with Coverage

# Activate coverage tool
$ dart pub global activate coverage 1.15.0

# Run tests with coverage
$ dart test --coverage=coverage

# Format coverage data
$ dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info

# Generate HTML coverage report (requires lcov)
$ genhtml coverage/lcov.info -o coverage/

# Open coverage report
$ open coverage/index.html

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Roadmap πŸ—ΊοΈ

  • Publish to pub.dev
  • Add support for custom templates
  • Generate repository/service layers
  • Add validation helpers
  • Support for custom attribute types
  • Generate API documentation
  • Migration generator for schema changes

License

This project is licensed under the MIT License - see the LICENSE file for details.

Copyright (c) 2025 Moinsen Development

Acknowledgments


About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •