TODO App useing Graphql, Nodejs, MongoDB and React JS

Graphql is a query language for APIs and runtime to execute those queries by using a type system you define for your data

Advantage of Graphql

Client can request only that data which is needed so overfetching and underfetching is avoided.

Unlike Rest APIs graphql have single endpoint.

Graphql APIs are based on schema that define types of data that can be quried

GraphQL allows clients to specify nested queries and mutations, enabling them to fetch or modify related data in a single request.

Backend (Node.js, GraphQL, MongoDB)

mkdir todo-backend
cd todo-backend
npm init -y
npm install express graphql apollo-server-express mongoose

apollo-server-express

Apollo Server is a community-driven open-source GraphQL server that works with any GraphQL schema. It's designed to be easy to set up, integrate seamlessly with other libraries, and be extensible to meet complex requirements.

// server.js
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const mongoose = require("mongoose");
const typeDefs = require("./schema/schema");
const resolvers = require("./resolver");

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

  await server.start(); // Start the Apollo Server instance

  const app = express();
  server.applyMiddleware({ app }); // Apply Apollo Server middleware to Express

  await new Promise((resolve) => app.listen({ port: 4000 }, resolve));
  console.log(`Server ready at http://localhost:4000${server.graphqlPath}`);
};

mongoose
  .connect(
    "mongodb+srv://saurabh27:iaJK68wWnbmX3Rsm@ecom-app.ndoybf7.mongodb.net/?retryWrites=true&w=majority",
    {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    }
  )
  .then(() => {
    console.log("MongoDB connected");
    startServer();
  })
  .catch((error) => console.error("MongoDB connection error:", error));
// model/todo.js

const mongoose = require('mongoose');

const todoSchema = new mongoose.Schema({
  task: {
    type: String,
    required: true
  },
  completed: {
    type: Boolean,
    default: false
  }
});

module.exports = mongoose.model('Todo', todoSchema);
// schema/schema.js
const { gql } = require('apollo-server-express');

const typeDefs = gql`
  type Todo {
    id: ID!
    task: String!
    completed: Boolean!
  }

  type Query {
    todos: [Todo]!
  }

  type Mutation {
    createTodo(task: String!): Todo!
    toggleTodoCompleted(id: ID!): Todo!
    deleteTodo(id: ID!): Todo!
  }
`;

module.exports = typeDefs;
// resolver.js
const Todo = require('./models/todo');

const resolvers = {
  Query: {
    todos: async () => {
      try {
        return await Todo.find()
      }
      catch(error) {
        console.log(error)
        return []
      }
    }
  },
  Mutation: {
    createTodo: async (_, { task }) => {
      const todo = new Todo({ task });
      await todo.save();
      return todo;
    },
    toggleTodoCompleted: async (_, { id }) => {
      const todo = await Todo.findById(id);
      todo.completed = !todo.completed;
      await todo.save();
      return todo;
    },
    deleteTodo: async (_, { id }) => {
      const todo = await Todo.findByIdAndDelete(id);
      return todo;
    }
  }
};

module.exports = resolvers;

Frontend (React.js)

npx create-react-app todo-frontend
cd todo-frontend
npm install @apollo/client graphql

apollo client -

Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL.

// App.js
import React, { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
import './App.css'

const GET_TODOS = gql`
  query {
    todos {
      id
      task
      completed
    }
  }
`;

const CREATE_TODO = gql`
  mutation CreateTodo($task: String!) {
    createTodo(task: $task) {
      id
      task
      completed
    }
  }
`;

const TOGGLE_TODO_COMPLETED = gql`
  mutation ToggleTodoCompleted($id: ID!) {
    toggleTodoCompleted(id: $id) {
      id
      task
      completed
    }
  }
`;

const DELETE_TODO = gql`
  mutation DeleteTodo($id: ID!) {
    deleteTodo(id: $id) {
      id
      task
      completed
    }
  }
`;

function App() {
  const { loading, error, data } = useQuery(GET_TODOS);
  const [createTodo] = useMutation(CREATE_TODO);
  const [toggleTodoCompleted] = useMutation(TOGGLE_TODO_COMPLETED);
  const [deleteTodo] = useMutation(DELETE_TODO);
  const [taskInput, setTaskInput] = useState('');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  const handleCreateTodo = async () => {
    await createTodo({ variables: { task: taskInput } });
    setTaskInput('');
  };

  const handleToggleTodoCompleted = async (id) => {
    await toggleTodoCompleted({ variables: { id } });
  };

  const handleDeleteTodo = async (id) => {
    await deleteTodo({ variables: { id } });
  };

  return (
    <div className='container'>
      <h1>TODO List</h1>
      <input
        type="text"
        value={taskInput}
        onChange={(e) => setTaskInput(e.target.value)}
      />
      <button onClick={handleCreateTodo}>Add</button>
      <ul>
        {data.todos.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodoCompleted(todo.id)}
            />
            <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
              {todo.task}
            </span>
            <button onClick={() => handleDeleteTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

const root = ReactDOM.createRoot(document.getElementById("root"));

const client = new ApolloClient({
  uri: "http://localhost:4000/graphql", // Your GraphQL server URL
  cache: new InMemoryCache(),
});

root.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Did you find this article valuable?

Support SAURABH TARAGI by becoming a sponsor. Any amount is appreciated!