Getting Started โ
This short guide will take you from basic usage to use of the optional document builder. Node is used but Deno and Bun users should be able to follow along as well.
๐ก Setup Your Project โ
If you don't already have a project, create one:
pnpm init
We're going to use TypeScript for this project but you don't have to.
tsx
makes it to easy run TypeScript files.@tsconfig/strictest
is optional but makes sure we have a good default settings.
pnpm add --save-dev typescript tsx @tsconfig/strictest
touch tsconfig.json main.ts
{
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16"
}
}
๐ฆ Install Graffle โ
Graffle is still in development so there is no stable release to install yet. Use the next
distribution tag to find the latest pre-release.
Graffle has a peer dependency on graphql
so you will need to install that too.
pnpm add graffle@next graphql
๐ Verify Project Configuration โ
Graffle is an ESM only package built around package exports
. This imposes a few requirements on your project configuration.
- One of the following:
- Your project is using ESM (your
package.json
type
is set tomodule
) - Your CJS proejct uses
node@^20.17
with--experimental-require-module
ornode@^23
(where that flag is the default).
- Your project is using ESM (your
- If you are using TypeScript:
- If you are using TypeScript your
tsconfig.json
must setmodule
andmoduleResolution
toNode16
orBundler
. Otherwise TypeScript will not be able to find the types when you attempt to import entrypoints fromgraffle
. - Your TypeScript version must be
typescript@^4.9
.
- If you are using TypeScript your
- If you are using React Native:
- Do this to enable support for package
exports
.
- Do this to enable support for package
๐ Send Your First Document โ
Now you're ready to send your first GraphQL document. We'll use a publicly available GraphQL API (Thanks Trevor!). Create a Graffle instance and then use it to send a document.
// @filename: main.ts
import { Graffle } from 'graffle'
const graffle = Graffle.create({
schema: 'https://countries.trevorblades.com/graphql',
})
const data = await graffle.gql`
query myQuery ($filter: [String!]) {
countries (filter: { name: { in: $filter } }) {
name
continent {
name
}
}
}
`
.send({ filter: [`Canada`, `Germany`, `Japan`] })
console.log(data)
{
"countries": [
{ "name": "Canada", "continent": { "name": "North America" } },
{ "name": "Germany", "continent": { "name": "Europe" } },
{ "name": "Japan", "continent": { "name": "Asia" } }
]
}
๐ง Meet Document Builder โ
You could stop here if you want, but if you're curious about Graffle's document builder (aka. "query builder") continue on!
The document builder is an alternative to the GraphQL document syntax. With it, you express GraphQL documents in JavaScript. And thanks to Graffle's powerful types all inputs (GraphQL arguments) and outputs (execution results) are type safe โจ.
To access the document builder you must first perform a code generation step. When you installed graffle
you also gained access to a command line interface (CLI) in your project. Use it to generate code that will augment your client.
pnpm graffle --schema https://countries.trevorblades.com/graphql
You will see a directory named graffle
has been created in the current working directory. The generated code within augments a global TypeScript type that Graffle's static code knows about. The result is the illusion of new client methods available to you but in reality its actually just a Proxy instance that was there all along, dynamically receiving all the property requests. In other words you have just generated some code but nothing about the runtime has actually changed. Feel free to poke around at the files. Notice how the vast majority is just types (we can ignore the runtime parts for now).
|
|- graffle/
| |- ...
|
Let's rewrite our code to use the document builder. You should notice some new methods in your IDE like .document
and .query.countries
.
// todo twoslash
// @filename: main.ts
import { Graffle } from 'graffle'
const graffle = Graffle.create({
schema: 'https://countries.trevorblades.com/graphql',
})
const data = await graffle.document({
query: {
myQuery: {
countries: {
$: { filter: { name: { in: [`Canada`, `Germany`, `Japan`] } } },
name: true,
continent: {
name: true,
},
},
},
},
}).send()
console.log(data)
// ^?
{
"countries": [
{ "name": "Canada", "continent": { "name": "North America" } },
{ "name": "Germany", "continent": { "name": "Europe" } },
{ "name": "Japan", "continent": { "name": "Asia" } }
]
}
๐งน Simplify With Root Fields โ
The .document
method is 1:1 with the GraphQL document syntax in regards to what it can express. Graffle also gives you dedicated methods for each root field (fields on Query
, Mutation
, and Subscription
) which can be simpler depending on your use-case. Let's refactor our code with it.
// ...
const data = await graffle.document.query.countries({
$: { filter: { name: { in: [`Canada`, `Germany`, `Japan`] } } },
name: true,
continent: {
name: true,
},
})
// ...
๐งฐ Utilities โ
Graffle gives you utilities beyond the direct client itself. For example you can build up reusable selection sets:
import { Graffle } from './graffle/__.js'
const ContinentSelection = Graffle.Select.Continent({ name: true })
import { Select } from './graffle/_.js'
const ContinentSelection = Select.Continent({ name: true })
You can also do the same thing at the type level which can sometimes be handy when you want to define data types based on selection sets.
import { type Graffle } from './graffle/__.js'
type Continent = Graffle.Select.Continent<{ name: true }>
import { type Select } from './graffle/_.js'
type Continent = Select.Continent<{ name: true }>
๐๏ธ Conclusion โ
Hopefully you found this introduction has been useful. Graffle has a lot more to offer than shown so far such as extensions, anyware, custom scalar support, how every GraphQL query language feature is realized in the document builder, and more. Peruse these docs to learn about it all.
Thanks for taking time to learn Graffle. Let us know how your experience goes as your begin to build with it!