Next.js is not just a front-end framework; it also allows you to build powerful full-stack applications with its integrated API routes. This feature enables you to define backend endpoints directly within your Next.js project, providing a seamless way to handle server-side logic, data fetching, and integrations. In this blog, we'll explore how to use API routes in Next.js and build a full-stack application.
What are API Routes?
API routes in Next.js allow you to create serverless functions as part of your application. These routes can handle HTTP requests and are defined within the pages/api directory. Each file in this directory maps to an endpoint in your application.
Benefits of API Routes
Unified Project Structure:
- Keep your backend and frontend code in a single project, simplifying development and deployment.
Serverless Architecture:
- API routes are serverless functions that scale automatically and reduce infrastructure management.
Flexibility:
- Handle various tasks like form submissions, database operations, authentication, and third-party API integrations.
Building a Full-Stack Application
Let's build a simple full-stack application with Next.js. We'll create a task management app with the following features:
Step 1: Setting Up Your Project
Ensure you have a Next.js project set up. If not, create a new one:
npx create-next-app task-manager
cd task-manager
Step 2: Creating the API Routes
We'll create API routes for managing tasks. First, let's create a mock database using an array.
Create a Mock Database
Create a new file called data.js in the root directory:
let tasks = [];
export const getTasks = () => tasks;
export const addTask = (task) => {
tasks.push(task);
};
export const deleteTask = (id) => {
tasks = tasks.filter(task => task.id !== id);
};
Create the Tasks API
Create a new file called pages/api/tasks.js:
import { getTasks, addTask, deleteTask } from '../../data';
export default function handler(req, res) {
if (req.method === 'GET') {
const tasks = getTasks();
res.status(200).json(tasks);
} else if (req.method === 'POST') {
const task = req.body;
addTask(task);
res.status(201).json(task);
} else if (req.method === 'DELETE') {
const { id } = req.body;
deleteTask(id);
res.status(204).end();
} else {
res.setHeader('Allow', ['GET', 'POST', 'DELETE']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
Step 3: Creating the Frontend
Create the Task Form
Create a new file called components/TaskForm.js:
import { useState } from 'react';
const TaskForm = ({ onAddTask }) => {
const [task, setTask] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
if (task.trim()) {
const newTask = { id: Date.now(), task };
await fetch('/api/tasks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newTask),
});
onAddTask(newTask);
setTask('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
placeholder="Add a new task"
required
/>
<button type="submit">Add Task</button>
</form>
);
};
export default TaskForm;
Create the Task List
Create a new file called components/TaskList.js:
const TaskList = ({ tasks, onDeleteTask }) => {
const handleDelete = async (id) => {
await fetch('/api/tasks', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ id }),
});
onDeleteTask(id);
};
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>
{task.task}
<button onClick={() => handleDelete(task.id)}>Delete</button>
</li>
))}
</ul>
);
};
export default TaskList;
Create the Main Page
Modify pages/index.js to include the task form and task list components:
import { useState, useEffect } from 'react';
import TaskForm from '../components/TaskForm';
import TaskList from '../components/TaskList';
export default function Home() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
const fetchTasks = async () => {
const res = await fetch('/api/tasks');
const tasks = await res.json();
setTasks(tasks);
};
fetchTasks();
}, []);
const addTask = (task) => {
setTasks([...tasks, task]);
};
const deleteTask = (id) => {
setTasks(tasks.filter(task => task.id !== id));
};
return (
<div>
<h1>Task Manager</h1>
<TaskForm onAddTask={addTask} />
<TaskList tasks={tasks} onDeleteTask={deleteTask} />
</div>
);
}
Step 4: Run the Development Server
Start the development server:
npm run dev
Conclusion
By using API routes in Next.js, you can create a comprehensive full-stack application with a unified project structure. This approach simplifies development and deployment, providing a seamless way to handle both frontend and backend logic. Next.js’s API routes enable you to build scalable and efficient serverless functions, making it an excellent choice for modern web applications.
Explore the official Next.js documentation on API routes to learn more about advanced features and best practices. Happy coding!