The Task Manager app tool is designed to simplify task management with CRUD operation: creation, deletion, and modification of tasks. Users can easily generate new tasks, remove completed ones, and update task details. In this step-by-step tutorial, you will learn the process of building a Basic Task Manager App with Express, React and GraphQL.
Preview of final output: Let us have a look at how the final application will look like.
Prerequisites
Approach to create Task Manager App:
- Creating a Task Manager application involves using React for the front end and Express with GraphQL for the back end.
- The front end is built with React, a JavaScript library for building user interfaces, and utilizes Apollo Client for state management.
- Apollo Client is employed for handling data fetching and mutations through GraphQL queries in the front end.
- Communication between the front end and back end occurs via Express, a Node.js framework, with express-graphql middleware managing GraphQL queries.
- The backend defines a GraphQL schema with queries and mutations specifically designed for tasks.
- Task management is handled in memory on the server side, utilizing GraphQL’s capabilities for querying, creating, updating, and deleting tasks.
- Apollo Client’s useQuery and useMutation hooks facilitate interaction between the frontend and backend, fetching tasks and updating the UI dynamically.
- The application follows a client-server architecture, where React interacts with Express through GraphQL for CRUD operations on tasks.
Steps to create Application and install dependencies
Step 1: Create a new directory for your project and navigate it to the terminal using the following commands
mkdir task-manager-app
cd task-manager-app
Step 2: Create a new React app using Create React App.
npx create-react-app client
cd client
Step 3: Install the required dependencies.
npm install @apollo/client graphql
Project Structure:
The updated dependencies in package.json file will look like :
Frontend Dependencies:
"dependencies":{
"react":"18.2.0",
"@apollo/client": "3.8.8",
"@apollo/react-hooks": "4.0.0",
"react-dom": "18.2.0",
"graphql": "16.8.11"
}
Backend Dependencies
"dependencies": {
"express": "4.18.2",
"express-graphql": "0.12.0",
"graphql": "15.8.0",
"cors": "2.8.5",
"apollo-server-express": "3.13.0"
}
Example: Add the following code in client/src/App.js
//App.js import React, { useState } from "react" ;
import { useQuery,
useMutation,
gql,
ApolloProvider,
ApolloClient,
InMemoryCache,
} from "@apollo/client" ;
import "./App.css" ;
const GET_TASKS = gql` query {
tasks {
id
title
description
}
}
`; const CREATE_TASK = gql` mutation CreateTask($title: String!, $description: String!) {
createTask(title: $title, description: $description) {
id
title
description
}
}
`; const DELETE_TASK = gql` mutation DeleteTask($id: ID!) {
deleteTask(id: $id)
}
`; const UPDATE_TASK = gql` mutation UpdateTask($id: ID!, $title: String, $description: String) {
updateTask(id: $id, title: $title, description: $description) {
id
title
description
}
}
`; function App() {
const { loading, error, data } = useQuery(GET_TASKS);
const [createTask] = useMutation(CREATE_TASK);
const [deleteTask] = useMutation(DELETE_TASK);
const [updateTask] = useMutation(UPDATE_TASK);
const [newTask, setNewTask] = useState({ title: "" , description: "" });
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
const handleCreateTask = () => {
createTask({
variables: newTask,
refetchQueries: [{ query: GET_TASKS }],
});
setNewTask({ title: "" , description: "" });
};
const handleDeleteTask = (id) => {
deleteTask({
variables: { id },
refetchQueries: [{ query: GET_TASKS }],
});
};
const handleUpdateTask = (id, title, description) => {
updateTask({
variables: { id, title, description },
refetchQueries: [{ query: GET_TASKS }],
});
};
return (
<div>
<h1>GeeksforGeeks</h1>
<h3>Task Manager</h3>
<div>
<h2>Create Task</h2>
<input
type= "text"
placeholder= "Title"
value={newTask.title}
onChange={(e) =>
setNewTask({ ...newTask, title: e.target.value })
}
/>
<input
type= "text"
placeholder= "Description"
value={newTask.description}
onChange={(e) =>
setNewTask({ ...newTask, description: e.target.value })
}
/>
<button onClick={handleCreateTask}>Create</button>
</div>
<div>
<h2>Tasks</h2>
<ul>
{data.tasks.map((task) => (
<li key={task.id}>
{task.title} - {task.description}
<button onClick={() => handleDeleteTask(task.id)}>
Delete
</button>
<button
onClick={() => {
const updatedTitle = prompt(
"Enter new title:" ,
task.title
);
const updatedDescription = prompt(
"Enter new description:" ,
task.description
);
handleUpdateTask(
task.id,
updatedTitle,
updatedDescription
);
}}
>
Update
</button>
</li>
))}
</ul>
</div>
</div>
);
} const client = new ApolloClient({
cache: new InMemoryCache(),
}); function ApolloApp() {
return (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
} export default ApolloApp;
|
/*App.css*/ body { font-family : "Roboto" , sans-serif ;
margin : 0 ;
padding : 0 ;
background-color : #f4f4f4 ;
display : flex;
align-items: center ;
justify- content : center ;
height : 100 vh;
} .container { width : 80% ;
padding : 20px ;
background-color : #fff ;
box-shadow: 0 0 10px rgba( 0 , 0 , 0 , 0.1 );
border-radius: 8px ;
} h 1 {
font-family : "Open Sans" , sans-serif ;
color : #037f07 ;
margin-bottom : 10px ;
text-align : center ;
} h 3 {
font-family : "Arial" , sans-serif ;
color : #333 ;
margin-bottom : 20px ;
text-align : center ;
font-size : 20px ;
} form { margin-bottom : 20px ;
} input { padding : 10px ;
margin-right : 10px ;
width : 200px ;
} button { padding : 10px 16px ;
background-color : #4caf50 ;
color : white ;
border : none ;
cursor : pointer ;
} ul { list-style-type : none ;
padding : 0 ;
} li { margin-bottom : 20px ;
padding : 20px ;
border : 1px solid #ddd ;
background-color : #f9f9f9 ;
border-radius: 8px ;
display : flex;
justify- content : space-between;
align-items: center ;
} button.delete { background-color : #e74c3c ;
} button.update { background-color : #3498db ;
margin-right : 5px ;
} |
Now the Frontend of the app is ready and we are moving forward to the backand of the app.
Step 4: In the main project directory, create a new file named server.js for the Express server.
npm init -y
touch server.js
Step 5: Install the required dependencies for Express and GraphQL using below command :
npm install express express-graphql graphql cors
Example: Now, copy and paste the following code into server.js:
//server.js const express = require( "express" );
const { graphqlHTTP } = require( "express-graphql" );
const { buildSchema } = require( "graphql" );
const cors = require( "cors" );
let tasks = []; const schema = buildSchema(` type Task {
id: ID!
title: String!
description: String!
}
type Query {
tasks: [Task]
}
type Mutation {
createTask(title: String!, description: String!): Task
deleteTask(id: ID!): Boolean
updateTask(id: ID!, title: String, description: String): Task
}
`); const root = { tasks: () => tasks,
createTask: ({ title, description }) => {
const newTask = { id: tasks.length + 1, title, description };
tasks.push(newTask);
return newTask;
},
deleteTask: ({ id }) => {
tasks = tasks.filter((task) => task.id !== parseInt(id));
return true ;
},
updateTask: ({ id, title, description }) => {
const taskIndex = tasks.findIndex((task) => task.id === parseInt(id));
if (taskIndex !== -1) {
tasks[taskIndex] = { ...tasks[taskIndex], title, description };
return tasks[taskIndex];
}
return null ;
},
}; const app = express(); app.use(cors()); app.use( "/graphql" , graphqlHTTP({ schema, rootValue: root, graphiql: true }));
const port = 3001; app.listen(port, () => { console.log(`Server is running on http: //localhost:${port}/graphql`);
}); |
Step 6: Run and Test the App
Backend:
node server.js
Frontend:
cd client
npm start