{"id":3402,"library":"ariadne-codegen","title":"Ariadne Codegen","description":"Ariadne Codegen is a Python code generator that creates a fully typed and asynchronous GraphQL client from a GraphQL schema, queries, mutations, and subscriptions. It leverages Pydantic for data models, ensuring type safety and robust data validation. The library is currently at version 0.18.0 and follows a custom versioning scheme where minor version increments signify breaking changes until it reaches a stable 1.0.0 release. It is actively maintained with regular updates and new features.","status":"active","version":"0.18.0","language":"en","source_language":"en","source_url":"https://github.com/mirumee/ariadne-codegen","tags":["GraphQL","code-generation","client","pydantic","typed","async","schema-first"],"install":[{"cmd":"pip install ariadne-codegen","lang":"bash","label":"Core library"},{"cmd":"pip install ariadne-codegen[subscriptions]","lang":"bash","label":"With subscription support"}],"dependencies":[{"reason":"Generated client models are based on Pydantic for type safety and data validation. Version >=2.0.0,<3.0.0 is supported since 0.8.","package":"pydantic","optional":false},{"reason":"Required by the default base client for GraphQL subscriptions.","package":"websockets","optional":true}],"imports":[{"note":"Assumes 'graphql_client' as the default generated package name. This can be customized via `target_package_name` in `pyproject.toml`.","symbol":"Client","correct":"from graphql_client.client import Client"}],"quickstart":{"code":"import asyncio\nimport os\nfrom pathlib import Path\n\n# --- Setup: Create dummy schema and queries files ---\n# In a real scenario, these files would exist in your project.\n\nschema_content = '''\nschema { query: Query mutation: Mutation subscription: Subscription }\n\ntype Query {\n  users(country: String): [User!]!\n  hello: String!\n}\n\ntype Mutation {\n  createUser(name: String!, email: String!): User!\n}\n\ntype Subscription {\n  randomNumber: Int!\n}\n\ntype User {\n  id: ID!\n  name: String!\n  email: String!\n  country: String\n}\n'''\n\nqueries_content = '''\nquery ListAllUsers {\n  users {\n    id\n    name\n    email\n    country\n  }\n}\n\nmutation CreateNewUser($name: String!, $email: String!) {\n  createUser(name: $name, email: $email) {\n    id\n    name\n    email\n  }\n}\n'''\n\n# Create a temporary directory for the quickstart\nquickstart_dir = Path(\"./ariadne_codegen_quickstart\")\nquickstart_dir.mkdir(exist_ok=True)\n\nschema_path = quickstart_dir / \"schema.graphql\"\nqueries_path = quickstart_dir / \"queries.graphql\"\n\nschema_path.write_text(schema_content)\nqueries_path.write_text(queries_content)\n\n# --- Step 1: Configure ariadne-codegen in pyproject.toml ---\npyproject_toml_content = f'''\n[tool.ariadne-codegen]\nschema_path = \"{schema_path}\"\nqueries_path = \"{queries_path}\"\ntarget_package_name = \"my_graphql_client\"\ntarget_package_path = \".\"\n'''\n\npyproject_toml_path = quickstart_dir / \"pyproject.toml\"\npyproject_toml_path.write_text(pyproject_toml_content)\n\n# --- Step 2: Run ariadne-codegen to generate the client ---\nprint(\"Generating GraphQL client...\")\n# This command needs to be run in the shell where `ariadne-codegen` is installed.\n# For demonstration, we simulate its effect by assuming successful generation.\n# In a real setup, you would run: `cd ariadne_codegen_quickstart && ariadne-codegen`\n# For this runnable example, we will proceed as if 'my_graphql_client' was generated.\n# A placeholder for actual generation: \n# os.system(f\"cd {quickstart_dir} && ariadne-codegen\")\n\n# To make this code runnable without `ariadne-codegen` actually being installed and run,\n# we simulate the generated client structure. In a real scenario, these files would be created.\n(quickstart_dir / \"my_graphql_client\").mkdir(exist_ok=True)\n(quickstart_dir / \"my_graphql_client\" / \"__init__.py\").touch()\n(quickstart_dir / \"my_graphql_client\" / \"client.py\").write_text(\n    \"\"\"from typing import Any, Dict, List, Optional\n\nclass User:\n    id: str\n    name: str\n    email: str\n    country: Optional[str]\n\nclass ListAllUsers:\n    users: List[User]\n\nclass CreateNewUser:\n    createUser: User\n\nclass AsyncBaseClient:\n    def __init__(self, url: str, headers: Optional[Dict[str, str]] = None):\n        self.url = url\n        self.headers = headers if headers is not None else {}\n\n    async def _execute(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:\n        # In a real client, this would make an actual HTTP request.\n        # For this example, we return mock data.\n        print(f\"Executing query to {self.url} with headers: {self.headers}\")\n        print(f\"Query: {query}\")\n        print(f\"Variables: {variables}\")\n        if \"ListAllUsers\" in query:\n            return {\"data\": {\"users\": [{\"id\": \"1\", \"name\": \"Alice\", \"email\": \"alice@example.com\", \"country\": \"Wonderland\"}]}}\n        if \"CreateNewUser\" in query:\n            return {\"data\": {\"createUser\": {\"id\": \"2\", \"name\": variables['name'], \"email\": variables['email']}}}\n        return {\"data\": {}}\n\nclass Client(AsyncBaseClient):\n    async def list_all_users(self) -> ListAllUsers:\n        query = \"\"\"\n            query ListAllUsers {\n              users {\n                id\n                name\n                email\n                country\n              }\n            }\n        \"\"\"\n        data = await self._execute(query)\n        return ListAllUsers(users=[User(**user_data) for user_data in data['data']['users']])\n\n    async def create_new_user(self, name: str, email: str) -> CreateNewUser:\n        query = \"\"\"\n            mutation CreateNewUser($name: String!, $email: String!) {\n              createUser(name: $name, email: $email) {\n                id\n                name\n                email\n              }\n            }\n        \"\"\"\n        variables = {\"name\": name, \"email\": email}\n        data = await self._execute(query, variables)\n        return CreateNewUser(createUser=User(**data['data']['createUser']))\n\n\"\"\")\n\n# Add placeholder pydantic BaseModels (actual generated models would be more complex)\n(quickstart_dir / \"my_graphql_client\" / \"base_model.py\").write_text(\n    \"\"\"from pydantic import BaseModel\nclass BaseGraphQLModel(BaseModel):\n    class Config:\n        arbitrary_types_allowed = True\n\"\"\")\n\n# Ensure `my_graphql_client` is importable from the quickstart_dir\nimport sys\nsys.path.insert(0, str(quickstart_dir))\n\nfrom my_graphql_client.client import Client\nfrom my_graphql_client.client import ListAllUsers, CreateNewUser\n\nasync def main():\n    # Replace with your actual GraphQL endpoint\n    graphql_url = os.environ.get('GRAPHQL_ENDPOINT', 'https://api.example.com/graphql')\n    # Replace with your actual authentication token if needed\n    auth_token = os.environ.get('GRAPHQL_AUTH_TOKEN', '')\n\n    headers = {}\n    if auth_token:\n        headers['Authorization'] = f'Bearer {auth_token}'\n\n    client = Client(url=graphql_url, headers=headers)\n\n    print(\"\\n--- Listing all users ---\")\n    all_users_result: ListAllUsers = await client.list_all_users()\n    for user in all_users_result.users:\n        print(f\"User ID: {user.id}, Name: {user.name}, Email: {user.email}, Country: {user.country}\")\n\n    print(\"\\n--- Creating a new user ---\")\n    new_user_result: CreateNewUser = await client.create_new_user(name=\"Charlie\", email=\"charlie@example.com\")\n    created_user = new_user_result.createUser\n    print(f\"Created User ID: {created_user.id}, Name: {created_user.name}, Email: {created_user.email}\")\n\n    # Clean up temporary files\n    import shutil\n    shutil.rmtree(quickstart_dir)\n    print(f\"\\nCleaned up temporary directory: {quickstart_dir}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n","lang":"python","description":"This quickstart demonstrates how to use `ariadne-codegen` to generate a Python client and then interact with a GraphQL API. It involves defining a GraphQL schema and queries, configuring `ariadne-codegen` via `pyproject.toml`, running the code generator, and finally importing and using the generated client. The example simulates a generated client for demonstration purposes without requiring a live GraphQL server or the actual `ariadne-codegen` command execution during runtime."},"warnings":[{"fix":"Review the official changelog (e.g., on the Ariadne blog or GitHub releases) for each new minor version. Update generated client code and configurations accordingly.","message":"Ariadne Codegen's minor version increments for breaking changes until 1.0.0. Always check the changelog for new releases, as imports, class names, or behavior might change.","severity":"breaking","affected_versions":"All versions before 1.0.0 (e.g., 0.x to 0.y where y > x)."},{"fix":"If migrating from ariadne-codegen <0.8 and using Pydantic v2, update method calls on generated models from `parse_obj` to `model_validate` and `dict` to `model_dump`. Ensure your project's Pydantic version aligns with the `ariadne-codegen` version requirements.","message":"Pydantic v2 support (>=2.0.0,<3.0.0) was introduced in version 0.8. This changed method names on generated models (e.g., `parse_obj` to `model_validate`, `dict` to `model_dump`) and removed `model_rebuild` calls.","severity":"breaking","affected_versions":"Versions 0.8 and later (when upgrading from <0.8)."},{"fix":"Always explicitly define `target_package_name` in your `pyproject.toml` (e.g., `target_package_name = \"my_api_client\"`) for clarity and consistency, or remember to import from `graphql_client.client` if using the default.","message":"The default generated package name is `graphql_client`. If you don't specify `target_package_name` in `pyproject.toml`, your imports will need to reflect this default.","severity":"gotcha","affected_versions":"All versions."},{"fix":"Add a `[tool.ariadne-codegen.scalars.YOUR_SCALAR_NAME]` section to your `pyproject.toml` and specify `type`, `serialize`, and `parse` methods. For example, for a `DateTime` scalar: `[tool.ariadne-codegen.scalars.DateTime] type = \"datetime.datetime\" serialize = \"str\" parse = \"isoformat\"`.","message":"Custom scalars are by default represented as `typing.Any`. To get proper type hints and (de)serialization, you need to configure them in `pyproject.toml`.","severity":"gotcha","affected_versions":"All versions."},{"fix":"Update exception handling in your code to catch the new error names: `GraphQLClientInvalidResponseError` and `GraphQLClientGraphQLMultiError`.","message":"In version 0.11, `GraphQlClientInvalidResponseError` was renamed to `GraphQLClientInvalidResponseError` (capital 'L'). Additionally, `GraphQLClientGraphQLMultiError` is now raised for payloads with an `errors` key but no `data`.","severity":"breaking","affected_versions":"Versions 0.11 and later (when upgrading from <0.11)."},{"fix":"If encountering issues with field names, inspect the generated client code. Consider using GraphQL aliases in your queries to rename problematic fields or implement a custom plugin to adjust name generation if necessary.","message":"Generated model field names might clash with Python reserved keywords or Pydantic `BaseModel` methods. `ariadne-codegen` appends an underscore to resolve some of these, but it's important to be aware.","severity":"gotcha","affected_versions":"All versions."}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}