DEV Community

Cover image for Learn Graphql by building an API for a to-do app.
Ahmd Talat
Ahmd Talat

Posted on • Updated on

Learn Graphql by building an API for a to-do app.

Welcome, this tutorial guides you through building a Todo Graphql-powered API with Node.js and MongoDB.

Getting Started

What you will learn:

  • Obtain a basic understanding of GraphQL principles
  • Define a GraphQL schema that represents the structure of your data set
  • Build an API with Apollo-Server that lets you execute queries against your schema

Requirements

This tutorial assumes that you are familiar with the command line and
JavaScript/ES6, and that you have a recent version of Node.js (8+) and npm || yarn installed.

Step 1: Create a new project

  1. Create a directory for a new project and cd into it:

    mkdir graphql-todo-server
    cd graphql-todo-server
    
  2. Initialize a new Node.js project with yarn or npm:

    yarn init --yes
    

Your project directory now contains a package.json file.

Step 2: Install dependencies

To run Apollo-Server, we need to install apollo-server, graphql. Also we need to install mongoose to connect to DB and create our model:

  • apollo-server is the core library for Apollo Server itself, which helps you define the shape of your data and how to fetch it.
  • graphql is the library used to build a GraphQL schema and execute queries against it.
  • mongoose provides a straight-forward, schema-based solution to model your application data.

Run the following command to install these dependencies and save them in
your project's node_modules directory:

yarn install apollo-server graphql mongoose
Enter fullscreen mode Exit fullscreen mode

Also, create the following for a better project structure:

touch index.js

mkdir graphql models
Enter fullscreen mode Exit fullscreen mode
  • index.js is our entry for our server
  • graphql directory will hold our typeDef and resolvers
  • model for our mongodb models

Step 3: Build a Schema

Every data graph uses a schema to define the types of data it includes.
first, we will create a file typeDefs.js which will hold our shcema inside our graphql directory

    cd graphql
    touch typeDefs.js
Enter fullscreen mode Exit fullscreen mode

Now, the schema will look like this:

const { gql } = require("apollo-server");

module.exports = gql`
  type Todo {
    id: ID!
    body: String!
    created: String!
  }

  type Query {
    getTodos: [Todo]!
  }

  type Mutation {
    createTodo(body: String!): Todo!
    deleteTodo(todoId: ID!): String!
   }
`;

Enter fullscreen mode Exit fullscreen mode

We are exporting our Schema to use it in index.js when creating our server, Our Todo is very simple:

The type Todo, defines our todo object's fields:

  • id: every Todo will have a unique id that Mongogb will create for us.
  • body: of type string for our todo text.
  • created: will hold the date.
  • The ! after each type means it can not be null

The Query type

our schema needs to define queries that clients can execute against the data graph.

The Mutation type

with the query, we can fetch the data, but not to modify it. our schema needs to define mutations to modify our data.

The gql tag

the gql tag is used to surround GraphQL operation and schema language (which are represented as Strings), and makes it easier to differentiate from ordinary strings.

Step 4: Create our Todo model

const { model, Schema } = require("mongoose");

const todoSchema = new Schema({
  body: String,
  created: String
});

module.exports = model("Todo", todoSchema);

// Note: the id will be generated automatically
Enter fullscreen mode Exit fullscreen mode

Step 5: Define the resolvers

We've defined the shape of our data, but Apollo doesn't know how to fetch it. To fix this, we create a resolver.
First, we create the file in graphql

touch graphql/resolvers.js
Enter fullscreen mode Exit fullscreen mode

Now, our resolvers will look like this:

// we need a Todo model in our mutations to modify the data.
const Todo = require('../models/Todo');

module.exports = {
  Query: {
    // here is the getTodos, that we defined in our typeDefs.
    // simply, using the Todo model to fetch all the todos with async/await
    // and return the result.
    async getTodos() {
      try {
        const todos = await Todo.find({}).sort({ created: -1 });
        return todos;
      } catch (err) {
        throw new Error(err);
      }
    }
  },

  Mutation: {
      async createTodo(_, { body }) {
    // destructure the body from our args.
    // create a new Todo, save and return that todo
    // created is the date.
      try {
        const newTodo = new Todo({
          body,
          created: new Date().toISOString()
        });
        const todo = await newTodo.save();
        return todo;
      } catch (err) {
        throw new Error(err);
      }
    },

    async deleteTodo(_, { todoId }) {
      // Find the todo by its Id and delete it.
      try {
        const todo = await Todo.findById(todoId);
        if (todo) {
            await todo.delete();
            return 'Todo deleted!';
        } else {
            return 'Todo does not exist'
        }
      } catch (err) {
        throw new Error(err);
      }
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Again!, we exporting the resolvers to use it when creating the Apollo Server.
The resolver takes a couple of positional arguments (parent,args,context,info). for our app we need only the args, for more info, read Resolvers type signature.

Step 6: Create the Apollo Server

finally, We create our server and connect to the db.

const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');

// The config file contains any project configuration
// MONGODB will be something like this "'mongodb://username:password@host:port/database?options...'", you can get your own from Mongodb.com.
// PORT: process.env.PORT || '5000', if no env variables.

const { MONGODB ,PORT } = require('./config');

const typeDefs = require('./graphql/typeDefs');
const resolvers = require('./graphql/resolvers');

const server = new ApolloServer({
  typeDefs,
  resolvers
});

mongoose
  .connect(MONGODB, {
    useUnifiedTopology: true,
    useNewUrlParser: true
  })
  .then(() => {
    console.log('MongoDB is connected ...');
    return server.listen({
      port: PORT
    });
  })
  .then(res => {
    console.log('Server running at ', res.url);
  });


Enter fullscreen mode Exit fullscreen mode

Now, you can run your server by running this command.

node index.js
Enter fullscreen mode Exit fullscreen mode

To re-run the server automatically when a file changes. you can install nodemon package globally. and then use it in your project.

npm install -g nodemon

nodemon index.js
Enter fullscreen mode Exit fullscreen mode

your terminal will be like this if no errors.
Alt Text

Tada, that's it, I hope you find it helpful and please comment below if you have any questions.
Have A Great Day!

Top comments (4)

Collapse
 
monfernape profile image
Usman Khalil

I found this extremely helpful. Great explanation

Collapse
 
ahmdtalat profile image
Ahmd Talat

Thanks, Usman, glad to know that

Collapse
 
koalapvh13 profile image
Matheus Dias Vieira

Amazing! For the first time I understood the GraphQL Basics. Thanks man. Greetings from Brazil.

Collapse
 
ahmdtalat profile image
Ahmd Talat

Hello Matheus, happy to know that
Thanks