Nov 5th, 2021 - written by Kimserey with .
Last week we looked at how to setup an Apollo server to serve graphql requests. We went through the setup by defining a TemplateStringArray
for the schema and defining functions in an object map for the resolvers. In this week post, we will look at how to simplify this two areas by leveraging Typescript features using typegraphql
package.
We start from the Apollo server created in last week post - Get Started With Apollo Server. From there we can add our new dependencies:
1
npm i class-validator type-graphql reflect-metadata
class-validator
is a dependency of typegraphl
and reflect-metadata
is a shim required for type reflection. We also import reflect-metadata
at the top of our entrypoint index.ts
:
1
import "reflect-metadata";
Then we make sure that our tsconfig.json
file has the following settings:
1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018", "esnext.asynciterable"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Those settings must be set correctly for typegraphql
to work properly.
Now that we have the package installed properly, we can start by moving our schema to a typed schema rather than a string
. This is what we started with:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const typeDefs = gql`
type Person {
id: ID!
name: String!
}
type Book {
title: String
author: Person
}
type Query {
books: [Book]
}
`;
We can see that we have three types, Person
, Book
and Query
. We will leave Query
aside first and define Person
and Book
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ObjectType()
class Person {
@Field(() => ID)
id: string;
@Field(() => ID)
name: string;
}
@ObjectType()
class Book {
@Field(() => String)
title: string;
@Field(() => Person, { nullable: true })
author: Person;
}
As we can see, we are able to create types that mirror our schema. We use ObjectType
decorator to indicate to typegraphql that this class is a type for graphql and Field
to specify the field and the type of the field. ID
is a special value from typegraphql which indicates an opaque value.
Next we can update our resolvers; which previously looked as such:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const resolvers = {
Query: {
books: () => [
{
title: "Made of Wolves",
authorId: "1",
},
{
title: "The Visitor in the City",
authorId: "2",
},
],
},
Book: {
author: (parent: any) => {
return {
id: parent.authorId,
name: parent.authorId == "1" ? "James Carter" : "Arthur Novotic",
};
},
},
};
We had a Query
resolver with books
attribute and a Book
resolver with author
attribute. Those can be defined with typegraphql as such:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Resolver((_of) => Book)
class BookResolver {
@FieldResolver()
author(@Root() book: Book): Person {
return {
id: book.authorId,
name: book.authorId == "1" ? "James Carter" : "Arthur Novotic",
};
}
@Query((_returns) => [Book])
books(): Book[] {
return [
{
title: "Made of Wolves",
authorId: "1",
},
{
title: "The Visitor in the City",
authorId: "2",
},
];
}
}
We can see that we use @Resolver
decorator to define a class as the book resolver. The query is then defined using the @Query
decorator and the Book.author
field is defined using the @FieldResolver
decorator. @Root
represents the first parent
argument given to the resolver, the other argument can be retrieved using @Ctx
or @Arg
.
Lastly we can build the schema using buildSchema
which we import from type-graphql
:
1
2
3
4
5
6
7
8
9
10
11
async function main() {
const schema = await buildSchema({
resolvers: [BookResolver],
});
const server = new ApolloServer({ schema });
await server.listen(4000);
console.log("Server started on http://localhost:4000");
}
main();
buildSchema
will translate all the queries and mutations together with associated types link into the resolvers. And once we start our server, we can see our graphql schema as expected! And that concludes today’s post!
Today we saw how we could create a graphql schema in Typescript with classes using type-graphql
. We started from last week project which was implementing barebone graphql schema and resolvers with Apollo Server, and translated the schema into Typescript classes and resolvers into resolver classes. We then finished the post by building the schema and testing the resulting schema. I hope you liked this post and I see you on the next one!