I am online
← Back to Articles

Simple GraphQL Example with TypeScript, TypeORM, and TypeGraphQL

GraphQLJune 25, 2024
Simple GraphQL Example with TypeScript, TypeORM, and TypeGraphQL

Entity Definition

First, let's define our Task entity using TypeORM and TypeGraphQL.

import { Field, ID, ObjectType } from "type-graphql";
import {
  BaseEntity,
  Column,
  CreateDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from "typeorm";

@Entity()
@ObjectType()
export class Task extends BaseEntity {
  @PrimaryGeneratedColumn()
  @Field(() => ID)
  id: number;

  @CreateDateColumn()
  @Field(() => String)
  created: Date;

  @UpdateDateColumn()
  @Field(() => String)
  updated: Date;

  @Column()
  @Field(() => String)
  title: string;

  @Column()
  @Field(() => Boolean)
  isComplete: boolean;
}

Entity Definition

Next, let's define our TaskResolver to handle GraphQL queries and mutations.

// src/resolvers/task.ts
import { Arg, Int, Query, Resolver, Mutation } from "type-graphql";
import { Task } from "../entities/task";

@Resolver()
export class TaskResolver {
  @Query(() => String)
  hello(): string {
    return "hello frontYnova";
  }

  @Query(() => [Task])
  tasks(): Promise<Task[]> {
    return Task.find({});
  }

  @Query(() => Task, { nullable: true })
  async task(@Arg("id", () => Int) id: number): Promise<Task | undefined> {
    return await Task.findOne({ where: { id } });
  }

  @Mutation(() => Task)
  createTask(
    @Arg("title", () => String)
    title: string
  ): Promise<Task> {
    return Task.create({ title, isComplete: false }).save();
  }

  @Mutation(() => Boolean)
  deleteTask(
    @Arg("id", () => Int)
    id: number
  ): boolean {
    try {
      Task.delete({ id });
      return true;
    } catch {
      return false;
    }
  }

  @Mutation(() => Boolean, { nullable: true })
  async updateTask(
    @Arg("id", () => Int) id: number,
    @Arg("title", () => String) title: string,
    @Arg("isComplete", () => Boolean) isComplete: boolean
  ): Promise<boolean | null> {
    const task = await Task.findOne({ where: { id } });
    if (!task) return null;

    try {
      await Task.update({ id }, { title, isComplete });
      return true;
    } catch {
      return false;
    }
  }
}

Resolver Definition

Next, let's define our TaskResolver to handle GraphQL queries and mutations.

// src/resolvers/task.ts
import { Arg, Int, Query, Resolver, Mutation } from "type-graphql";
import { Task } from "../entities/task";

@Resolver()
export class TaskResolver {
  @Query(() => String)
  hello(): string {
    return "hello frontYnova";
  }

  @Query(() => [Task])
  tasks(): Promise<Task[]> {
    return Task.find({});
  }

  @Query(() => Task, { nullable: true })
  async task(@Arg("id", () => Int) id: number): Promise<Task | undefined> {
    return await Task.findOne({ where: { id } });
  }

  @Mutation(() => Task)
  createTask(
    @Arg("title", () => String)
    title: string
  ): Promise<Task> {
    return Task.create({ title, isComplete: false }).save();
  }

  @Mutation(() => Boolean)
  deleteTask(
    @Arg("id", () => Int)
    id: number
  ): boolean {
    try {
      Task.delete({ id });
      return true;
    } catch {
      return false;
    }
  }

  @Mutation(() => Boolean, { nullable: true })
  async updateTask(
    @Arg("id", () => Int) id: number,
    @Arg("title", () => String) title: string,
    @Arg("isComplete", () => Boolean) isComplete: boolean
  ): Promise<boolean | null> {
    const task = await Task.findOne({ where: { id } });
    if (!task) return null;

    try {
      await Task.update({ id }, { title, isComplete });
      return true;
    } catch {
      return false;
    }
  }
}

In the resolver, we define several GraphQL operations:

  • hello: A simple query returning a greeting string.
  • tasks: A query to fetch all tasks.
  • task: A query to fetch a single task by ID.
  • createTask: A mutation to create a new task.
  • deleteTask: A mutation to delete a task by ID.
  • updateTask: A mutation to update an existing task.

Server Setup

Next, let's define our TaskResolver to handle GraphQL queries and mutations.

// src/index.ts
import "reflect-metadata";
import express, { Express } from "express";
import { ApolloServer } from "apollo-server-express";
import { buildSchema } from "type-graphql";
import { TaskResolver } from "./resolvers/task";
import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";
import { createConnection } from "typeorm";
import { Task } from "./entities/task";

const main = async () => {
  const conn = await createConnection({
    type: "postgres",
    database: "todolist",
    entities: [Task],
    logging: true,
    synchronize: true,
    username: "postgres",
    password: "postgres",
    port: 5432,
  });

  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [TaskResolver],
      validate: false,
    }),
    plugins: [ApolloServerPluginLandingPageGraphQLPlayground],
  });

  await apolloServer.start();

  const app: Express = express();
  apolloServer.applyMiddleware({ app });

  app.get("/", (_req, res) => res.send("Hello frontYnova"));

  const PORT = process.env.PORT || 8000;
  app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
};

main().catch((err) => console.log(err));

Happy coding!