Building a CRUD-backend with GraphQL, TypeScript and TypeGraphQL
Tags: graphql, typescript, node, javascript
Published: 2021-03-26 09:17:29

# Intro
Hey, Swayne here. Over the next couple of months I will be writing some articles on graphQL, because I want to understand this better. Like always, I encourage you to tear me to shreds and I would like for you to correct/question every small detail (seriously). Thank you. 

For this first article I just want develop a simple hello world app. I will be testing with GraphQL playground. Note: I will be writing this using typescript and type-graphql, but it should be the same besides the explicit type definitions (and awesome autofill😉). But of course let me know if you are used to JS, I will translate it for you.

### What do you need?

The recipe to a good GraphQL-based backend

✅  Resolver

🎩. Schema 

💻  Apollo-Server

I will go through the basics of a GraphQL-backend using TypeScript. I will also be using TypeGraphQL.

# Basics of GraphQL

To send queries with GraphQL, you have to define your types first. It's like the schema of your API, so it tells which requests should return which types. Here is an example when getting a type Person:

```graphql
type Person {
	name: String!
	age: Int!
}
```

You are telling graphQL what type it should expect when getting the name or age of a person. Note the exclamation point `!` means the field cannot be null. You don't have to define this, it's completely optional, but improves your design and database structure.

### Type-GraphQL classes

TypeGraphQL is a GraphQL framework for TypeScript, which makes working with queries and schemas easier. I like TypeGraphQL (TGQL), because I think the structure is simpler and the developer experience nicer. Let's look at the above type translated into TGQL using classes and decorators

```graphql
@ObjectType()
class Person {
	@Field()
	name: String!

	@Field()
	age: Int!
}
```

You will notice that we have added `@Field()` and `@ObjectType`. These are called decorators. `@Field` is used to declare what a field is, and `@ObjectType` marks the class as a GraphQL type. 

### Resolver

There are two different types of resolvers, Mutations and Queries. Queries are read-only requests to get and view data from the GQL API. Mutations are resolvers where you create, update or delete data through the API, as the name indicates. Resolvers are functions, and in TGQL you (like in the Schema) have to make a class first.

```tsx
@Resolver()
class UserResolver {
}
```

You also have to use the `@Resolver()` decorator. Here is an example of a simple query:

```tsx
import { Query, Resolver } from "type-graphql";

@Resolver()
export class HelloWorldResolver {
  @Query(() => String)
  hello() {
    return "hi!";
  }
}
```

As you can see you define a `hello()` function and which returns a type string with the name `hello()` and returns a string of "hi!".

We can now move on to an actual use-case.

# CRUD-guide with a Database, TS, GraphQL and Type-GraphQL, TypeORM

We will be studying the follow technologies:

### Tech-Stack

- GraphQL
- Type-GraphQL
- TypeORM
- SQL-lite
- TypeScript

The code for this tutorial is [available on Github](https://github.com/lastnameswayne/crudappexampleblogpost) under the branch "server-CRUD".

Intialize the repo with Ben Awads command `npx create-graphql-api graphql-example` 

and delete all of the code regarding PostgresSQL in `ormconfig.json`

You can also just clone this [starter GitHub Repo](https://github.com/lastnameswayne/startergraphqlserver/tree/main) I made.

Change the data in index.ts to:

```tsx
(async () => {
  const app = express();

  const options = await getConnectionOptions(
    process.env.NODE_ENV || "development"
  );
  await createConnection({ ...options, name: "default" });

  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloWorldResolver],
      validate: true
    }),
    context: ({ req, res }) => ({ req, res })
  });

  apolloServer.applyMiddleware({ app, cors: false });
  const port = process.env.PORT || 4000;
  app.listen(port, () => {
    console.log(`server started at http://localhost:${port}/graphql`);
  });
})();
```

To start with, we are creating an app with express()

```tsx
	await createConnection();
```

createConnection() is from TypeORM, which establishes a connection to the SQL-lite database.

```tsx
  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloWorldResolver],
      validate: true
    }),
    context: ({ req, res }) => ({ req, res })
  });
```

There are two important concepts in the above code, `apolloServer` and `buildSchema()`. ApolloServer is a sort of middle layer between your Server and Client. In our case we will be using it to define a schema-property, by calling the buildSchema-function from TypeGraphQL. 

To build a schema, you need resolvers. Right now we are using a standard HelloWorldResolver, which we will look at soon. We are also using Apollo to get the context, making it possible to share a database connection between resolvers. Lastly,  `validate: true` forces TypeGraphQL to validate inputs and arguments based on the definitions of your decorators.

![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/70a0595d-3763-4eef-90e1-b6e6fca7a0c0/Untitled.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/70a0595d-3763-4eef-90e1-b6e6fca7a0c0/Untitled.png)

Let's look at the last few lines in `index.ts`

```tsx
apolloServer.applyMiddleware({ app, cors: false });
```

Here we are applying the apolloServer as middleware and passing on our express-app, "connecting" those two.

Lastly, we go `app.listen()`

```tsx
  app.listen(port, () => {
    console.log(`server started at http://localhost:${port}/graphql`);
  });
})();
```

`app.listen()` takes a port and starts the server on that given port!

## Entities in TGQL

After some setup, we are ready! 

There are many variations of a CRUD-app, so the difference of a note-taking app and blog-post-app is often just the column names! Point being, you can adjust this to your own needs. I will be making an app to save the scores of the pick-up basketball games I play🏀,

Let's look create a starter entity to define the general structure of our application:

```tsx
import { Field, Int } from "type-graphql";
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";

@ObjectType()
@Entity()
export class Game extends BaseEntity {
    @Field(() => Int)
    @PrimaryGeneratedColumn()
    id: number;
  
    @Field(() => Int)
    @Column('int')
    myTeamScore: number;
  
    @Field(() => Int)
    @Column()
    opponentTeamScore: number;
  
    @Column()
    date: string;
  }
```

This is a pretty simple `Game`, where we save an `id`, `myTeamScore`, `opponentTeamScore` and `date`. We are making sure to give type-definition for each Column. Note, using a `date`-type for the date-attribute would be better practice, but handling dates in Typescript is nearly an article on it's own😅  For now, we can treat dates as a string, but I will show you how to handle them using the Date-type another time. I promise🤝

We are using the  `@Field()`-decorator to declare the types of our field. Sometimes TGQL automatically infers them, but for numbers you have to declare the type explicitly.

On the line above the attributes, we are using two decorators `@Column` and `PrimaryGeneratedColumn()`. You need at least one `PrimaryGeneratedColumn()`, so it's possible to uniquely identify each user. The rest are just standard Columns in a database table.

Type-ORM will automatically infer the types from the TypeScript-types, but you can also set them manually:

```tsx
@Column('int')
myTeamScore: number;
```

You have to check what types your database-provider uses [by looking it up in the docs](https://github.com/typeorm/typeorm/blob/master/docs/entities.md#column-types-for-sqlite--cordova--react-native--expo)📄

If you wanted to, you could also save a `playerName` or `teamName` as string, but that it for another tutorial😉

Let's write some resolvers to actually create, read, update and delete in the database! First, start the server by running  `yarn start`, as you can see in the `package.JSON`:

```tsx
"scripts": {
    "start": "nodemon --exec ts-node src/index.ts",
    "build": "tsc"
```

## Creating a game

Create a new file called `GameResolver.ts` in the resolvers folder please 🥺

The basic structure of a resolver is:

```tsx
import { Mutation, Resolver } from "type-graphql";

@Resolver()
export class GameResolver extends BaseEntity {
    @Mutation()
    createGame() {

    }
}
```

We use the `@Mutation`-decorator to signify that we want to make a change. `createGame()` is the name of the function.

You have to add it to your resolvers-array in the buildSchema-function from index.ts:

```tsx
const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloWorldResolver, GameResolver]
    }),
    context: ({ req, res }) => ({ req, res })
  });
```

I will be building the Resolver step-by-step and explaining as I go:

```tsx
import { Arg, Int, Mutation, Resolver,} from "type-graphql";

@Resolver()
export class GameResolver {
    @Mutation(() => Boolean)
    createGame(
        @Arg('myTeamScore', () => Int) myTeamScore: number,
    ) {
				console.log(myTeamScore)
        return true
    }
}
```

On line 3, I set the return type for the resolver as a Boolean. This isn't really important right now, as I am just returning `true` if it worked. I also log the score✏️

On line 5 I use the `@Arg()`-decorator from TGQL decorator to pass in my arguments. Inside the decorator, I set the TGQL-type of the argument `myTeamScore` to `Int` and outside the parenthesis I set the TypeScript type. Note, that you have to import `Int` from TGQL, since in GraphQL, the type `number` can either be an `Int` or a `Float`, which is why you need to specify further.

Let's add the actual logic for inserting a Game into the database⚡️

```tsx
@Resolver()
export class GameResolver {
    @Mutation(() => Boolean)
    async createGame(
        @Arg('myTeamScore', () => Int) myTeamScore: number,
        @Arg('opponentTeamScore', () => Int) opponentTeamScore: number,
        @Arg('date', () => String) date: string,
    ) {
        await Game.insert({myTeamScore, opponentTeamScore, date})
        console.log(myTeamScore, date);
        return true
    }
}
```

On lines 5-7 I added more `@Args()` based on my Entity in Game.ts. On line 9, we use the TypeORM insert method to add a `Game` to the database. 

Now, it's time to test our new Resolver.

## GraphQL Playground

We will be testing these using [GraphQL playground](https://github.com/graphql/graphql-playground) from Prisma. Go to "localhost:4000/graphQL" in your browser. In the GraphQL playground, you can write out different queries. To try out over resolver, we will write in the window:

```graphql
mutation {
  createGame(
    myTeamScore: 21, 
    opponentTeamScore: 19, 
    date: "19-01-2020"
	)
}
```

This is like calling any function from other programming languages. I add in my own sample data. As a developer, reality can be whatever you want, so (naturally) my team wins😇

## Getting the games

We can create a Query for getting the movies.

```tsx
@Query(() => [Game])
    games() {
        return Game.find()
    }
```

We want to return an array of `Game`-objects, and in the method body we use `Game.find()` from typeORM to, well, find them😄 

In the GraphQL Playground we can then write the query:

```graphql
query {
  games{
    id,
    myTeamScore,
    opponentTeamScore,
    date
  }
}
```

This will get all of the games. The amazing thing about GraphQL (compared to REST atleast), is that you can choose what data to get. In example, you can remove the date-property from the above query if you don't need it. This is really efficient and especially useful for larger projects.

## Update

Say that we want to update a game, we need to create a new resolver:

```tsx
@Mutation(() => Boolean)
    async updateGame(
        @Arg('id', () => Int) id: number,
        @Arg('myTeamScore', () => Int) myTeamScore: number,
        @Arg('opponentTeamScore', () => Int) opponentTeamScore: number,
        @Arg('date', () => String) date: string,
    ) {
        await Game.update({id}, {myTeamScore, opponentTeamScore, date})
        return true
    }
```

The resolver above takes in 4 arguments:

- an `id` ****to identify what post to delete
- an updated `myTeamScore`, `opponentTeamScore` and `date`.

You then call `Game.update()` (also a function from TypeORM) which updates the database values. Lastly, I return true. We can now head over to the GraphQL Playgrpund:

```graphql
mutation {
  updateGame(
    id: 1
    myTeamScore: 19, 
    opponentTeamScore: 21, 
    date: "19-01-2020"
	)
}
```

To update we make sure to pass in some sample *updated* values.

## Delete

The last of the CRUD-operations, delete. To delete you just need an id to identify the post.

```tsx
@Mutation(() => Boolean)
    async deleteGame(
        @Arg("id", () => Int) id: number
    ) {
        await Game.delete({id})
        return true
    }
```

You can then call `Game.delete()` and pass in the `id` as an object 

In the playground:

```tsx
mutation {
  deleteGame(id: 1)
}
```

I want to delete the first post, so I pass in the `id`.

# Conclusion

As you can see, GraphQL gives us a structured way of making operations on the server. Using Type-GraphQL and TypeORM we can set up our Entities and and any write mutator/query resolvers we can think of. The general process is:

1️⃣ Write your entities with types and decorators.

2️⃣ Decide what you want your resolver to return.

3️⃣ Pass in the args from your entity.

4️⃣ Make the needed operation in your resolver body.

And that's it! ✅

However, there are some ways to simplify our `@Args()`. As you probably noticed, the Resolvers quickly get ugly the more Arguments we add. This project is pretty small, but imagine if we had more! The solution is to refactor the arguments into a separate input classes, which I will explain further in the [article on Authtenthication](https://dev.to/lastnameswayne/how-to-implement-authentication-with-graphql-and-redis-2opg-temp-slug-4037703?preview=d0b206fd3a53884541603a9aef9155f0b0001483a2a63328ae55c4a867a21d738a8f5a77494d3c7b8a1a414901fe6dda78a33bd8f769bb27b72a8e4e), which is also worth reading!🙏

Feel free to leave any feedback either here or [on my Twitter](https://twitter.com/lastnameswayne)