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

 
 
 

Contributors