This tutorial is specially designed for those people who try to learn new concepts with the help of programming without going too much into the theory.
It is used to build graphql API’s with node and typeScript. It helps to define schema directly from our typescript code.
In this tutorial, we will cover the following topics
- Type-graphql setup
- Detail schema and resolver overview
- Relations in graphql
General steps to set up the type-graphql
- Create Express server
- Create a Resolver with schema
- Build Schema by using
buildSchema
function fromtype-graphql
- Create an apollo server by passing schema in the apollo server
- Start the apollo server before the express server
Step 1
Final Code is attached at the end of the article but you can download the basic code setup with all of the necessary dependencies.
https://github.com/mahmedyoutube/typeorm-with-typegraphql/tree/basic-project-setup
Step 2
Now we are creating the user schema and resolver. Creating a schema in type-graphql is very easy just like type-orm
@ObjectType()
creates a new Schema in graphql@Field()
creates a new field in a schema and if you want the field optional then you can pass{ nullable: false }
in Field function
Don’t worry if you find difficulty to grasp this concept, I will explain it again in next section.
Simple type-graphql schema code example
import { Field, ObjectType } from "type-graphql";
@ObjectType()
export class UserSchema {
@Field()
id: number;
@Field()
createdAt: Date;
@Field()
updatedAt: Date;
@Field()
firstName: string;
@Field()
lastName: string;
@Field()
email: string;
@Field()
password: string;
@Field()
phoneNumber?: string;
}
Simple type-graphql resolver code example
- @Query() is used to create a Query resolver. Its first param defines what will be the return type. In the second param, you can pass many optional params but by passing
{nullable:true}
means you are expecting a null value from the query function
import { Query, Resolver } from "type-graphql";
import { UserSchema as User} from "../../schema/UserSchema";
@Resolver(() => User)
export class UserResolver {
@Query(() => [User], { nullable: true })
async users(): Promise<User[] | null> {
return [];
}
}
Step 3
Now we will buildSchema by using buildSchema
type-graphql function.
await buildSchema({
resolvers: [UserResolver],
validate: { forbidUnknownValues: false },
nullableByDefault: false, // by default all fields are not optional
});
Step 4
Basic code to set up the apollo server. First, build the schema and then pass it to graphql.
const schema = await buildSchema({
resolvers: [UserResolver],
validate: { forbidUnknownValues: false },
nullableByDefault: false, // by default all fields are not optional
});
const apolloServer = new ApolloServer({
schema,
context: ({ req }) => {
return {
req,
};
},
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
Complete Code Example with schema and apollo server. This example is using the class-based approach. You can convert it into a functional-based approach
import { NonEmptyArray, buildSchema } from "type-graphql";
import { GraphQLSchema } from "graphql";
import { UserResolver } from "./resolvers/UserResolver";
import { ApolloServer } from "apollo-server-express";
import { Express } from "express";
class GraphQl {
private _schema: GraphQLSchema;
private resolvers(): NonEmptyArray<Function> | NonEmptyArray<string> {
return [UserResolver];
}
async buildGraphQLSchema() {
this._schema = await buildSchema({
resolvers: this.resolvers(),
validate: { forbidUnknownValues: false },
nullableByDefault: false, // by default all fields are not optional
});
}
async startApolloServer(app: Express) {
await this.buildGraphQLSchema();
const apolloServer = new ApolloServer({
schema: this.schema,
context: ({ req }) => {
return {
req,
};
},
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
}
get schema() {
return this._schema!;
}
}
const graphQl = new GraphQl();
export default graphQl;
Step 5
Main Server file
import express from "express";
import { db } from "./database/db";
import graphQl from "./graphql";
const app = express();
db.connect();
app.use(express.json());
graphQl.startApolloServer(app);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(
`server is listening on the port ${PORT} graphql: http://localhost:${PORT}/graphql`
);
});
Complete Code to setup type-graphql in express
GitHub — mahmedyoutube/typeorm-with-typegraphql at type-graphql-project-setup
Detailed overview of the schema and resolvers in graphql
Schema
It describes the shape of your available data.
How you can define a schema in type-graphql?
You can follow the following steps to define the schema in type-graphql
- Create a Class
- Add
@ObjectType()
decorator at the top of the class - Add fields in a class with @Field() decorator
Example
import { Field, ObjectType } from "type-graphql";
@ObjectType()
export class UserSchema {
@Field()
id: number;
@Field()
createdAt: Date;
@Field()
updatedAt: Date;
@Field()
firstName: string;
@Field()
lastName: string;
@Field()
email: string;
@Field()
password: string;
@Field()
phoneNumber?: string;
}
Resolvers
It is a function that resolves a value for a type or field within a schema
How you can define a resolver in type-graphql?
You can follow the following steps to define the schema in type-graphql
- Create a Class
- Add
@Resolver(() => YourSchemaClassName)
decorator at the top of the class - To build a query resolver, add
@Query(() => YourSchemaClassName)
decorator at the top of the function. - Same way for mutation just like query resolver. But in mutation, you need to understand some other concepts also like adding @Arg() in param so type-graphql can know that you are accepting the particular argument in your mutation function
Examples
- Basic Query resolver example
import { Query, Resolver } from "type-graphql";
import { UserSchema as User} from "../../schema/UserSchema";
@Resolver(() => User)
export class UserResolver {
//[User] mean return result will consist of array of users, or you can imagine like typescript user[]
@Query(() => [User], { nullable: true }) // nullable mean your query can return null
async users(): Promise<User[] | null> {
return [];
}
}
2. Basic Mutation example
import { Arg, Mutation, Query, Resolver } from "type-graphql";
import { UserSchema as User } from "../../schema/UserSchema";
@Resolver(() => User)
export class UserResolver {
@Mutation(() => User)
async createUser(@Arg("firstName") firstName: string) {
console.log("firstName ", firstName);
}
}
If you want to pass multiple arguments in mutation function, then it is preferable to create a new
ArgsType
class
example
ArgsType class example
import { ArgsType, Field } from "type-graphql";
@ArgsType()
export class CreateNewUserArgs{
@Field({ nullable: false })
firstName: string;
@Field({ nullable: false })
lastName: string;
@Field((type) => String, { nullable: false })
email: string;
@Field({ nullable: false })
password: string;
@Field({ nullable: true })
phoneNumber?: string;
}
Mutation example with ArgsType
import { Arg, Args, Mutation, Query, Resolver } from "type-graphql";
import { UserSchema as User } from "../../schema/UserSchema";
import { CreateNewUserArgs } from "./types";
@Resolver(() => User)
export class UserResolver {
@Mutation(() => User)
async createUser(
@Args()
{ firstName, lastName, email, password, phoneNumber }: CreateNewUserArgs
) {
console.log(
"firstName ",
firstName,
lastName,
email,
password,
phoneNumber
);
}
}
Relations in type-graphql on the schema level
Relations example on the schema level. You can pass the arrow function in @Field()
decorator to define a relation between schemas
Example
Add the below code at the end of the UserSchema
@Field((type) => [Product], { nullable: true })
products?: Product[];
Complete ProductSchema code with relation
@ObjectType()
export class ProductSchema {
@Field()
id: number;
@Field()
createdAt: Date;
@Field()
updatedAt: Date;
@Field()
productPic: string;
@Field()
productDescription: string;
@Field()
price: number;
@Field(() => User)
user: User;
}
Relations in type-graphql on resolver level
Just like query and mutation, you can define FieldResolver at the top of the function with return type and optional param
UserResolver example
@FieldResolver(() => [Product], { nullable: true })
async products(@Root() user: User) {
return [];
}
Complete type-graphql code link
GitHub – mahmedyoutube/typeorm-with-typegraphql at typegraphql-code
Conclusion
In short, in this article, we learn about type-graphql setup and relations between graph nodes with code examples.
If you still have a question you can reach out to me on LinkedIn. I will try my best to answer your question.
Thank you for reading my article