Using GraphQL in TypeScript projects
After installing nitrogql to your TypeScript project, you can start using GraphQL in your project. This page guides you how to use GraphQL with nitrogql.
๐งโ๐ซ Currently, this guide assumes that you are already familiar with GraphQL.
๐ก Check out our examples for a working example.
Writing your schema
nitrogql is a tool for the schema-first approach to GraphQL. This means that you write your schema first to define your GraphQL API.
Place your schema files in the directory specified by the schema
field in the configuration file.
# ./schema/todo.graphql
"One todo item."
type Todo {
"ID of this todo item."
id: ID!
"Contents of this todo item."
body: String!
"When not null, date when this item was marked done."
finishedAt: Date
"Date when this item was created."
createdAt: Date!
}
Writing operations
Once you have your schema, you can write GraphQL operations. Place your operation files in the directory specified by the documents
field in the configuration file.
nitrogql recommends having separate operation files (.graphql
) instead of embedding them in your code. This is the only way of writing GraphQL operations in nitrogql.
# ./app/getTodos.graphql
query getTodos {
todos {
id
body
createdAt
finishedAt
}
}
Statically checking GraphQL files
To check GraphQL files, run the following command in your project directory:
npx nitrogql check
๐ก Note: nitrogql CLI looks for the configuration file in the current directory by default. To specify the location of the configuration file, use the --config-file
option.
If all GraphQL files are valid, the command will exit with code 0. Otherwise, it will print errors and exit with code 1.
Generating TypeScript files
nitrogql generates several TypeScript files from GraphQL files:
Schema type definition file is configured by the generate.schemaOutput
option. It is a single .d.ts
file that contains type definitions for all types in the schema, so it must be generated.
Resolver type definition file is configured by the generate.resolversOutput
option. It is a single .d.ts
file that contains type definitions for all resolvers. It is optional, and only generated when you specify the option. Resolver types are useful when you are developing a GraphQL server.
Server GraphQL schema file is configured by the generate.serverGraphqlOutput
option. It is a .ts
file that exports the entire GraphQL schema as a single string (in SDL format). It can be used at runtime to initialize a GraphQL server without having to manually load and concatenate all schema files. It is optional, and only generated when you specify the option.
Operation type definition files are generated for each operation file. They are always emitted as long as you configure nitrogql to load operation files (via the documents
option). By default, operation type definition files are .d.graphql.ts
files and are placed next to each operation file.
๐ก Note: .d.graphql.ts
files are supported by TypeScript 5.0 or later. If you are using TypeScript 4.x, you need to configure nitrogql to generate .d.ts
files instead. See Configuration for details.
To generate these files, you need to specify the location of generated files in the configuration file:
schema: ./schema/*.graphql
documents:
extensions:
nitrogql:
generate:
# Specify the location of generated schema file.
schemaOutput: ./src/generated/schema.d.ts
# Specify the location of generated resolver file.
resolversOutput: ./src/generated/resolvers.d.ts
# Specify the location of generated server GraphQL schema file.
serverGraphqlOutput: ./src/generated/graphql.ts
There is no option to specify the location of generated types for operation files; they are always placed next to operation files.
Then, run the following command in your project directory:
npx nitrogql generate
๐ก Note: the generate
command implies check
. If there are errors in GraphQL files, the command fails and does not generate any files.
After a successful run, you will see generated files in the specified locations.
Using generated types from client code
With the help of the generated operation type definition files, nitrogql allows you to use GraphQL operations type-safely in your TypeScript code. To use them, you directly import .graphql
files in your code.
However, probably you need to adjust your tsconfig.json
so that TypeScript allows importing .graphql
files.
{
"compilerOptions": {
// ...
+ "allowArbitraryExtensions": true,
// ...
}
}
After configuring TypeScript correctly, it's time to import .graphql
files. With default settings, these files export a TypedDocumentNode
object as a default export. You can use it with any GraphQL client library. Below is an example with Apollo Client and React:
import { useQuery } from "@apollo/client";
import getTodosQuery from "./getTodos.graphql";
export function SampleComponent() {
const { data } = useQuery(getTodosQuery);
return <ul>{
data?.todos.map(
(todo) => <li key={todo.id}>{todo.body}</li>
)
}</ul>;
}
In this example getTodos.graphql
is an operation file that contains a GraphQL query (query getTodos { ... }
). By passing the exported query object to useQuery
, you can execute the query.
Of course this is type-safe. The type of the query result, data
, precisely matches the shape of the query. This means that if your code tries to access a field that does not exist in the schema, or is not fetched by the operation, TypeScript will report an error.
Using generated types from server code
In the schema-first approach to GraphQL, you develop a GraphQL server so that it implements the schema you wrote. Typically this is done by writing resolvers. The generated resolver type definition file helps you write resolvers in a type-safe manner.
๐ฐ Below guide uses Apollo Server as an example, but you can use any GraphQL server library.
With nitrogql, the basic setup of a GraphQL server will look like:
import { ApolloServer } from "@apollo/server";
// server GraphQL schema file
import { schema } from "@/app/generated/graphql";
// resolver types
import { Resolvers } from "@/app/generated/resolvers";
// Context is an object that is passed to all resolvers.
// It is created per request.
type Context = {};
// define all resolvers.
const resolvers: Resolvers<Context> = {
Query: {
todos: async () => { /* ... */ }
},
Mutation: {
toggleTodos: async (_, variables) => { /* ... */ }
},
// ...
};
const server = new ApolloServer({
typeDefs: schema,
resolvers,
});
// ...
Of course, you can use any TypeScript technique you know to organize/structure your code. For example, you might want to define Query resolvers and Mutation resolvers separately:
const queryResolvers: Resolvers<Context>["Query"] = {
// ...
};
const mutationResolvers: Resolvers<Context>["Mutation"] = {
// ...
};
const resolvers = {
Query: queryResolvers,
Mutation: mutationResolvers,
// ...
};
However, at this step the generated resolver type definition is not practically usable because it does not allow use of default resolvers. This means that you need to define resolvers for every single field of every object type in the schema. This isn't what you usually do.
To mitigate this problem, nitrogql provides the nitrogql:model
plugin. This plugin allows you to use a @model
directive in your schema to mark a field as included in the model. Fields with this directive are to be resolved by default resolvers, so you don't need to define resolvers for them.
This may not be something familiar to you, but it is needed for making it practical to write resolvers while maintaining the perfect type safety.
For details about the nitrogql:model
plugin, see nitrogql:model
plugin.
Watching and generating types automatically
It is tedious to run generate
command every time you change GraphQL files. Unfortunately, nitrogql does not provide a built-in way to watch GraphQL files and generate types automatically. However, you can use chokidar-cli or similar tools to watch GraphQL files and run generate
command automatically:
chokidar '**/*.graphql' --initial --command 'npx nitrogql generate'
Alternatively, you can use Run on Save VSCode extension to run generate
command automatically when you save a GraphQL file. Example configuration:
{
"emeraldwalk.runonsave": {
"commands": [
{
"match": "\\.graphql$",
"cmd": "npx nitrogql generate"
}
]
}
}
๐งบ Read Next: Configuration, CLI Usage