Package: ariadne-codegen
Ariadne Code GeneratorPython code generator that takes graphql schema, queries, mutations and subscriptions and generates Python package with fully typed and asynchronous GraphQL client.It's available asariadne-codegencommand and reads configuration from thepyproject.tomlfile:$ ariadne-codegenIt can also be run aspython -m ariadne_codegen.FeaturesGenerate pydantic models from schema types, inputs and enums.Generate pydantic models for GraphQL results.Generate client package with each GraphQL operation available as async method.InstallationAriadne Code Generator can be installed with pip:$ pip install ariadne-codegenTo support subscriptions, default base client requireswebsocketspackage:$ pip install ariadne-codegen[subscriptions]Configurationariadne-codegenreads configuration from[tool.ariadne-codegen]section in yourpyproject.toml. You can use other configuration file with--configoption, eg.ariadne-codegen --config custom_file.tomlMinimal configuration for client generation:[tool.ariadne-codegen]schema_path="schema.graphql"queries_path="queries.graphql"Required settings:queries_path- path to file/directory with queriesOne of the following 2 parameters is required, in case of providing both of themschema_pathis prioritized:schema_path- path to file/directory with graphql schemaremote_schema_url- url to graphql server, where introspection query can be perfomedOptional settings:remote_schema_headers- extra headers that are passed along with introspection query, eg.{"Authorization" = "Bearer: token"}. To include an environment variable in a header value, prefix the variable with$, eg.{"Authorization" = "$AUTH_TOKEN"}remote_schema_verify_ssl(defaults totrue) - a flag that specifies wheter to verify ssl while introspecting remote schematarget_package_name(defaults to"graphql_client") - name of generated packagetarget_package_path(defaults to cwd) - path where to generate packageclient_name(defaults to"Client") - name of generated client classclient_file_name(defaults to"client") - name of file with generated client classbase_client_name(defaults to"AsyncBaseClient") - name of base client classbase_client_file_path(defaults to.../ariadne_codegen/client_generators/dependencies/async_base_client.py) - path to file wherebase_client_nameis definedenums_module_name(defaults to"enums") - name of file with generated enums modelsinput_types_module_name(defaults to"input_types") - name of file with generated input types modelsfragments_module_name(defaults to"fragments") - name of file with generated fragments modelsinclude_comments(defaults to"stable") - option which sets content of comments included at the top of every generated file. Valid choices are:"none"(no comments),"timestamp"(comment with generation timestamp),"stable"(comment contains a message that this is a generated file)convert_to_snake_case(defaults totrue) - a flag that specifies whether to convert fields and arguments names to snake caseinclude_all_inputs(defaults totrue) - a flag specifying whether to include all inputs defined in the schema, or only those used in supplied operationsinclude_all_enums(defaults totrue) - a flag specifying whether to include all enums defined in the schema, or only those used in supplied operationsasync_client(defaults totrue) - default generated client isasync, change this to optionfalseto generate synchronous client insteadopentelemetry_client(defaults tofalse) - default base clients don't support any performance tracing. Change this option totrueto use the base client with Open Telemetry support.files_to_include(defaults to[]) - list of files which will be copied into generated packageplugins(defaults to[]) - list of plugins to use during generationPluginsAriadne Codegen implements a plugin system that enables further customization and fine-tuning of generated Python code. It’s documentation is available separately in thePLUGINS.mdfile.Standard pluginsAriadne Codegen ships with optional plugins importable from theariadne_codegen.contribpackage:ariadne_codegen.contrib.shorter_results.ShorterResultsPlugin- This plugin processes generated client methods for operations where only single top field is requested, so they return this field's value directly instead of operation's result type. For example get_user method generated for queryGetUser() { user(...) { ... }}will return value of user field directly instead ofGetUserResult.ariadne_codegen.contrib.extract_operations.ExtractOperationsPlugin- This extracts query strings from generated client's methods into separateoperations.pymodule. It also modifies the generated client to import these definitions. Generated module name can be customized by addingoperations_module_name="custom_name"to the[tool.ariadne-codegen.operations]section in config. Eg.:[tool.ariadne-codegen]...plugins=["ariadne_codegen.contrib.extract_operations.ExtractOperationsPlugin"][tool.ariadne-codegen.extract_operations]operations_module_name="custom_operations_module_name"ariadne_codegen.contrib.no_reimports.NoReimportsPlugin- This plugin removes content of generated__init__.py. This is useful in scenarios where generated plugins contain so many Pydantic models that client's eager initialization of entire package on first import is very slow.Using generated clientGenerated client can be imported from package:from{target_package_name}.{client_file_name}import{client_name}Example with default settings:fromgraphql_client.clientimportClientPassing headers to clientClient (with default base client), takes passed headers and attaches them to every sent request.client=Client("https://example.com/graphql",{"Authorization":"Bearer token"})For more complex scenarios, you can pass your own http client:client=Client(http_client=CustomComplexHttpClient())CustomComplexHttpClientneeds to be an instance ofhttpx.AsyncClientfor async client, orhttpx.Clientfor sync.WebsocketsTo handle subscriptions, defaultAsyncBaseClientuseswebsocketsand implementsgraphql-transport-wssubprotocol. Argumentsws_originandws_headersare added as headers to the handshake request andws_connection_init_payloadis used as payload ofConnectionInitmessage.File uploadDefault base client (AsyncBaseClientorBaseClient) checks if any part ofvariablesdictionary is an instance ofUpload. If at least one instance is found then client sends multipart request according toGraphQL multipart request specification.ClassUploadis included in generated client and can be imported from it:from{target_package_name}importUploadBy default we use this class to represent graphql scalarUpload. For schema with different name for this scalar, you can still useUploadand default client for file uploads:[tool.ariadne-codegen.scalars.OTHERSCALAR]type="Upload"Open TelemetryWhen config optionopentelemetry_clientis set totruethen default, included base client is replaced with one that implements the opt-in Open Telemetry support. By default this support does nothing but when theopentelemetry-apipackage is installed and thetracerargument is provided then the client will create spans with data about performed requests.Tracing arguments handled byBaseClientOpenTelemetry:tracer:Optional[Union[str, Tracer]] = None- tracer object or name which will be passed to theget_tracermethodroot_context:Optional[Context] = None- optional context added to root spanroot_span_name:str = "GraphQL Operation"- name of root spanAsyncBaseClientOpenTelemetrysupports all arguments whichBaseClientOpenTelemetrydoes, but also exposes additional arguments regarding websockets:ws_root_context:Optional[Context] = None- optional context added to root span for websocket connectionws_root_span_name:str = "GraphQL Subscription"- name of root span for websocket connectionCustom scalarsBy default, not built-in scalars are represented astyping.Anyin generated client.
You can provide information about specific scalar by adding section topyproject.toml:[tool.ariadne-codegen.scalars.{graphqlscalarname}]type="(required) python type name"serialize="function used to serialize scalar"parse="function used to create scalar instance from serialized form"For each custom scalar client will use giventypein all occurrences of{graphql scalar name}. If provided,serializeandparsewill be used for serialization and deserialization. In result modelstypewill be annotated withBeforeValidator, eg.Annotated[type, BeforeValidator(parse)]. In inputs annotation will usePlainSerializer, eg.Annotated[type, PlainSerializer(serialize)].
Iftype/serialize/parsecontains at least one.then string will be split by it's last occurrence. First part will be used as module to import from, and second part as type/method name. For example,type = "custom_scalars.a.ScalarA"will producefrom custom_scalars.a import ScalarA.Example with scalar mapped to built-in typeIn this case scalar is mapped to built-instrwhich doesn't require customserializeandparsemethods.[tool.ariadne-codegen.scalars.SCALARA]type="str"Example with type supported by pydanticIn this scenario scalar is represented asdatetime, so it needs to be imported. Pydantic handles serialization and deserialization so customparseandserializeis not necessary.[tool.ariadne-codegen.scalars.DATETIME]type="datetime.datetime"Example with fully custom typeIn this example scalar is represented as classTypeB. Pydantic can`t handle serialization and deserialization so customparseandserializeis necessary. To providetype,parseandserializeimplementation we can usefiles_to_includeto copytype_b.pyfile.[tool.ariadne-codegen]...files_to_include=[".../type_b.py"][tool.ariadne-codegen.scalars.SCALARB]type=".type_b.TypeB"parse=".type_b.parse_b"serialize=".type_b.serialize_b"# inputs.pyclassTestInput(BaseModel):value_b:Annotated[TypeB,PlainSerializer(serialize_b)]# get_b.pyclassGetB(BaseModel):query_b:Annotated[TypeB,BeforeValidator(parse_b)]# client.pyclassClient(AsyncBaseClient):asyncdeftest_mutation(self,value:TypeB)->TestMutation:...variables:Dict[str,object]={"value":serialize_b(value),}...Extending generated typesExtending models with custom mixinsmixindirective allows to extend class generated for query/mutation field with custom logic.mixintakes two required arguments:from- name of a module to import fromimport- name of a parent classGenerated class will useimportas extra base class, and import will be added to the file.from{from}import{import}...classOperationNameField(BaseModel,{import}):...This directive can be used along withfiles_to_includeoption to extend functionality of generated classes.Example of usage ofmixinandfiles_to_include:Query withmixindirective:query listUsers {
    users @mixin(from: ".mixins", import: "UsersMixin") {
        id
    }
}Part ofpyproject.tomlwithfiles_to_include(mixins.pycontainsUsersMixinimplementation)files_to_include=[".../mixins.py"]Part of generatedlist_users.pyfile:...from.mixinsimportUsersMixin...classListUsersUsers(BaseModel,UsersMixin):...Multiple clientsTo generate multiple different clients you can store config for each in different file, then provide path to config file by--configoption, eg.ariadne-codegen --config clientA.toml
ariadne-codegen --config clientB.tomlGenerated code dependenciesGenerated code requires:pydantichttpxwebsockets(only for default async base client)Bothhttpxandwebsocketsdependencies can be avoided by providing another base client class withbase_client_file_pathandbase_client_nameoptions.ExampleExample with simple schema and few queries and mutations is availablehere.Generating graphql schema's python representationInstead of generating a client, you can generate a file with a copy of a GraphQL schema. To do this callariadne-codegenwithgraphqlschemaargument:ariadne-codegen graphqlschemagraphqlschemamode reads configuration from the same place asclientbut uses onlyschema_path,remote_schema_url,remote_schema_headers,remote_schema_verify_ssloptions to retrieve the schema andpluginsoption to load plugins.In addition to the above,graphqlschemamode also accepts additional settings specific to it:target_file_pathA string with destination path for generated file. Must be either a Python (.py), or GraphQL (.graphqlor.gql) file.Defaults toschema.py.Generated Python file will contain:Necessary importsType map declaration{type_map_variable_name}: TypeMap = {...}Schema declaration{schema_variable_name}: GraphQLSchema = GraphQLSchema(...)Generated GraphQL file will contain a formatted output of theprint_schemafunction from thegraphql-corepackage.schema_variable_nameA string with a name for schema variable, must be valid python identifier.Defaults to"schema". Used only if target is a Python file.type_map_variable_nameA string with a name for type map variable, must be valid python identifier.Defaults to"type_map". Used only if target is a Python file.ContributingWe welcome all contributions to Ariadne! If you've found a bug or issue, feel free to useGitHub issues. If you have any questions or feedback, don't hesitate to catch us onGitHub discussions.For guidance and instructions, please seeCONTRIBUTING.md.Also make sure you follow@AriadneGraphQLon Twitter for latest updates, news and random musings!Crafted with ❤️ byMirumee Softwarehello@mirumee.com