Easy GraphQL Server in Node.js
Tags: node, graphql, typescript
Published: 2023-02-28 07:05:39

In this article, we will create a simple CRUD (Create, Read, Update, Delete) using GraphQL in Node.js

I'll be using the following tools :
- Express
- Typescript 
- Apollo Server for the GraphQL engine
- Sequelize

Javascript environment is very opinionated you can use other tools to achieve the same goal. But the idea is similiar.

I assume that you're already familiar in Node.js development. So, I won't explain further on the project setup. I want to focus on GraphQL explanation. If you want to see my setup you can check it out on my Github repo below.

I have this model

```
export interface MovieModel extends Model {
    id: number;
    title: string;
    description: string;
    posterUrl: string;
}
```

We will create GraphQL based on the **MovieModel**.

### Schema

In GraphQL, you need to define a schema. The schema is like a blueprint of how GraphQL API can be accessed. You can learn more about GraphQL schema [here](https://graphql.org/learn/schema/).

There are two main operations in GraphQL: Query and Mutation. Query is read-only operation. It does not modify any data. Mutation is request for modifying data. It's used to create, update or delete data on the server. You need to define your operations in schema.

There are other operation types too such as Subscription. But, in this article I won't dive further other operations.

Okay, we need to create root schema first, it looks like this.

```
export const rootSchema = `#graphql

 type Query {
     root: String
 }
 type Mutation {
     root: String
 }
`;
```

And then we create Movie schema.

```
export const movieSchema = `#graphql

 type Movie {
   id: Int!
   title: String!
   description: String
   posterUrl: String
 }

 extend type Query {
    getAllMovies: [Movie]
    getOneMovie(id: Int!): Movie
 }

 extend type Mutation {
    createMovie(title: String!, description: String, posterUrl: String): Movie
    
    updateMovie(id: Int, title: String, description: String, posterUrl: String): Movie
    
    deleteMovie(id: Int!): String
 }
 `
```

As you can see, we can define custom data type on GraphQL schema. In this case, I created **Movie** type that has same attributes as the **MovieModel**.

After that we need to define our operations. 

I created two Query operations. **getAllMovies** and **getOneMovie**. 

**getAllMovies** doesn't have any input, but it will return an array of **Movie**. 

**getOneMovie** have one input, **id** with an integer type. The exclamation mark (!) indicates that the input is required.

**getOneMovie** will only return one **Movie** data. That's why it doesn't have square brackets.

And then I created three mutations. Basically it's for create, update and delete. 

There's no different in syntax between Query and Mutation.

### Resolver

A resolver is the logical function when a GraphQL API is called. The resolver function must has same name as defined in the schema.

Here's example of **Movie** Resolver

```
const MovieResolver = {
    Query: {
        async getAllMovies(root, input, context) {
            return Movie.findAll();
        },
        async getOneMovie(root, input, context){
           return Movie.findByPk(input.id);
        }
    },
    Mutation : {
        async createMovie(root, input, context) {
            const { title, description, posterUrl } = input;
            return Movie.create({ title, description, posterUrl });
        },
        async updateMovie(root, input, context) {
            const id = input.id;
            const movie = input;
            await Movie.update(movie, { 
                where: { id: id }
            });

            return {id: id, ...movie};

        },
        async deleteMovie(root, {id}, context){
            const deleteRes = await Movie.destroy({
                where: {id: id}
            });

            if(deleteRes){
                return `Movie id:${id} has been deleted`;
            }else{
                return `Can't delete Movie id:${id}`;
            }
        },
    }
}

export default MovieResolver;
```
To put it simply, Resolver is like Controller in MVC design pattern.

### Initializing Apollo Server

The final touch is to initialize Apollo Server in **main.ts**

```

async function startServer(){
    const app = express();

    const schema = makeExecutableSchema({
        resolvers: [MovieResolver],
        typeDefs: [rootSchema, movieSchema],
    });

    const apolloServer = new ApolloServer({
        schema: schema,
        introspection: true,
    });
    
    await apolloServer.start();

    app.use(
        cors(),
        bodyParser.json(),
        expressMiddleware(apolloServer)
    )

    app.listen(PORT, () => {
        console.log(`Server is running on port ${PORT}....`);
    });
}

startServer();

```


**makeExecutableSchema** is a function to register our resolvers and schema. 

**introspection** is feature in Apollo to enable GraphQL Server to get the specification details of schema. It's useful in development stage. But, it should be disabled in production for security purpose.

Apollo has built in GUI GraphQL Client. You can access it in /graphql

Here's an example of query and mutation request on Apollo GraphQL Client

![Example of Query in GraphQL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/029r3de5mn5bpz9m6eau.png)

![Example of Mutation in GraphQL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q9zfb57ki7qk3je3412r.png)

That's it for now!

You can access my code here 

https://github.com/rfnajid/apollo-graphql-example