Migrating to typecscript-operations and client-preset 6.0
What’s new?
typescript-operations and client-preset v6.0 come with a major overhaul of type generation and config for better DX.
- Type generation and usage changes
- Object and Args types are no longer generated
- Scalar types are no longer generated as re-useable type
- Input and Enum types are only generated if used in documents
__typenameis only generated if used in documents- Document field types are generated to correctly match runtime expectation
- Configuration and dependency changes
- No longer depends on
typescriptplugin - New approach in
typescript-operationsto generate and share types between generated types without extra depedencies - Consolidation, deletion and simplification of config options such as
avoidOptionals,enumsAsTypesand many more.
- Other bug fixes and quality of life improvements
For more details on this change, read the Breaking changes section. For a full list of changes, see the CHANGELOG.
Installation
Install the new versions of official plugins that are in your dependencies:
npm i -D @graphql-codegen/cli@latest @graphql-codegen/typescript-operations@latest @graphql-codegen/client-preset@latestGraphQL Codegen packages share a lot of code internally, so if you have installed other official packages (such as
@graphql-codegen/visitor-plugin-common or @graphql-codegen/typescript-resolvers) explicitly in the same repo, be
sure to update them at the same time to avoid unexpected issues.
Migration
client-preset
client-preset already applies the recommended setup, you won’t have to make any changes to default config:
const config: CodegenConfig = {
generates: {
'src/gql/': {
preset: 'client'
}
}
}typescript-operations
typescript-operations can be used in a variety of custom setup, this section aims to explain the changes in the most popular setup.
One-file setup
This setup generates all base Input+Enum types and operation types into one file. Previously, this setup required typescript plugin but in the new version, you can remove typescript plugin as typescript-operations works by itself now.
const config: CodegenConfig = {
generates: {
'src/graphql/types.generated.ts': {
plugins: ['typescript', 'typescript-operations'],
plugins: ['typescript-operations']
}
}
}Multi-file setup
Some repos may have multiple Codegen projects, each generating types for operations within its scope. In such cases, users may want to re-use the base Input+Enum types generated by typescript plugin with import-types preset:
const config: CodegenConfig = {
generates: {
'src/shared/base-types.generated.ts': {
plugins: ['typescript']
},
'src/project-1/types.generated.ts': {
documents: 'src/project-1/**/*.graphql.ts',
preset: 'import-types',
plugins: ['typescript-operations'],
presetConfig: {
typesPath: '../shared/base-types.generated.ts'
}
},
'src/project-2/types.generated.ts': {
documents: 'src/project-2/**/*.graphql.ts',
preset: 'import-types',
plugins: ['typescript-operations'],
presetConfig: {
typesPath: 'src/shared/base-types.generated.ts'
}
}
}
}Now, it is simpler to do this with typescript-operations as it supports this approach using its own config:
const config: CodegenConfig = {
generates: {
'src/shared/base-types.generated.ts': {
documents: 'src/**/*.graphql.ts' // Parses all files with GraphQL documents to generate Enum+Input types that are used by every project.
plugins: ['typescript-operations'],
config: {
generatesOperationTypes: false,
}
},
'src/project-1/types.generated.ts': {
documents: 'src/project-1/**/*.graphql.ts', // Only parses GraphQL documents within project-1's scope
plugins: ['typescript-operations'],
config: {
importSchemaTypesFrom: 'src/shared/base-types.generated.ts',
}
},
'src/project-2/types.generated.ts': {
documents: 'src/project-2/**/*.graphql.ts', // Only parses GraphQL documents within project-1's scope
plugins: ['typescript-operations'],
config: {
importSchemaTypesFrom: 'src/shared/base-types.generated.ts',
},
}
}
}Breaking changes
- Object types are no longer generated
Previously, Object types from the schema are generated (via the typescript plugin), for example:
// Example schemna type generated from a User Object type
export type User = {
__typename?: 'User'
id: Scalars['ID']['output']
name: Scalars['String']['output']
}These types contain all the fields from the schema. It’s expected in GraphQL operations to not fetch all fields in practice so these types should never be used. However, they are often accidentally used in application code because they are generated.
Now, Object types are no longer generated. Operation types (Variables and Result) are generated based on fields in the documents so these should always be used for client types.
If you need these for any reasons, please generate them using typescript plugin into a separate file.
- Args types are no longer generated
Args types are only used for server use cases, so they are no longer generated for client use cases.
- Scalar types are no longer generated as re-useable type
Previously, Scalar types from the schema are generated into an object and re-used in Variables types:
// All native+custom scalars found in the schema
export type Scalars = {
ID: { input: string | number; output: string }
String: { input: string; output: string }
Boolean: { input: boolean; output: boolean }
Int: { input: number; output: number }
Float: { input: number; output: number }
}Now, scalars in Input and Variables types are inlined (similar to Result types) to avoid using this utility types:
export type Scalars = {
ID: { input: string | number; output: string }
String: { input: string; output: string }
Boolean: { input: boolean; output: boolean }
Int: { input: number; output: number }
Float: { input: number; output: number }
}
export type UserInput = {
id: Scalars['ID']['input']
id: string | number
}
export type UserVariables = Exact<{
id: Scalars['ID']['input']
id: string | number
}>- Input and Enum types are only generated if used in documents
Previously, all Input and Enum types are generated even if they are not used. This could increase bundle size when Enums that incur runtime are used e.g. when native TypeScript enum or const enum are used. Now, only Input and Enum types used in operations are generated.
__typenameis only generated if used in documents
Previously, __typename in Results is generated as optional by default, even when it is not requested:
query User {
user {
# Note: __typename is not in the selection set
id
}
}Previously, the above operation resulted in a type with optional __typename:
export type UserQuery = {
user: {
__typename?: 'User'
id: string
}
}Now, __typename will not be generated by default, if it is not in the selection set:
export type UserQuery = {
user: {
__typename?: 'User'
id: string
}
}Some clients, such as Apollo Client, automatically request __typename. To achieve the same behaviour you can use
skipTypeNameForRoot and nonOptionalTypename options to configure type behaviours.
- Document field types are generated to correctly match runtime expectation
Previously, nullable fields in Results are generated as optional by default:
export type UserQuery = {
user: {
age?: string | null
}
}Now, nullable fields in Results are never optional (except in some cases e.g. when @defer is used)
export type UserQuery = {
user: {
age?: string | null
age: string | null
}
}- Consolidation of Enum config options and change of default value
Previously, there were many options and