Using GraphQL for Your APIs
Tags: graphql, javascript, node, api
Published: 2022-07-01 23:12:53

Most of the APIs request an endpoint to access a predefined data structure. If you want to access other resources, it is necessary to request another endpoint, it makes the process kind of tricky.

1. We only define a single endpoint (for example http://example/graphql).
2. Since this is a query language, all actions are done through a POST.


## Solution
GraphQL allow us to retrieve only the data we need by using a query language for web APIs.


## Recipe

### CRUD GraphQL API with Nodejs, Express & MongoDB

1. Create a new directory to store the project, run npm init to configure the new project
2. Run npm install to create our package.json file
3. Create a server.js file (entry point for our server)
4. Create the src folder and the below required folders and files:
    1. Create src/schema directory and an index.js file (will contain the business logic)
    2. Create a src/resolvers directory and an index.js file.
    3. Create an src/models directory and a post.js which holds what a post should look like.
    
    ```console
    ├── src
    │   ├── schema
    │   │   └── index.js
    │   ├── resolvers
    │   │   └── index.js
    │   └── models
    │       └── post.js
    ├── package-lock.json
    ├── package.json
    └── server.js
    ```

5. Install the dependencies

    #### Using npm
    ```console
      # npm
      $ npm install --save express express-graphql graphql body-parser
    ```

    #### Using yarn
    ```console
      # yarn
      $ yarn add --save express express-graphql graphql body-parser
    ```

    > You can also install "nodemon" locally to avoid having to restart your server with each change
    
    ```console
      $npm install --save-dev nodemon
    ```

    > Also you need to update your "package.json"  for using "nodemon".
    
    ```json
      "scripts": { "start": "nodemon server.js"}
    ```
6. Edit the schema, it will allow us to define our GraphQL objects and list the different actions available through our API.

    ```js
    
    // ./src/schema/index.js
    const { buildSchema } = require('graphql');

    module.exports = buildSchema(`
      """
      A Post refers to available attributes for a Post
      """
      type Post {
        _id: ID!
        body: String!
        createdAt: String!
      }
      input PostType {
        body: String!
      }
      type RootQuery {
        posts: [Post!]
        post(_id: String!): Post!
      }
      type Mutation {
        createPost(post:PostType): Post,
        deletePost(_id: String): Post,
        updatePost(_id: String, body: String): String
      }
      schema {
        query: RootQuery
        mutation: Mutation
      }
    `);

    ```
    We set up two queries, one to fetch all posts, and the other one to  retrieve a post by id.

    To be able to retrieve posts, we must first have them in our database. That implies we’ll have to create them. We achieve this with the help of a GraphQl mutation. It’s a query that (creates/updates/deletes).

    1. The "createPost" mutation is used to make a new post in this case. It takes an item of type PostInput and creates a post from it.
    2. The "deletePost" mutation to delete a post by id.
    3. The "updatePost" mutation is made to update a post, It takes two params _id and the new body.

7. Create Moongoose Model, install the mongoose package:
    
    ```console
    npm install mongoose
    ```

    Then edit the file "src/model/post.js" in order to add the model.

    ```js
    //./src/models/post.js
    const mongoose = require("mongoose")

    const Schema = mongoose.Schema

    const postSchema = new Schema(
      {
        body: {
          type: String,
          required: true,
        },
      },
      { timestamps: true }
    )

    module.exports = mongoose.model("Post", postSchema)
    ```

8. Edit the resolver file, it is a set of functions that generates a GraphQL query response. It’s a GraphQL query handler, the name of every query or mutation must exactly match the name of the resolver function. 

    Add this code below in the "src/resolvers/index.js"

    ```js
    //./src/resolvers/index.js
    const Post = require("../models/post")

    module.exports = {
      posts: async () => {
        try {
          const postsFetched = await Post.find()
          return postsFetched.map(post => {
            return {
              ...post._doc,
              _id: post.id,
              createdAt: new Date(post._doc.createdAt).toISOString(),
            }
          })
        } catch (error) {
          throw error
        }
      },

      post: async (_id) => {
        try {
          const postFetched = await Post.findById(_id);
          return {
            ...postFetched._doc,
            _id: postFetched.id,
            createdAt: new Date(postFetched._doc.createdAt).toISOString(),
          }
        } catch (error) {
          throw error
        }
      },

      createPost: async args => {
        try {
          const { body } = args.post
          const post = new Post({
            body,
          })
          const newPost= await post.save()
          return { ...newPost._doc, _id: newPost.id }
        } catch (error) {
          throw error
        }
      },

      deletePost: async (id) => {
        try {
          const deletedPost = await Post.findByIdAndDelete(id);
          return {
            ...deletedPost._doc,
            _id: deletedPost.id,
            createdAt: new Date(deletedPost._doc.createdAt).toISOString(),
          }
        } catch (error) {
          throw error
        }
      },

      updatePost: async args => {
        try {
          const { _id, body } = args
          const updatedPost = await Post.findByIdAndUpdate(_id, { body: body });
          return `Post ${updatedPost.id} updated Successfully!!!`
        } catch (error) {
          throw error
        }
      },
    }
    ```

    Based on the "src/shcema/index.js", we created a query named posts that returns an array of posts. The posts method sends the request to MongoDB using the model created with mongoose.

    The mutation described before in our Schema will be handled by the second resolver function createPost. It takes the post object as an input and uses it to create a new post based on the Post model.
    And to save it in MongoDB, we just need to utilize another mongoose helper, the save() function, and return the newly generated post as anticipated in the Schema.

    We now have a schema and resolvers for our API, which is almost everything we need to go on to the next phase and construct a server and endpoint.

8. Create MongoDB

    Go to https://cloud.mongodb.com/  and create you own cluster, as recommendation download https://www.mongodb.com/es/products/compass to connect to the DB.
    
    Save the "Connection String" from your MongoDB.
    
9. Create a new file nodemon.json in the root of your project folder, that file will be used to store our environment variables. In case that you are not using  nodemon , create an .env file

    ```json
    { 
      "env": { 
                "MONGO_DB": "your_Connection String"
            }
    }
    ```

10. Create the Server & connect MongoDB

    Add this to "server.js" file

    ```js
    const express = require("express");
    const { graphqlHTTP } = require("express-graphql");
    const graphQlSchema = require("./src/schema");
    const graphQlResolvers = require("./src/resolvers");
    const mongoose = require("mongoose");

    const app = express();

    app.use(
      "/graphql",
      graphqlHTTP({
        schema: graphQlSchema,
        rootValue: graphQlResolvers,
        graphiql: true,
      })
    );

    const uri = `${process.env.MONGO_DB}?retryWrites=true&w=majority`;
    const options = { useNewUrlParser: true, useUnifiedTopology: true };
    mongoose
      .connect(uri, options)
      .then(() => app.listen(4000, console.log("Server is listening on 4000")))
      .catch((error) => {
        throw error;
      });

    ```

    Imported the schema and resolvers that were previously created. graphqlHTTPis needed to use them. It’s a express-graphql method that expects some options, in this case, are the schema and the resolvers. I also enabled graphiql, a useful tool for query testing.
    
    The last part of the code is to establish the connection to MongoDB

11. Run the project, use "npm start" at the console, Then open at your browser: "http://localhost:4000/graphql".
    
    We can now try our queries directly from the interface.

    You can run this code to create the first post:
    
    ```js
    mutation {
      createPost(post: {body:"My first post"}){
        body,
        createdAt
      }
    }
    ```

Feel free to visit this repository containing all what this blog contains: https://github.com/ICCHA-Technologies/GraphQL-api