Building Your First CRUD Application: A Step-by-Step Guide
Learn how to create a complete CRUD (Create, Read, Update, Delete) application from scratch using modern web technologies.
Understanding CRUD Operations
CRUD represents the four basic operations of persistent storage:
- Create: Adding new records
- Read: Retrieving existing records
- Update: Modifying existing records
- Delete: Removing records
Project Setup
Let's create a simple Task Management application using React and Node.js.
1. Frontend Setup
// App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [tasks, setTasks] = useState([]);
const [newTask, setNewTask] = useState('');
useEffect(() => {
fetchTasks();
}, []);
const fetchTasks = async () => {
try {
const response = await axios.get('http://localhost:5000/api/tasks');
setTasks(response.data);
} catch (error) {
console.error('Error fetching tasks:', error);
}
};
return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Task Manager</h1>
{/* Task form and list will go here */}
</div>
);
}
export default App;
2. Backend Setup
// server.js
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
let tasks = [];
// Create
app.post('/api/tasks', (req, res) => {
const task = {
id: Date.now(),
title: req.body.title,
completed: false
};
tasks.push(task);
res.status(201).json(task);
});
// Read
app.get('/api/tasks', (req, res) => {
res.json(tasks);
});
// Update
app.put('/api/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).json({ message: 'Task not found' });
task.title = req.body.title;
task.completed = req.body.completed;
res.json(task);
});
// Delete
app.delete('/api/tasks/:id', (req, res) => {
const taskIndex = tasks.findIndex(t => t.id === parseInt(req.params.id));
if (taskIndex === -1) return res.status(404).json({ message: 'Task not found' });
tasks.splice(taskIndex, 1);
res.status(204).send();
});
app.listen(5000, () => {
console.log('Server running on port 5000');
});
Implementing CRUD Operations
1. Create Operation
// TaskForm.js
const TaskForm = ({ onAddTask }) => {
const [title, setTitle] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:5000/api/tasks', {
title
});
onAddTask(response.data);
setTitle('');
} catch (error) {
console.error('Error creating task:', error);
}
};
return (
<form onSubmit={handleSubmit} className="mb-4">
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Add new task"
className="border p-2 mr-2"
/>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
Add Task
</button>
</form>
);
};
2. Read Operation
// TaskList.js
const TaskList = ({ tasks, onUpdateTask, onDeleteTask }) => {
return (
<div className="space-y-2">
{tasks.map(task => (
<div key={task.id} className="flex items-center justify-between p-2 border">
<span className={task.completed ? 'line-through' : ''}>
{task.title}
</span>
<div className="space-x-2">
<button
onClick={() => onUpdateTask(task.id, { ...task, completed: !task.completed })}
className="text-green-500"
>
{task.completed ? 'Undo' : 'Complete'}
</button>
<button
onClick={() => onDeleteTask(task.id)}
className="text-red-500"
>
Delete
</button>
</div>
</div>
))}
</div>
);
};
3. Update Operation
// Update task function
const updateTask = async (id, updatedTask) => {
try {
const response = await axios.put(`http://localhost:5000/api/tasks/${id}`, updatedTask);
setTasks(tasks.map(task =>
task.id === id ? response.data : task
));
} catch (error) {
console.error('Error updating task:', error);
}
};
4. Delete Operation
// Delete task function
const deleteTask = async (id) => {
try {
await axios.delete(`http://localhost:5000/api/tasks/${id}`);
setTasks(tasks.filter(task => task.id !== id));
} catch (error) {
console.error('Error deleting task:', error);
}
};
Adding Error Handling
// Error handling component
const ErrorMessage = ({ message }) => {
return (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{message}
</div>
);
};
// Usage in App.js
const [error, setError] = useState(null);
// In your API calls
try {
// API call
} catch (error) {
setError(error.response?.data?.message || 'An error occurred');
}
Adding Loading States
// Loading state component
const LoadingSpinner = () => {
return (
<div className="flex justify-center items-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>
);
};
// Usage in App.js
const [loading, setLoading] = useState(false);
// In your API calls
setLoading(true);
try {
// API call
} finally {
setLoading(false);
}
Best Practices
- State Management
- Use proper state management for larger applications
- Consider using Redux or Context API
- Keep state as local as possible
- Error Handling
- Implement proper error boundaries
- Show user-friendly error messages
- Log errors for debugging
- Performance
- Implement pagination for large datasets
- Use proper indexing in the database
- Optimize API calls
- Security
- Implement proper authentication
- Validate input data
- Use HTTPS
- Implement rate limiting
Testing Your CRUD Application
// Example test using Jest
describe('Task API', () => {
test('should create a new task', async () => {
const response = await axios.post('http://localhost:5000/api/tasks', {
title: 'Test Task'
});
expect(response.status).toBe(201);
expect(response.data.title).toBe('Test Task');
});
test('should get all tasks', async () => {
const response = await axios.get('http://localhost:5000/api/tasks');
expect(response.status).toBe(200);
expect(Array.isArray(response.data)).toBe(true);
});
});
Conclusion
Building a CRUD application is a fundamental skill for web developers. This guide has covered:
- Setting up a full-stack application
- Implementing all CRUD operations
- Adding error handling and loading states
- Following best practices
- Testing the application
Remember to:
- Keep your code organized
- Follow security best practices
- Write tests for your application
- Handle errors gracefully
- Consider scalability from the start
Next Steps
- Add user authentication
- Implement a proper database
- Add more features like:
- Task categories
- Due dates
- Priority levels
- Search functionality
Resources
Citations
- React Documentation - State and Lifecycle
- Express.js - API Reference
- MongoDB - CRUD Operations
- Jest - Testing Framework
🚀 Ready to kickstart your tech career?
Comments