The QL compiler is a fun codegenerator for GraphQL clients. Specifically, it is capable of reading
.graphql
query, mutation, and fragment files and combining this with schema introspection JSON to
produce ad-hoc type definitions for TypeScript. It is similar to the tools
Apollo Tooling CLI and
GraphQL Code Generator, but smaller in scope
(and faster).
Say you have a query that looks like this:
query CommentQuery($id: ID!) {
comment(id: $id) {
author {
name
avatar: profilePictureUrl
}
content
}
}
If you are using TypeScript and a GraphQL client, it would be useful to get the type of this query.
You could write one out by hand (and then maintain this definition as the query changes). But since
GraphQL supports introspection and has a schema, we already know the type for the above! qlc
enables you to automate the codegen of the following types:
export type CommentQuery_comment_author = {
name: string;
avatar: string | null;
};
export type CommentQuery_comment = {
author: CommentQuery_comment_author;
content: string;
};
export type CommentQuery = {
comment: CommentQuery_comment;
};
export type CommentQueryVariables = {
id: string;
};
You can download some prebuilt binaries on the
releases page. You will need to build from source with
cargo
for other platforms.
For convenience, it is also available as an NPM package that supports x64/aarch64 MacOS and x64 linux:
yarn add @notarize/qlc-cli
qlc
will recursively scan directories, finding .graphql
files and producing .graphql.d.ts
files. By default, it starts at the working directory but you can optionally provide it a directory
argument. qlc
supports fragment imports with the #import "<file>.graphql"
syntax at the top of
your files; it supports both relative imports and absolute imports starting at the root directory
supplied to qlc
.
You will need to supply qlc
with the JSON result of the introspection query. Most, if not all,
GraphQL servers support producing this query result, and the canonical implementation can even be
found in the official graphql NPM package. See
this blog post
for more information. For simplicity, the NPM package comes with a helper script that should be
suitable for most users. See below.
# Download a schema JSON from an endpoint and write to my_schema.json
yarn run qlc-download-schema https://<FQDN>/graphql my_schema.json
# Run qlc searching the src/ directory with schema JSON located at my_schema.json
yarn run qlc -s my_schema.json src
# There are some other options available for more complex requirements.
yarn run qlc --help
Many of the options can also be configured through a camelcased JSON file (by default
.qlcrc.json
). For example:
{ "useCustomScalars": true, "numThreads": 2 }
qlc
outputs "typed" GraphQL document nodes so that clients can auto infer result and variable
types. Reference implementations are included in the @notarize/qlc-cli/typed-documentnode
module.
This requires the optional graphql
dependency. One can choose not to use this implementation by
overriding the --typed-graphql-documentnode-module-name
option. For example:
yarn run qlc --typed-graphql-documentnode-module-name 'my-typenode'
// In my-typenode.ts, something like:
export type QueryDocumentNode<Data, Vars> = unknown;
// more types for Mutation, Subscription, etc.
How much faster is "faster"? All results below are collected on MacOS, a 2.8 GHz quad-core machine
with an NVMe storage device, with the operating system's IO cache hot. The hyperfine
utility
measured runtime. The directory in question has 4523 files and 534 .graphql
files.
Command | Version | Mean Time ± σ | NPM Dependencies |
---|---|---|---|
qlc |
0.6.0 | 118.8 ms ± 10.8 ms | 1 (itself) |
apollo client:codegen --target=typescript |
2.31.1 (node 14.15.0) | 4.817 s ± 0.475 s | 355 |
To develop qlc, one needs a working cargo
, rustc
, node
, and yarn
installation. The repository
provides this via a Nix shell (shell.nix
) for ease. node
and yarn
are used for some tasks, like
packaging the NPM release (see pkg/npm
), as well as creating a mock schema JSON for testing.
Here are a number of reminders for useful commands, most of which also are executed in CI:
# If using the nix shell, the `just` command runner can be utilized to list recipes
just
# Static tools
just lint
just format
# Benchmarking on a `src` directory (using hyperfine)
hyperfine --warmup 2 -p 'find src -name "*.graphql.d.ts" -type f -exec rm {} +' '../qlc/target/release/qlc src'
# Testing
## Test Setup
just build-test-schema
## Run all tests
just test
## Run matching test
just test union_with_typename
## Instruct cargo test not to capture stdout/stderr so that one can see `dbg!()` output, etc.
just test -- --nocapture
## Instruct the test harness not to delete temporary directories created during testing for debugging
KEEP_TEST_TEMPDIRS=t just test
## Instruct the test harness overwrite expected fixtures with actual output -- useful for large swath compiler output changes
## Warning: will change repo files on disk
OVERWRITE_FIXTURES=t just test