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