Flutter GraphQL Codegen

pub package license

A code generator for Flutter GraphQL applications that generates type-safe Dart classes and client extensions from GraphQL schemas and operations.

✨ Features

  • 🎯 Type-safe code generation from GraphQL schemas and operations
  • πŸ”§ Automatic Dart class generation with proper JSON serialization
  • πŸš€ GraphQL client extensions for queries, mutations, and subscriptions
  • πŸ›‘οΈ Null safety support with graceful error handling
  • πŸ“¦ Custom scalar support (DateTime, Decimal, Long, Byte, etc.)
  • πŸ”„ Safe type converters with fallback values
  • πŸ“‹ Support for complex GraphQL features:
    • Interfaces and unions
    • Enums with custom values
    • Input types and arguments
    • Fragments and inline fragments
    • Custom directives
    • Nested and recursive types
  • 🌐 Multiple schema sources: local files, HTTP/HTTPS URLs, file URLs
  • πŸ§ͺ Comprehensive test coverage with robust error handling

πŸš€ Getting Started

Installation

Add this package to your pubspec.yaml:

dependencies:
  flutter_graphql_codegen: ^1.0.0
  graphql: ^5.1.3
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.6
  json_serializable: ^6.7.1

Configuration

  1. Create a build.yaml file in your project root:
targets:
  $default:
    builders:
      flutter_graphql_codegen:
        enabled: true
        options:
          config_path: "graphql_codegen.yaml"
  1. Create a graphql_codegen.yaml configuration file:
# Schema source - supports multiple formats
schema_url: "lib/graphql/schema.graphql"  # Local file path
# schema_url: "https://api.example.com/graphql"  # HTTP/HTTPS URL
# schema_url: "file:///absolute/path/to/schema.graphql"  # File URL

output_dir: "lib/graphql/generated"
document_paths:
  - "lib/graphql/documents/**/*.graphql"

Schema Source Options

The schema_url field supports multiple source types:

🏠 Local Files:

schema_url: "schema.graphql"                    # Relative path
schema_url: "lib/graphql/schema.graphql"        # Relative path with directories
schema_url: "/absolute/path/to/schema.graphql"  # Absolute path

πŸ”— File URLs:

schema_url: "file:///absolute/path/to/schema.graphql"

🌐 HTTP/HTTPS URLs:

schema_url: "https://api.example.com/graphql"      # GraphQL endpoint
schema_url: "http://localhost:4000/graphql/sdl"    # Direct SDL endpoint

Alternative Configuration Format

You can also use alternative field names for compatibility:

schema: "lib/graphql/schema.graphql"  # Instead of schema_url
documents:                            # Instead of document_paths
  - "lib/graphql/documents/**/*.graphql"

πŸ“– Usage

Basic Example

  1. Define your GraphQL operations in .graphql files:
# lib/graphql/queries/get_user.graphql
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
    isActive
    createdAt
  }
}
  1. Run the code generator:
# First run: Generates GraphQL operation files and types.dart
dart run build_runner build --delete-conflicting-outputs

# Second run: Generates JSON serialization code (types.g.dart)
dart run build_runner build --delete-conflicting-outputs

Important: You need to run the build command twice:

  • First run generates GraphQL types and operations
  • Second run generates JSON serialization code for the types

This is because the JSON serialization generator (json_serializable) needs the types.dart file to exist before it can generate the corresponding .g.dart files.

  1. Use the generated code in your Flutter app:
import 'package:graphql/client.dart';
import 'lib/graphql/generated/types.dart';
import 'lib/graphql/generated/get_user.dart';

// Initialize GraphQL client
final client = GraphQLClient(
  link: HttpLink('https://your-graphql-endpoint.com/graphql'),
  cache: GraphQLCache(),
);

// Use the generated extension method
final result = await client.getUserQuery(
  variables: GetUserArguments(id: 'user-123'),
);

if (result.hasException) {
  print('Error: ${result.exception}');
} else {
  final user = result.parsedData?.user;
  print('User: ${user?.name} (${user?.email})');
  print('Created: ${user?.createdAt}');
}

Advanced Features

Custom Scalars

The generator automatically handles custom scalars with safe converters:

// Generated safe converters with fallback values
@DateTimeConverter()
DateTime? createdAt;

@SafeIntConverter()
int count;

@SafeBoolConverter()
bool isActive;

@DecimalConverter()
Decimal? price;

Complex Types and Operations

# Schema types
type User {
  id: ID!
  profile: UserProfile
  posts: [Post!]!
  metadata: JSON
  role: UserRole!
}

input CreateUserInput {
  name: String!
  email: String!
  role: UserRole = USER
}

enum UserRole {
  ADMIN
  MODERATOR
  USER
}

# Mutation example
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    email
    role
  }
}

# Subscription example
subscription UserUpdated($id: ID!) {
  userUpdated(id: $id) {
    id
    name
    isActive
  }
}

Generated Extensions Usage

// Mutation with input validation
final createResult = await client.createUserMutation(
  variables: CreateUserArguments(
    input: CreateUserInput(
      name: 'John Doe',
      email: 'john@example.com',
      role: UserRole.moderator,
    ),
  ),
);

// Subscription with real-time updates
final subscription = client.userUpdatedSubscription(
  variables: UserUpdatedArguments(id: 'user-123'),
);

await for (final result in subscription.stream) {
  if (result.data != null) {
    final user = result.parsedData?.userUpdated;
    print('User updated: ${user?.name}');
  }
}

Error Handling

The generator includes robust error handling:

// Safe converters handle malformed data gracefully
@SafeBoolConverter()
bool isActive; // Defaults to false if null or invalid

@SafeIntConverter()
int count; // Defaults to 0 if null or invalid

// Custom error handling
final result = await client.getUserQuery(
  variables: GetUserArguments(id: 'invalid-id'),
);

if (result.hasException) {
  // Handle GraphQL errors
  final graphQLErrors = result.exception?.graphqlErrors;
  final networkError = result.exception?.linkException;
  
  print('GraphQL Errors: $graphQLErrors');
  print('Network Error: $networkError');
} else {
  // Safe to access data
  final user = result.parsedData?.user;
}

βš™οΈ Configuration Options

Option Description Default Required
schema_url GraphQL schema source (URL or file path) - βœ…
output_dir Output directory for generated files lib/generated ❌
document_paths Glob patterns for GraphQL operation files **/*.graphql ❌

πŸ“š API Reference

Generated Types

All GraphQL types are generated as Dart classes with:

  • βœ… JSON serialization support (fromJson/toJson)
  • βœ… Null safety with safe defaults
  • βœ… Proper field typing based on GraphQL schema
  • βœ… Custom scalar handling with type converters

Generated Extensions

For each operation, the generator creates client extensions:

  • Queries: client.{operationName}Query(variables?)
  • Mutations: client.{operationName}Mutation(variables?)
  • Subscriptions: client.{operationName}Subscription(variables?)

Type Safety

// Generated with full type safety
class GetUserArguments {
  final String id;
  
  GetUserArguments({required this.id});
}

class User {
  final String id;
  final String? name;
  final String? email;
  @SafeBoolConverter()
  final bool isActive;
  
  User({
    required this.id,
    this.name,
    this.email,
    required this.isActive,
  });
}

πŸ”§ Troubleshooting

Common Issues

"Target of URI hasn't been generated" Error

If you see errors like:

Target of URI hasn't been generated: 'package:your_app/graphql/generated/types.g.dart'
The method '_$UserFromJson' isn't defined for the type 'User'

Solution: Run the build command twice as described above. The first run generates the GraphQL types, and the second run generates the JSON serialization code.

Missing JSON Serialization Methods

If generated classes are missing fromJson/toJson methods:

  1. Ensure you have the required dependencies:
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.6
  json_serializable: ^6.7.1
  1. Run build_runner twice:
dart run build_runner build --delete-conflicting-outputs
dart run build_runner build --delete-conflicting-outputs

Build Runner Conflicts

If you encounter build conflicts, use the clean option:

dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs

Schema Download Issues

For HTTP/HTTPS schema URLs, ensure:

  • The endpoint is accessible
  • Authentication headers are set if required
  • The URL returns valid GraphQL schema (SDL format)

Complex Type Generation Issues

If complex types aren't generating correctly:

  • Check that your GraphQL schema is valid
  • Ensure operation files have the correct extension (.graphql)
  • Verify the document_paths configuration matches your file structure

πŸ§ͺ Testing

The package includes comprehensive test coverage:

# Run all tests
dart test

# Run with coverage
dart test --coverage=coverage
dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib

🀝 Contributing

Contributions are welcome! Please see our Contributing Guidelines for details.

Development Setup

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/ifmelate/flutter_graphql_codegen.git
  3. Install dependencies: dart pub get
  4. Run tests: dart test
  5. Make your changes
  6. Submit a pull request

πŸ”„ Changelog

See CHANGELOG.md for a list of changes and releases.

πŸ“„ License

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

Libraries

flutter_graphql_codegen
Flutter GraphQL Code Generator