GraphQL is a powerful query language that allows you to build flexible APIs. It lets you define a type system for your data, so when you execute a query, it returns only the data you need.
GraphQL can offer a better developer experience when used with TypeScript because they are both typed language. TypeScript is a typed superset of JavaScript that extends it by adding types. So, using these technologies together will certainly help you to build predictable and strongly-types APIs.
In this tutorial, I will first explain why combining such technologies, and then show you how to use TypeScript with GraphQL by building an API from scratch using TypeGraphQL.
Prerequisites
This tutorial assumes that you have some experience using TypeScript, particularly TypeScript classes and decorators. Knowledge of GraphQL will come in handy but is not mandatory.
In this guide, we will be using TypeGraphQL, which is a modern framework for building GraphQL API using Node.js and TypeScript.
Why use TypeScript with GraphQL
TypeScript is a popular programming language developed and maintained by Microsoft. It is a superset of JavaScript that uses static type-checking to make your code predictable.
Over the years, TypeScript has proven to be a valuable language for large codebases. TypeScript enhances code quality with its types, which adds many benefits, such as robustness, understandability, and predictability.
GraphQL solves the problem of over-fetching or under-fetching APIs. It provides one single endpoint for all requests using a Post
method to get exactly the data you need, nothing more and nothing less. In this way, GraphQL makes your queries flexible, and your API readable and easy to learn.
TypeScript and GraphQL both rely on types to make your code understandable. However, GraphQL types can only be defined in a GraphQL schema using the method buildSchema
or a file with .gql
extension. The GraphQL types are not supported on GraphQL resolvers because resolvers are just regular JavaScript functions, not GraphQL code. TypeScript solves this issue because, as we mentioned earlier, it’s a superset of JavaScript. So, It can set types on the GraphQL resolvers. This is the reason why using TypeScript along with GraphQL makes sense.
GraphQL handles the types for the GraphQL schemas, and TypeScript sets the types on the GraphQL resolvers. However, because you are handling multiple languages, building strongly-typed APIs using Node.js, GraphQL, and TypeScript can be a challenge to maintain.
Maintaining consistency between your schema and resolvers is what TypeGraphQL intends to solve. TypeGraphQL allows you to use TypeScript classes and decorators to create the schema, types, and resolvers of your API. It uses TypeScript to build the entire GraphQL API.
illustration
So far, we have learned why pairing TypeScript with GraphQL can be useful and why TypeGraphQL is handy for building and maintaining GraphQL APIs that use TypeScript.
Without further ado, let’s dive into the practice part and build up the GraphQL API using TypeGraphQL.
Setting up
To use TypeScript and GraphQL, we first need to create a new Node.js app.
Open your command-line interface (CLI) and run this command:
yarn init
Or for npm
npm init
You’ll need to respond to a few configuration questions which will emit a package.json
file. Next, install the dependencies needed for this tutorial.
yarn add express apollo-server-express graphql reflect-metadata type-graphql class-validator
For npm
npm install express apollo-server-express graphql reflect-metadata type-graphql class-validator
We will break these packages down later and explain what they do. For now, let’s install their types so TypeScript can understand the libraries.
yarn add -D @types/express @types/node nodemon
Or
npm install -D @types/express @types/node nodemon
Note that we installed nodemon
as well to enable live-reloading whenever a change occurs.
Here’s what each of the installed libraries do:
-
express
is a minimalist web framework for Node.js -
apollo-server-express
is a middleware that allows usingexpress
in an Apollo GraphQL server. -
reflect-metadata
enables TypeScript decorators to add the ability to augment a class and its members when the class is defined. It’s a dependency of TypeGraphQL. -
class-validator
allows TypeGraphQL to use decorator and non-decorator based validation.
Next, we need to structure the project as follows:
src
| ├── resolvers
| | └── todoResolver.ts
| └── schemas
| | └── Todo.ts
| └── index.ts
├── tsconfig.json
├── package.json
└── yarn.lock
Here, there are four files to underline:
- The entry point of the server (
index.ts
). - The
schemas
folder that contains the GraphQL Schema of the project. - The
resolvers
folder that holds the resolvers of the API. - The
tsconfig.json
file tells to TypeScript how to compile the code.
With this in place, we can now add a script to start the server in the package.json
file.
"scripts": {
"start": "nodemon --exec ts-node src/index.ts"
}
This script will start the server using nodemon
. And whenever our code is updated, it will restart.
Let’s now configure the tsconfig.json
.
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
These two properties should be set to true
to be able to use TypeScript decorators in the project.
We can now build a GraphQL Schema for the API.
Build the GraphQL Schema
TypeGraphQL allows you to build a schema using TypeScript classes and decorators. It’s just syntactic sugar because under the hood TypeGraphQL will still generate regular GraphQL code. We will see the code generated later - for now, let’s create the schema.
-
schemas/Todo.ts
import { Field, ObjectType, InputType } from ‘type-graphql’
@ObjectType() export class Todo {
@Field() id: number
@Field() title: string
@Field() description: string
@Field() status: boolean
}
@InputType() export class TodoInput implements Partial {
@Field() title: string
@Field() description: string
}
At the first the syntax might look weird, however, it’s relatively simple to understand. It’s just TypeScript decorators and classes.
Here, the @ObjectType()
provided by TypeGraphQL enables creating a new object or schema. The Todo
class reflects the shape of a Todo object, and the TodoInput
defines the expected data for adding a new Todo.
Now, let’s write the same code using GraphQL.
type Todo {
id: ID!
title: String!
description: String!
status: Boolean!
}
input TodoInput {
title: String!
description: String!
}
As you can see, the logic is the same. The only difference is that here, we don’t use TypeScript.
Now we’re ready to create our GraphQL resolvers.
Create the GraphQL resolver
Unlike GraphQL, TypeGraphQL puts the GraphQL query or mutation in the resolvers. The name of the function will be used as an endpoint when querying or mutating data.
-
resolvers/todoResolver.ts
import { Query, Resolver, Mutation, Arg } from ‘type-graphql’;
import { Todo, TodoInput } from ‘../schemas/Todo’;
@Resolver((of) => Todo) export class TodoResolver {
private todos: Todo[] = []
@Query((returns) => [Todo], { nullable: true })
async getTodos(): Promise<Todo[]> {
return await this.todos
}
@Mutation((returns) => Todo)
async addTodo(
@Arg('todoInput') { title, description }: TodoInput
): Promise<Todo> {
const todo = {
id: Math.random(), // not really unique
title,
description,
status: false,
}
await this.todos.push(todo)
return todo
}
}
Here, we use the Resolver
decorator to create a new GraphQL resolver that returns a Todo. Next, we build a GraphQL query to fetch all Todos.
After that, we define a mutation query that expects a title
, and a description
to add a new Todo on the array of data.
By the way, you don’t need to use async/await here, because this won’t take time to complete. But, I add it here for reference when you need to deal with a real server.
Let’s now convert the code to GraphQL.
type Mutation {
addTodo(todoInput: TodoInput!): Todo!
}
type Query {
getTodos: [Todo!]
}
With this in place, we can build the server that uses the schema and resolver we’ve just created.
Create the Server
-
src/index.ts
import ‘reflect-metadata’;
import { ApolloServer } from ‘apollo-server-express’;
import * as Express from ‘express’ import { buildSchema } from ‘type-graphql’;
import { TodoResolver } from ‘./resolvers/todoResolver’;
async function main() { const schema = await buildSchema({ resolvers: [TodoResolver], emitSchemaFile: true, })
const app = Express()
const server = new ApolloServer({
schema,
})
server.applyMiddleware({ app })
app.listen(4000, () =>
console.log('Server is running on http://localhost:4000/graphql')
)
}
main()
As you can see here, we import TodoResolver
, which needs to be passed as a resolver to the buildSchema
method. With that, TypeGraphQL can build a new GraphQL Schema based on the Todo resolver.
Next, we pass the schema
object (it contains the GraphQL schemas and resolvers) to Apollo to create the server.
Setting the property emitSchemaFile: true
allows TypeGraphQL to generate a schema.gql
file at build-time.
Let’s check if the app works. Run the following command:
yarn start
Or
npm start
Visit http://localhost:4000/graphql
, and then add this code block below to GraphQL Playground to create a new Todo.
mutation {
addTodo(todoInput: { title: "Todo 1", description: "This is my todo" }) {
title
description
status
}
}
The Todo object should be created successfully!
todo-created
Now query for the newly created Todo using the following GraphQL query.
{
getTodos {
title
description
status
}
}
You should see that all Todos have been returned.
all-todos
Great! Our app looks good.
We have now finished building a GraphQL API using TypeScript.
You can find the finished project in this Github repo
Thanks for reading
GraphQL in TakeShape
TakeShape provides a flexible GraphQL API to manage your content easily. It gives you the ability to immediately see how changes to your content model will impact your API using the API Explorer. You don’t have to build any backend on your own, everything is set up for you. TakeShape automatically generates a secure GraphQL API to expose all of your content and services.
Next steps
Check out these resources to dive deeper into the content of this tutorial: