Easy GraphQL Server in Node.js

rfnajid

Rida F Najid

Posted on February 28, 2023

Easy GraphQL Server in Node.js

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

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
 }
`;
Enter fullscreen mode Exit fullscreen mode

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
 }
 `
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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();

Enter fullscreen mode Exit fullscreen mode

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

Example of Mutation in GraphQL

That's it for now!

You can access my code here

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

💖 💪 🙅 🚩
rfnajid
Rida F Najid

Posted on February 28, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related