Creating robust web applications often involves handling data through basic operations known as CRUD: Create, Read, Update, and Delete. Next.js, with its powerful features and flexibility, makes it an excellent choice for building such applications. In this guide, we’ll dive into how you can implement CRUD operations in a Next.js application.
Setting Up Your Project
First, ensure you have Node.js installed. Then, create a new Next.js project:
npx create-next-app crud-nextjs
cd crud-nextjs
Creating the Backend API
For simplicity, we’ll use a JSON file as our database. Create a data directory in the root of your project and a db.json file inside it:
{
"posts": [
{ "id": 1, "title": "First Post", "content": "This is my first post" }
]
}
Next, install json-server to create a REST API:
npm install json-server
Add a script to your package.json to start the JSON server:
"scripts": {
"json-server": "json-server --watch data/db.json --port 3001"
}
Start the JSON server:
npm run json-server
Your API is now running at http://localhost:3001/posts.
Integrating API with Next.js
Create a new file lib/api.js to handle API requests:
const API_URL = 'http://localhost:3001/posts';
export const fetchPosts = async () => {
const res = await fetch(API_URL);
return res.json();
};
export const createPost = async (post) => {
const res = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(post)
});
return res.json();
};
export const updatePost = async (id, post) => {
const res = await fetch(`${API_URL}/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(post)
});
return res.json();
};
export const deletePost = async (id) => {
const res = await fetch(`${API_URL}/${id}`, {
method: 'DELETE'
});
return res.json();
};
Creating Pages and Components
Now, let’s create our pages and components for CRUD operations.
1. List Posts
Create pages/index.js to list all posts:
import { useEffect, useState } from 'react';
import { fetchPosts } from '../lib/api';
const Home = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPosts = async () => {
const postsData = await fetchPosts();
setPosts(postsData);
};
getPosts();
}, []);
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
export default Home;
2. Create Post
Create pages/create.js:
import { useState } from 'react';
import { createPost } from '../lib/api';
const CreatePost = () => {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
const newPost = { title, content };
await createPost(newPost);
setTitle('');
setContent('');
};
return (
<div>
<h1>Create Post</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Title"
/>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="Content"
></textarea>
<button type="submit">Create</button>
</form>
</div>
);
};
export default CreatePost;
3. Update Post
Create pages/edit/[id].js:
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { fetchPosts, updatePost } from '../../lib/api';
const EditPost = () => {
const router = useRouter();
const { id } = router.query;
const [post, setPost] = useState({ title: '', content: '' });
useEffect(() => {
if (id) {
const getPost = async () => {
const posts = await fetchPosts();
const selectedPost = posts.find(post => post.id === Number(id));
setPost(selectedPost);
};
getPost();
}
}, [id]);
const handleSubmit = async (e) => {
e.preventDefault();
await updatePost(id, post);
router.push('/');
};
return (
<div>
<h1>Edit Post</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={post.title}
onChange={(e) => setPost({ ...post, title: e.target.value })}
placeholder="Title"
/>
<textarea
value={post.content}
onChange={(e) => setPost({ ...post, content: e.target.value })}
placeholder="Content"
></textarea>
<button type="submit">Update</button>
</form>
</div>
);
};
export default EditPost;
4. Delete Post
Update pages/index.js to include a delete button:
import { useEffect, useState } from 'react';
import { fetchPosts, deletePost } from '../lib/api';
const Home = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPosts = async () => {
const postsData = await fetchPosts();
setPosts(postsData);
};
getPosts();
}, []);
const handleDelete = async (id) => {
await deletePost(id);
setPosts(posts.filter(post => post.id !== id));
};
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
{post.title}
<button onClick={() => handleDelete(post.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
};
export default Home;
Conclusion
You’ve now implemented a basic CRUD application in Next.js. This setup provides a solid foundation for more advanced features, such as authentication, role-based access, and more complex data models. Happy coding!