Installation
The gql.tada package provides typings and the runtime API as a library,
while @0no-co/graphqlsp integrates with the TypeScript language server
to integrate with an IDE or editor.
Step 1 — Installing packages
We’ll start by installing gql.tada as a dependency, and @0no-co/graphqlsp as
a dev-dependency using out project’s package manager.
npm install gql.tadanpm install --save-dev @0no-co/graphqlsppnpm add gql.tadapnpm add --save-dev @0no-co/graphqlspyarn add gql.tadayarn add --dev @0no-co/graphqlspbun add gql.tadabun add --dev @0no-co/graphqlspNext, we’ll have to add @0no-co/graphqlsp as a plugin to our TypeScript
configuration.
{ "compilerOptions": { "strict": true, "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "./schema.graphql", "tadaOutputLocation": "./src/graphql-env.d.ts" } ] }}This will start up a “TypeScript Language Service Plugin” which runs when TypeScript is analyzing a file in our IDE or editor.
gql.tada on its own won’t provide you with editor hints, diagnostics, or errors, so @0no-co/graphqlsp is crucial
in providing you feedback and help when writing GraphQL documents.
Step 2 — Configuring a schema
@0no-co/graphqlsp needs to have a GraphQL API’s schema to function correctly.
The schema provides it with the types, fields, and description information of a GraphQL API.
We can set @0no-co/graphqlsp up with our schema in our tsconfig.json file.
In the plugin options we’ll update the schema key.
{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp",
"schema": "./schema.graphql", "tadaOutputLocation": "./src/graphql-env.d.ts" } ] }}The schema option currently allows for three different formats to load a schema. It accepts either:
- a path to a
.graphqlfile containing a schema definition (in GraphQL SDL format) - a path to a
.jsonfile containing a schema’s introspection query data - a URL to a GraphQL API that can be introspected
{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "./schema.graphql" } ] }}{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "./introspection.json" } ] }}{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "http://localhost:4321/graphql" } ] }}{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": { "url": "http://localhost:4321/graphql", "headers": { "Accept": "application/graphql-response+json" } } } ] }}Step 3 — Configuring typings
Afterwards, @0no-co/graphqlsp is ready to also output a typings file for gql.tada.
The latter needs a type of an introspected GraphQL schema to infer types of
GraphQL documents automatically.
This is configured by providing an output location to @0no-co/graphqlsp in our tsconfig.json file.
In the plugin options we’ll update the tadaOutputLocation key.
{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "./schema.graphql",
"tadaOutputLocation": "./src/graphql-env.d.ts" } ] }}The tadaOutputLocation path can either be a .ts file, a .d.ts file, or
a folder, in which case a introspection.d.ts file will be created.
Once we start up our editor, @0no-co/graphqlsp will run and will create
the output file. In this example, we’ve created a src/graphql-env.d.ts file.
When opening this file we should see code that looks like the following:
declare const introspection: {10 collapsed lines
"__schema": { "queryType": { "name": "Query" }, "mutationType": null, "subscriptionType": null, "types": [ // ... ] }};
import * as gqlTada from 'gql.tada';
declare module 'gql.tada' { interface setupSchema { introspection: typeof introspection }}This file declares our schema’s introspection data in gql.tada. After this file
is created by @0no-co/graphqlsp automatically, gql.tada is set up project-wide
and is ready to be used.
Initializing gql.tada manually
Above, we let @0no-co/graphqlsp generate a src/graphql-env.d.ts file, which sets
gql.tada up project-wide for us.
This limits what we can do, since we can’t customize any scalars, or further configuration
for gql.tada. This setup also fails if we have multiple schemas (for example, in a monorepo),
since the declaration in graphql-env.d.ts sets a schema up project-wide.
To work around this, we may update @0no-co/graphqlsp’s configuration to instead
output a .ts file:
{ "compilerOptions": { "plugins": [ { "name": "@0no-co/graphqlsp", "schema": "./schema.graphql",
"tadaOutputLocation": "./src/introspection.ts" } ] }}When opening the project, this will now create a src/introspection.ts file that only exports our
introspection query data, which we can use to initialize gql.tada manually.
We’ll create a file that uses this introspection data and passes it to gql.tada:
import { initGraphQLTada } from 'gql.tada';import type { introspection } from '../introspection';
export const graphql = initGraphQLTada<{ introspection: typeof introspection;}>();
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';export { readFragment } from 'gql.tada';Instead of declaring our schema project-wide, we now have created a graphql function
that specifically uses the introspection.ts file that @0no-co/graphqlsp outputs
for us. Instead of importing graphql from gql.tada, we should now import it from our
custom src/graphql.ts file.
Customizing scalar types
Now that we’ve set up a src/graphql.ts file, which uses initGraphQLTada<>() to create
a custom graphql function, we may also use this function to customize our scalars.
By default, gql.tada will have types defined for the built-in scalars
in GraphQL. However, it won’t be able to know the serialized type of your custom scalars.
For instance, our schema may contain a DateTime scalar which, when queried, becomes
a string of new Date().toISOString(), however, gql.tada won’t know that this type
is a string.
import { initGraphQLTada } from 'gql.tada';import type { introspection } from '../introspection';
export const graphql = initGraphQLTada<{ introspection: typeof introspection;
scalars: { DateTime: string, JSON: any, },}>();
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';export { readFragment } from 'gql.tada';When using these scalars, they’ll now be mapped to the types in the scalars object type.