API Routes in Next.js: Building a Full-Stack Application

API Routes in Next.js: Building a Full-Stack Application

Leverage API Routes to Create a Comprehensive Full-Stack Application

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

  1. Unified Project Structure:

    • Keep your backend and frontend code in a single project, simplifying development and deployment.
  2. Serverless Architecture:

    • API routes are serverless functions that scale automatically and reduce infrastructure management.
  3. 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:

  • A form to add new tasks.

  • A list of tasks fetched from an API route.

  • The ability to delete tasks.

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.

  1. Create a Mock Database

    Create a new file called data.js in the root directory:

     // data.js
     let tasks = [];
     export const getTasks = () => tasks;
     export const addTask = (task) => {
     export const deleteTask = (id) => {
       tasks = tasks.filter(task => task.id !== id);
  2. Create the Tasks API

    Create a new file called pages/api/tasks.js:

     // pages/api/tasks.js
     import { getTasks, addTask, deleteTask } from '../../data';
     export default function handler(req, res) {
       if (req.method === 'GET') {
         // Return all tasks
         const tasks = getTasks();
       } else if (req.method === 'POST') {
         // Add a new task
         const task = req.body;
       } else if (req.method === 'DELETE') {
         // Delete a task
         const { id } = req.body;
       } else {
         res.setHeader('Allow', ['GET', 'POST', 'DELETE']);
         res.status(405).end(`Method ${req.method} Not Allowed`);

Step 3: Creating the Frontend

  1. Create the Task Form

    Create a new file called components/TaskForm.js:

     // components/TaskForm.js
     import { useState } from 'react';
     const TaskForm = ({ onAddTask }) => {
       const [task, setTask] = useState('');
       const handleSubmit = async (e) => {
         if (task.trim()) {
           const newTask = { id: Date.now(), task };
           await fetch('/api/tasks', {
             method: 'POST',
             headers: {
               'Content-Type': 'application/json',
             body: JSON.stringify(newTask),
       return (
         <form onSubmit={handleSubmit}>
             onChange={(e) => setTask(e.target.value)}
             placeholder="Add a new task"
           <button type="submit">Add Task</button>
     export default TaskForm;
  2. Create the Task List

    Create a new file called components/TaskList.js:

     // 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 }),
       return (
           {tasks.map((task) => (
             <li key={task.id}>
               <button onClick={() => handleDelete(task.id)}>Delete</button>
     export default TaskList;
  3. Create the Main Page

    Modify pages/index.js to include the task form and task list components:

     // pages/index.js
     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();
       }, []);
       const addTask = (task) => {
         setTasks([...tasks, task]);
       const deleteTask = (id) => {
         setTasks(tasks.filter(task => task.id !== id));
       return (
           <h1>Task Manager</h1>
           <TaskForm onAddTask={addTask} />
           <TaskList tasks={tasks} onDeleteTask={deleteTask} />

Step 4: Run the Development Server

Start the development server:

npm run dev
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!

Did you find this article valuable?

Support ByteScrum Technologies by becoming a sponsor. Any amount is appreciated!