# GraphQL Codegen for TypeScript in Angular ## Existing Solutions - [GraphQL Code Generator](https://github.com/dotansimha/graphql-code-generator) ([Official Site](https://graphql-code-generator.com/)) - [apollo-tooling](https://github.com/apollographql/apollo-tooling) We are currently using the **GraphQL Code Generator**. ## What we like - Locality of the generated files. They are alongside the other component files. - They generate an Angular Service which can just be injected into the component. - It works on `some.component.graphql` files instead of using [graphql-tag](https://www.npmjs.com/package/graphql-tag) ## What we don't like - Global exported types (except Enums and Input Types) which can be accidently imported and used in the App. - Making it as unfriendly as possible for TypeScript errors (usage of `Pick`, `Maybe`, `Scalars` etc.). - No type alias for sub fields. Everyting is "inlined" starting from the root type. Making it nearly impossible to alias it and re-use the GraphQL structure oof sub fields inside the App. ## Implementation Options ~~**a) Extend, or fork Apollo Codegen**~~ (we are replacing [apollo-tooling](https://github.com/apollographql/apollo-tooling) with [ngx-urql](https://github.com/alexandercarls/ngx-urql) in the future) **b) Extend `GraphQL Code Generator`** For the plugin system we have likely have two options: 1. Replace [TypeScript Plugin](https://github.com/alexandercarls/ngx-urql/blob/master/projects/example/codegen.yaml#L6) and/or [typescript-operations Plugin](https://github.com/alexandercarls/ngx-urql/blob/master/projects/example/codegen.yaml#L14). 2. Add a new plugin which transforms the previous ones. ~~**c) Write our own stuff!**~~ (Option **b)** works for us as it provides a working infrastructure such as CLI and a configuarable plugin architecture) ## Target Concept Based on the current usage of **GraphQL Code Generator**. with the following `codegen.yml` configuration ```yml schema: "http://localhost:5000" documents: "src/**/*.graphql" hooks: afterAllFileWrite: - prettier --write generates: src/types.generated.ts: - add: "/* tslint:disable */" - "typescript" src/: preset: near-operation-file presetConfig: extension: .generated.ts baseTypesPath: types.generated.ts plugins: - add: "/* tslint:disable */" - "typescript-operations" - "typescript-apollo-angular" src/__generated__/fragmentTypes.json: plugins: - fragment-matcher ``` **Reduce globally accessible types from `types.generated.ts`.** Apollo only creates Enum and Input Types ([Example](https://gitlab.intern.atmina.systems/wobcom/abis-prototype/-/blob/981265ce61a12a463e45cb95b69106d0ca11a614/abis.web/__generated__/globalTypes.ts)) while GQL-Codegen creates every GraphQL Type, which has no usage in the application ([Example](https://gitlab.intern.atmina.systems/dguv/kulturcheck-app/-/blob/8dac64281fcaecd818049557b1289adccacea365/frontend/src/types.generated.ts#L48)) and can be wrongly imported. ==> **Solution**: Only generate Enums globally. **Create plain TypeScript interfaces**. GQL-Codegen heavily uses abstraction like `Scalars`, `Maybe`, `Pick` etc. which makes the generated interface as well as compile time errors less readable ([Example](https://gitlab.intern.atmina.systems/dguv/kulturcheck-app/-/blob/develop/frontend/src/app/auswertung/services/auswertung.generated.ts#L16)). ==> **Solution**: Generate the TypeScript Code as plainly as possible and minimize abstractions. There is no reason to be as much DRY as GQL-Codegen, hence why we are generating it instead of writing. **Create an alias for nested types instead of embedding it.** GQL-Codegen only embeds the interface which makes it really hard or impossible to a) Re-Use a nested type, especially with a union type ([Example](https://gitlab.intern.atmina.systems/dguv/kulturcheck-app/-/blob/8dac64281fcaecd818049557b1289adccacea365/frontend/src/app/auswertung/services/auswertung.generated.ts#L34)). b) TypeScript compile time errors are less, if at all, readable. We are currently creating and missusing lots of GraphQL Fragments to have a generated type alias ([Example](https://gitlab.intern.atmina.systems/dguv/kulturcheck-app/-/blob/8dac64281fcaecd818049557b1289adccacea365/frontend/src/app/shared/services/check.service.graphql#L13)). However, Fragments do not allow passing in variables. Apollo create a TypeScript Interface for each complex type which name starts with the query/mutation name and followed with the property path ([Example](https://gitlab.intern.atmina.systems/wobcom/abis-prototype/-/blob/981265ce61a12a463e45cb95b69106d0ca11a614/abis.web/src/app/partners/partner/tabs/partner-tk-leitung/__generated__/PartnerTkLeitungenByPartnerId.ts#L23)). If the name is too long, the developer can type still type alias it. We can leverage a self-written Directive which our Codegen-Plugin incorporates for the generated interface name. For example ```graphql= query GetStrukturAuswertung { strukturCheck { id frageHandlungsfelder @export("Handlungsfeld") { id checkTyp ordinal titel frageBloecke @export("FrageBlock") { id ordinal titel menuTitel # omitted } } } } ``` No intermediate interface will be exported for consumption. Only the query itself and fields which are annotated wih `@export` will be publicly accessible from the generated file. Make sure that the snippets from the `*.graphl`-Files are still copyable to Insomnia etc on its own. It is likely that they throw an error due to an unknown directive. Comments would be prefered here `# export FrageBlock`. However, as of today they are not part of the AST. ==> **Solution** Use the solution from Apollo. Allow for type aliasing with a custom GraphQL Directive **Create local Angular Services next to the `*.graphql` file.** ==> **Solution** Already implemented.