Type-Safe GraphQL Servers 🔎
This post exhibits the motivations & steps for adding Type-Safety into GraphQL Servers 🎆
Why should I add type-safety to my codebase?
Types enable developers to use highly-productive development tools and practices like static checking and code refactoring when developing JavaScript projects.
Building large scale applications, a static type checker can help safeguard your program, as well as helping you to eliminate existing errors (typos, misspellings, etc). TypeScript and Flow are both fantastic ways to limit technical debt in your JavaScript codebases, because folks,
JavaScript is DIFFICULT 🔥
At the same time it might be the best programming language to start with, since it doesn’t require to install and configure, but it doesn’t mean that it’s really hard to master and definitively, error prone!
JavaScript being weakly typed (in addition to being dynamically typed) poses a lot of problems. Note that a language is either statically or dynamically typed, let’s compare the differences between them.
Static 💥 Dynamic Typed
Static
A language is statically typed if the type of a variable is known at compile-time. For some languages this means that you as programmer must specify what type each variable is (e.g.: Java, C, C++).
Dynamic
A language is dynamically typed if the type is associated with run-time values. This means that you as programmer can write a little quicker because you do not have to specify types every time (e.g.: JavaScript, Python, Perl)
Remember❗️
Static 👉 Compile-Time 👉 Java
Dynamic 👉 Run-Time 👉 JavaScript
Speaking of these languages, in order to clarify their similarities, I’ve gotta mention one of my favorite JavaScript quotes written at the hands of the author of You Don’t Know JS saga (which I 💯 recommend to read):
“JavaScript is as related to Java as Carnival is to Car.” — Kyle Simpson
Definitely, they do not look alike. Let’s put focus on the most popular dynamically typed language…
Note that a language can be dynamically typed but also strongly typed, thus producing a much better language. How we could do that in JavaScript?
Type-Safety in JavaScript
Let’s review the most known tools for adding static typing into our JS codebases!
TypeScript
Typed superset of JavaScript that compiles to plain JavaScript on any browser, any host & any OS.
Pros & Cons
👌 Community & Tooling
👍 Sub-second response times
👎 Learning curve
Flow
Static type checker for your JavaScript code. It does a lot of work to make you more productive, making you code faster, smarter, more confidently, and to a bigger scale.
Pros & Cons
👌 Out-of-the-box utility
👍 Zero config
👎 Slower and buggier
TypeScript vs Flow
No matter if you are using TypeScript or Flow be aware of you’re not writing plain JavaScript. TypeScript implements both a type checker and a transpiler that emits plain JavaScript. Flow only does type checking and relies on Babel (or other tools) to remove type annotations.
If you’d like to know more about the differences regarding syntax and usability, I totally recommend to read this comparison!
In this post we will use TypeScript in our examples, it has better support for type generation that we’ll need later on.
GraphQL as API
GraphQL is well know for being a strongly-typed specification for building APIs, it comes with a lot of benefits and we should take advantage of them.
Whether you’re following a Schema-First or Resolver-first approach, you’d love to end up having a Single Source of Truth with which develop an End-to-end Type-Safe Application.
Avoiding manual static typing because it can get too time-consuming as well as hard to keep in sync through versions, we might want to auto-generate all our types based on that Single Source of Truth.
In this post we’ll use the Schema-First approach, thus the static types will be generated based on our GraphQL Schema instead of from our resolvers!
In order to remove friction between type definitions we might introduce automation. Here is where the GraphQL Types Generators come in!
GraphQL Types Generators
Let’s review the most known tools for auto generating types based on our GraphQL Schema.
GraphQL Code Generator
Tool that generates different types code outputs of your GraphQL Schema & Documents whether you are developing client, server or database.
Pros & Cons
👌 Client, Server & DB support
👍 Docs & Community
👎 Early stage of development
GraphQLGen
Generate & scaffold type-safe resolvers based on your GraphQL Schema in TypeScript, Flow & Reason.
Pros & Cons
👌 Defaults resolvers generation
👍 Integrable with Prisma
👎 Only Server support
GraphQL Code Generator vs GraphQLGen
Both are great for generating TypeScript & Flow types although GraphQLGen doesn’t provide you the ability to generate them for the Client. Instead, GraphQL Code Generator does, also it provides you a tons of plugins/configurations & the possibility to create your custom type generation templates. I found out this really useful in order to have a modular GraphQL Schema configured as you wish!
In this post we will use GraphQL Code Generator in our examples, it has better customization that we’ll need later on.
Ok, I know, too much theory…
… let’s set this up 🚀
Setup TypeScript
Install it
yarn add -D typescript
Install Run libraries
In order to execute TypeScript with Node, you have to add the following tools:
TS-Node: TypeScript execution and REPL for Node.js, with source map support.
Nodemon: Monitor for any changes in your Node.js application restarting automatically the server.
yarn add -D ts-node nodemon
You might want to use other configuration, setup whatever fits better for your app!
Include @types
Add your high quality type definitions of the libraries that you’re currently using over your codebase, such as graphql
, lodash
, etc. If you’re 🤨 about this, check the DefinitelyTyped docs!
yarn add -D @types/graphql
Create TypeScript config
This way TypeScript will include all the .ts
files in your directory (and sub directories) as a part of the compilation context. It will also select a few sane default compiler options, you could check them out here!
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
Create Run config
Nodemon supports local configuration files located in the current working directory where you have to specify things like which path to watch & the command to execute after any reload, which is your server. Check the sample config!
// nodemon.json
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts", "src/types/**/*.d.ts"],
"exec": "ts-node ./src/index.ts"
}
Add Run config
// package.json
{
...
"scripts": {
...
"start": "nodemon",
...
}
...
}
Rename .js files 👉 .ts
Modify all your JavaScript filename extensions to .ts
, it’ll allow you take advantage of the all TypeScript features
Test your app
Run your GraphQL Server, as JavaScript is 100% compatible with TypeScript (not the opposite, TS types are not allowed on JS compilers), it should work!
yarn start
Weeding out Errors
It’s not unexpected to get error messages after conversion. The important thing is to actually go one by one through these and decide how to deal with the errors. Often these will be legitimate bugs, but sometimes you’ll have to explain what you’re trying to do a little better to TypeScript. Don’t hesitate to check this migration guide for help to fix the possible errors!
Setup GraphQL Code Generator
Install it
yarn add -D graphql-code-generator
Install Plugins
We’ll setup the common, server & resolvers TypeScript plugins, you might want to use others, check the plugins available!
yarn add -D graphql-codegen-typescript-common graphql-codegen-typescript-server graphql-codegen-typescript-resolvers
Create Context type
If you wish to use a custom type for your GraphQL context and you don’t want to specify it each time that you declare your resolvers, you can create it manually and add it to the run config file (as we’ll do it in the next step):
// types/context.d.ts
export type MyContext = {
db: any;
find: any;
limit: any;
order: any;
project: any;
sort: any;
};
Create Run config
The CLI will automatically detect the defined config file and will generate code accordingly, you could check the available options here!
// codegen.yml
schema: src/schema/**/*.ts
overwrite: true
watch: true
require:
- ts-node/register
generates:
./src/types/types.d.ts:
config:
contextType: ./context#MyContext
plugins:
- typescript-common
- typescript-server
- typescript-resolvers
Add Run config
// package.json
{
...
"scripts": {
...
"generate": "gql-gen",
...
}
...
}
Generate Types ⚙️
Everything is setup for getting our desired static types 😍, let’s make that happen!
yarn generate
Add Resolver Types!
Now, you just have to include your TypeScript generated static types into your GraphQL Resolvers as here
Recapping
In order to add Type-Safety into your GraphQL Server using Schema-First approach you could:
⚫ Setup TypeScript
↳ Install TypeScript, run libraries & @types
↳ Create TypeScript config & run config
↳ Add run config
⚫️ Rename .js 👉.ts files
⚫️ Setup GraphQL Code Generator
↳ Install GraphQL Code Generator & plugins
↳ Create context type & run config
↳ Add run config
⚫️ Generate Types ⚙️
⚫️ Add Resolver Types!
👀 the benefits
Discover some of the use cases where you’d take advantage of using TypeScript together with GraphQL!
Evolve your schema
🔎 the arguments
Catch Misspellings
🔎 the context
Discover your data
The Future
In order to talk about what the new era holds 😬, you cannot miss the motivations for which GraphQL was created, Dan Schafer resumed them perfectly 👇
Schema💥Resolver First approach,
Types💥Classes/Decorator generators,
Readability💥Composability,
… 2019 aims to be busy for GraphQL 😅,
read the first impressions about its future,
it’s as exciting as overwhelming,
but really, don’t miss it, 💯 thread!
I’ll be following this post up 🔜 with the respective Type-Safe GraphQL Clients 🔎 post, stay tuned!
Please, consider🙏🏻ing, contribut♻️ing and shar💜ing it!