Package: gql-schema-codegen
gql_schema_codegenEasily generate Python typings from a GrapqhQL schema.Let's go straight to the point, here you can see a very simple input schema content and its corresponding output:Input GraphQL schemaschema{query:Querymutation:Mutation}enumUserType{STAFFADMIN}typeUser{id:ID!email:Stringusername:String!first_name:String!last_name:String!full_name:String!dob:String@date(format:"%Y-%m-%d")type:UserType!people:[String]}typeQuery{me:User!}inputSignUpInput{email:String!username:String!first_name:String!last_name:String!password:String!}inputLoginInput{identifier:String!password:String!}typeMutation{login(input:LoginInput!):User!signUp(input:SignUpInput!):User!logout:Boolean}Output Python typingsfromenumimportEnumfromtypingimportClassVar,List,Optional,TypedDictUserType=Enum('UserType','STAFF ADMIN')User=TypedDict('User',{'id':str,'email':Optional[str],'username':str,'first_name':str,'last_name':str,'full_name':str,'dob':Optional[str],'type':'UserType','people':Optional[List[str]],})Query=TypedDict('Query',{'me':'MeQueryResult',})MeQueryResult=ClassVar['User']Mutation=TypedDict('Mutation',{'login':'LoginMutationResult','signUp':'SignUpMutationResult','logout':'LogoutMutationResult',})LoginParams=TypedDict('LoginParams',{'input':'LoginInput',})LoginMutationResult=ClassVar['User']SignUpParams=TypedDict('SignUpParams',{'input':'SignUpInput',})SignUpMutationResult=ClassVar['User']LogoutMutationResult=boolSignUpInput=TypedDict('SignUpInput',{'email':str,'username':str,'first_name':str,'last_name':str,'password':str,})LoginInput=TypedDict('LoginInput',{'identifier':str,'password':str,})There are some more complex examples available undertestsif you are curious about how accurate is this tool.MotivationWhile I was trying outAriadne(a library for implementing GraphQL servers using schema-first approach) and writing the resolvers for the queries and mutations that I defined in my GraphQL schema, I was missing the ability to define the types for the params and the return values.
I hope this library helps some devs to write better typed resolvers for their projects, while keeping the resolvers code synced with the schema definition.Ariadne example with typed resolversfromtyping_extensionsimportUnpackfromgraphqlimportGraphQLResolveInfofrom..snapshots.test_schemaimportLoginParams,LoginMutationResult,MeQueryResult,SignUpParams,SignUpMutationResult,LogoutMutationResult,User,UserTypefromariadneimportQueryType,MutationTypemocked_user:User={'id':'1','email':'saulydominguez@gmail.com','dob':'28/05/1999','first_name':'Saul','last_name':'Dominguez','full_name':'Saul Dominguez','username':'saulydominguez','people':[],'type':UserType.ADMIN}query=QueryType()@query.field('me')defresolve_me(obj,info:GraphQLResolveInfo)->MeQueryResult:# implementation to obtain current userreturnmocked_usermutation=MutationType()@mutation.field('login')defresolve_login(_,info:GraphQLResolveInfo,**params:Unpack[LoginParams])->LoginMutationResult:_input=params['input']# you can use typed _input var down here# login implementationreturnmocked_user@mutation.field('signUp')defresolve_sign_up(_,info:GraphQLResolveInfo,**params:Unpack[SignUpParams])->SignUpMutationResult:# login implementationreturnmocked_user@mutation.field('logout')defresolve_logout(_,info:GraphQLResolveInfo)->LogoutMutationResult:# logout implementationreturnTrueInstallationYou can easily install it via pip:$pipinstallgql_schema_codegenUsageThree use cases depending on where the GraphQL schema is defined:1. Generate types from a single schema fileThis is the simplest case, you have just a single file with all the types declared there.$python-mgql_schema_codegen-p./schema.graphql-t./schema_types.py2. Generate types from a remote GraphQL serverYou have deployed a GraphQL server with introspection enabled, you can just provide a link to that server and this tool will do its job.$python-mgql_schema_codegen-uhttps://gitlab.com/api/graphql-t./schema_types.py⚠️ For now, it only works with public schemas, without any authentication required. I will be adding support for this soon.3. Generate types from different schema files in a directoryIt's a common thing that you don't have your schema definition centralized in a single file but in multiple ones instead, if that's your case you can provide that directory path where all the graphql/gql files are located and this tool will find them and merge them into a single schema to process it later on.$python-mgql_schema_codegen-p./dir_with_gql_types-t./schema_types.pyIf you just need more info about how to run it:$python-mgql_schema_codegen--help
usage:__main__.py[-h][--schema-pathSCHEMA_PATH][--schema-urlSCHEMA_URL][--to-pathTO_PATH][--config-fileCONFIG_FILE]GeneratepythonfilewithtypesfromaGraphQLschemafile.

optionalarguments:-h,--helpshowthishelpmessageandexit--schema-pathSCHEMA_PATH,-pSCHEMA_PATHpathoftheschemafile(default:schema.graphql)--schema-urlSCHEMA_URL,-uSCHEMA_URLurloftheschema--to-pathTO_PATH,-tTO_PATHwantedoutputfilepath(default:schema_types.py)--config-fileCONFIG_FILE,-cCONFIG_FILEpathoftheconfigfileinyamlformat(default:gql_schema_codegen.config.yml)Custom scalars supportBy default, scalars found in your schema will be generated asAny, if you already know the types for these scalars you can create a custom config file and define these types there:scalars:DateTime:strTime:strBigInt:intBoardID:intDuration:DesignFieldsNotice how you can also set another type from your schema (or a Python class) as custom value for your scalar.TestsThey are simple snapshot tests, they work by comparing the output with the expected output, each test corresponds to a file stored undertests/snapshots.
You will find different tests declared in thetestsdirectory.
If you want to run them you need to install thepytest-snapshotmodule first. Then, you can run them with:$pytest--snapshot-updateYou can also run a specific test this way:$pytest--snapshot-update-k"test_pokeapi_schema_snapshot"ContributionFeel free to open some issues and/or pull requests if you want to participate in the development of this module, either just by proposing changes or by actively participating with code. At the moment of writing this, I have at least a couple of ideas planned to improve the way this generator works. ✨SupportIf you like the work I do and want to support me, you can do it below:LicenseMIT