Next.js CRUD tutorial with examples in details


Next.js CRUD tutorial with examples in details

Creating a CRUD (Create, Read, Update, Delete) application using Next.js involves several steps. In this tutorial, we will build a simple application that manages a list of items. We will use a JSON file as our data source for simplicity, but in a real-world application, you would typically connect to a database.

Prerequisites

  • Basic understanding of React and Next.js
  • Node.js and npm installed on your machine

Setting Up the Next.js Project

  1. Create a new Next.js project:

    bash
    1npx create-next-app@latest nextjs-crud-example 2cd nextjs-crud-example
  2. Install necessary packages:

    For this tutorial, we will use axios for making HTTP requests.

    bash
    1npm install axios

Project Structure

Here's a basic structure we'll follow:

1/nextjs-crud-example 2 ├── /pages 3 │ ├── index.js 4 │ ├── create.js 5 │ ├── edit/[id].js 6 ├── /data 7 │ └── items.json

Step 1: Create Sample Data

Create a folder named data in the root of your project and add a file named items.json with the following content:

json
1[ 2 { "id": 1, "name": "Item 1" }, 3 { "id": 2, "name": "Item 2" }, 4 { "id": 3, "name": "Item 3" } 5]

Step 2: Create the Home Page (Read)

Edit the pages/index.js file to read and display the items:

javascript
1// pages/index.js 2import Link from 'next/link'; 3import axios from 'axios'; 4import { useEffect, useState } from 'react'; 5 6const Home = () => { 7 const [items, setItems] = useState([]); 8 9 useEffect(() => { 10 const fetchItems = async () => { 11 const response = await axios.get('/data/items.json'); 12 setItems(response.data); 13 }; 14 fetchItems(); 15 }, []); 16 17 return ( 18 <div> 19 <h1>Items List</h1> 20 <Link href="/create">Create New Item</Link> 21 <ul> 22 {items.map(item => ( 23 <li key={item.id}> 24 {item.name} - <Link href={`/edit/${item.id}`}>Edit</Link> 25 </li> 26 ))} 27 </ul> 28 </div> 29 ); 30}; 31 32export default Home;

Step 3: Create a Page to Add New Items (Create)

Create a new file named create.js in the pages directory:

javascript
1// pages/create.js 2import { useState } from 'react'; 3import axios from 'axios'; 4import { useRouter } from 'next/router'; 5 6const CreateItem = () => { 7 const [name, setName] = useState(''); 8 const router = useRouter(); 9 10 const handleSubmit = async (e) => { 11 e.preventDefault(); 12 const newItem = { id: Date.now(), name }; 13 14 // Normally you would send this to your backend 15 await axios.post('/api/items', newItem); 16 router.push('/'); 17 }; 18 19 return ( 20 <div> 21 <h1>Create New Item</h1> 22 <form onSubmit={handleSubmit}> 23 <input 24 type="text" 25 value={name} 26 onChange={(e) => setName(e.target.value)} 27 required 28 /> 29 <button type="submit">Create</button> 30 </form> 31 </div> 32 ); 33}; 34 35export default CreateItem;

Step 4: Create an Edit Page (Update)

Create a new file named edit/[id].js in the pages directory:

javascript
1// pages/edit/[id].js 2import { useEffect, useState } from 'react'; 3import { useRouter } from 'next/router'; 4import axios from 'axios'; 5 6const EditItem = () => { 7 const router = useRouter(); 8 const { id } = router.query; 9 const [item, setItem] = useState({ name: '' }); 10 11 useEffect(() => { 12 const fetchItem = async () => { 13 const response = await axios.get('/data/items.json'); 14 const foundItem = response.data.find(i => i.id == id); 15 setItem(foundItem); 16 }; 17 if (id) fetchItem(); 18 }, [id]); 19 20 const handleSubmit = async (e) => { 21 e.preventDefault(); 22 // Normally you would send this to your backend 23 await axios.put(`/api/items/${id}`, item); 24 router.push('/'); 25 }; 26 27 return ( 28 <div> 29 <h1>Edit Item</h1 <form onSubmit={handleSubmit}> 30 <input 31 type="text" 32 value={item.name} 33 onChange={(e) => setItem({ ...item, name: e.target.value })} 34 required 35 /> 36 <button type="submit">Update</button> 37 </form> 38 </div> 39 ); 40}; 41 42export default EditItem;

Step 5: Set Up API Routes (Delete)

Next.js allows you to create API routes to handle backend logic. Create a new folder named api inside the pages directory and add a file named items.js:

javascript
1// pages/api/items.js 2import items from '../../data/items.json'; 3 4export default function handler(req, res) { 5 if (req.method === 'GET') { 6 res.status(200).json(items); 7 } else if (req.method === 'POST') { 8 const newItem = req.body; 9 items.push(newItem); 10 res.status(201).json(newItem); 11 } else if (req.method === 'PUT') { 12 const { id } = req.query; 13 const index = items.findIndex(item => item.id == id); 14 if (index !== -1) { 15 items[index] = { ...items[index], ...req.body }; 16 res.status(200).json(items[index]); 17 } else { 18 res.status(404).json({ message: 'Item not found' }); 19 } 20 } else if (req.method === 'DELETE') { 21 const { id } = req.query; 22 const index = items.findIndex(item => item.id == id); 23 if (index !== -1) { 24 items.splice(index, 1); 25 res.status(204).end(); 26 } else { 27 res.status(404).json({ message: 'Item not found' }); 28 } 29 } else { 30 res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']); 31 res.status(405).end(`Method ${req.method} Not Allowed`); 32 } 33}

Step 6: Testing the Application

Now that you have set up the CRUD functionality, you can run your application:

bash
1npm run dev

Visit http://localhost:3000 in your browser. You should be able to create, read, update, and delete items in your application.

Conclusion

In this tutorial, you learned how to create a simple CRUD application using Next.js. You can expand this application by integrating a real database, adding user authentication, or implementing more complex features. ### Step 7: Implementing Delete Functionality

To implement the delete functionality, we need to add a delete button to the item list and handle the delete request in the API route.

First, let's add a delete button to the item list in the pages/index.js file:

javascript
1// pages/index.js 2import Link from 'next/link'; 3import axios from 'axios'; 4import { useEffect, useState } from 'react'; 5 6const Home = () => { 7 const [items, setItems] = useState([]); 8 9 useEffect(() => { 10 const fetchItems = async () => { 11 const response = await axios.get('/data/items.json'); 12 setItems(response.data); 13 }; 14 fetchItems(); 15 }, []); 16 17 const handleDelete = async (id) => { 18 await axios.delete(`/api/items/${id}`); 19 setItems(items.filter(item => item.id !== id)); 20 }; 21 22 return ( 23 <div> 24 <h1>Items List</h1> 25 <Link href="/create">Create New Item</Link> 26 <ul> 27 {items.map(item => ( 28 <li key={item.id}> 29 {item.name} - 30 <Link href={`/edit/${item.id}`}>Edit</Link> - 31 <button onClick={() => handleDelete(item.id)}>Delete</button> 32 </li> 33 ))} 34 </ul> 35 </div> 36 ); 37}; 38 39export default Home;

Now, when you click the delete button, the item will be deleted from the list and the API route will be called to delete the item.

Step 8: Handling Errors

To handle errors, we can add try-catch blocks to our API routes and error handling functions to our frontend code.

Let's add a try-catch block to our API route in the pages/api/items.js file:

javascript
1// pages/api/items.js 2import items from '../../data/items.json'; 3 4export default function handler(req, res) { 5 try { 6 if (req.method === 'GET') { 7 res.status(200).json(items); 8 } else if (req.method === 'POST') { 9 const newItem = req.body; 10 items.push(newItem); 11 res.status(201).json(newItem); 12 } else if (req.method === 'PUT') { 13 const { id } = req.query; 14 const index = items.findIndex(item => item.id == id); 15 if (index !== -1) { 16 items[index] = { ...items[index], ...req.body }; 17 res.status(200).json(items[index]); 18 } else { 19 res.status(404).json({ message: 'Item not found' }); 20 } 21 } else if (req.method === 'DELETE') { 22 const { id } = req.query; 23 const index = items.findIndex(item => item.id == id); 24 if (index !== -1) { 25 items.splice(index, 1); 26 res.status(204).end(); 27 } else { 28 res.status(404).json({ message: 'Item not found' }); 29 } 30 } else { 31 res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']); 32 res.status(405).end(`Method ${req.method} Not Allowed`); 33 } 34 } catch (error) { 35 console.error(error); 36 res.status(500).json({ message: 'Internal Server Error' }); 37 } 38}

Now, if an error occurs in our API route, it will be caught and a 500 error will be returned to the frontend.

Let's add error handling functions to our frontend code in the pages/index.js file:

javascript
1// pages/index.js 2import Link from 'next/link'; 3import axios from 'axios'; 4import { useEffect, useState } from 'react'; 5 6const Home = () => { 7 const [items, setItems] = useState([]); 8 const [error, setError] = useState(null); 9 10 useEffect(() => { 11 const fetchItems = async () => { 12 try { 13 const response = await axios.get('/data/items.json'); 14 setItems(response.data); 15 } catch (error) { 16 setError(error.message); 17 } 18 }; 19 fetchItems(); 20 }, []); 21 22 const handleDelete = async (id) => { 23 try { 24 await axios.delete(`/api/items/${id}`); 25 setItems(items.filter(item => item.id !== id)); 26 } catch (error) { 27 setError(error.message); 28 } 29 }; 30 31 return ( 32 <div> 33 <h1>Items List</h1> 34 <Link href="/create">Create New Item</Link> 35 {error ? ( 36 <p style={{ color: 'red' }}>{error}</p> 37 ) : ( 38 <ul> 39 {items.map(item => ( 40 <li key={item.id}> 41 {item.name} - 42 <Link href={`/edit/${item.id}`}>Edit</Link> - 43 <button onClick={() => handleDelete(item.id)}>Delete</button> 44 </li> 45 ))} 46 </ul> 47 )} 48 </div> 49 ); 50}; 51 52export default Home;

Now, if an error occurs when fetching or deleting items, an error message will be displayed to the user. ### Step 9: Styling the Application

To improve the user experience, we can add some basic styling to our application. You can use CSS modules or global styles. For simplicity, let's add some global styles.

  1. Create a new file named globals.css in the styles directory:
css
1/* styles/globals.css */ 2body { 3 font-family: Arial, sans-serif; 4 margin: 0; 5 padding: 0; 6 background-color: #f4f4f4; 7} 8 9h1 { 10 color: #333; 11} 12 13a { 14 text-decoration: none; 15 color: #0070f3; 16} 17 18a:hover { 19 text-decoration: underline; 20} 21 22ul { 23 list-style-type: none; 24 padding: 0; 25} 26 27li { 28 background: #fff; 29 margin: 10px 0; 30 padding: 10px; 31 border-radius: 5px; 32 display: flex; 33 justify-content: space-between; 34 align-items: center; 35} 36 37button { 38 background-color: #e63946; 39 color: white; 40 border: none; 41 padding: 5px 10px; 42 border-radius: 5px; 43 cursor: pointer; 44} 45 46button:hover { 47 background-color: #d62839; 48}
  1. Import the global styles in your _app.js file:
javascript
1// pages/_app.js 2import '../styles/globals.css'; 3 4function MyApp({ Component, pageProps }) { 5 return <Component {...pageProps} />; 6} 7 8export default MyApp;

Step 10: Deploying the Application

Once you are satisfied with your application, you can deploy it. Vercel is the recommended platform for deploying Next.js applications.

  1. Sign up for a Vercel account if you don't have one.
  2. Install the Vercel CLI globally:
bash
1npm install -g vercel
  1. Deploy your application by running the following command in your project directory:
bash
1vercel
  1. Follow the prompts to link your project to your Vercel account and deploy it.

Conclusion

You have now built a complete CRUD application using Next.js, complete with create, read, update, and delete functionalities. You also learned how to handle errors and style your application. This foundational knowledge can be expanded upon to create more complex applications with additional features and integrations. Happy coding! 

Comments

Popular posts from this blog

PrimeNG tutorial with examples using frequently used classes

Docker and Kubernetes Tutorials and QnA

oAuth in angular