Learn how to build Serverless Functions in Next.js 14 with Vercel

Muhaymin Bin Mehmood

Muhaymin Bin Mehmood

· 5 min read
Learn how to build Serverless Functions in Next.js 14 with Vercel Banner Image
Learn how to build Serverless Functions in Next.js 14 with Vercel Banner Image

Introduction

Serverless functions have transformed modern web development, enabling developers to build scalable and efficient applications without managing server infrastructure. In this guide, we'll explore how to build serverless functions in Next.js 14 and deploy them using Vercel. We'll walk through real-world examples and scenarios to help you understand the practical applications of serverless functions in your projects.

Prerequisites

  • Basic knowledge of React and Next.js
  • Familiarity with JavaScript and Node.js
  • A Vercel account (free or paid)
  • Node.js and npm/yarn installed
  • A code editor like VS Code

Step 1: Setting Up a Next.js 14 Project

Create a New Next.js 14 Project

Begin by creating a new Next.js project:

npx create-next-app@latest my-serverless-app

or

yarn create next-app my-serverless-app

Navigate to Your Project Directory

cd my-serverless-app

Step 2: Understanding Serverless Functions in Next.js

Serverless functions in Next.js are API routes that run on-demand in response to HTTP requests. These functions are ideal for handling tasks like form submissions, processing payments, interacting with databases, and more, without maintaining a dedicated server.

Create a Basic Serverless Function

Let's create a simple serverless function to understand the basics. Inside the pages/api directory, create a new file hello.js:

// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello, Next.js 14 with Serverless!' });
}

This function is returning a JSON response containing a message.

Run and Test the Serverless Function

Start your development server:

npm run dev

or

yarn dev

Navigate to http://localhost:3000/api/hello in your browser or use a tool like Postman to test the API.

Step 3: Real-World Example 1 - Contact Form Handling

Serverless functions are perfect for handling form submissions without needing a backend server. Let’s create a contact form and a corresponding serverless function to process the form data.

Create a Contact Form Component

In the pages directory, create a contact.js page:

// pages/contact.js
import { useState } from 'react';

export default function Contact() {
  const [formData, setFormData] = useState({ name: '', email: '', message: '' });
  const [responseMessage, setResponseMessage] = useState('');

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData),
    });

    const data = await response.json();
    setResponseMessage(data.message);
  };

  return (
    <div>
      <h1>Contact Us</h1>
      <form onSubmit={handleSubmit}>
        <input type="text" name="name" placeholder="Name" onChange={handleChange} />
        <input type="email" name="email" placeholder="Email" onChange={handleChange} />
        <textarea name="message" placeholder="Message" onChange={handleChange} />
        <button type="submit">Send</button>
      </form>
      <p>{responseMessage}</p>
    </div>
  );
}

Create the Contact Form Serverless Function

Create a new file contact.js inside the pages/api directory:

// pages/api/contact.js
export default function handler(req, res) {
  const { name, email, message } = req.body;

  if (!name || !email || !message) {
    return res.status(400).json({ message: 'All fields are required.' });
  }

  // Here, you could integrate with a third-party service, save to a database, etc.

  res.status(200).json({ message: `Thank you for your message, ${name}!` });
}

Test the Contact Form

Complete the form on the contact page and submit it. Verify the response message to confirm that the serverless function is functioning correctly.

Step 4: Real-World Example 2 - CRUD Operations with a Database

Next, we'll explore how to use serverless functions for CRUD (Create, Read, Update, Delete) operations with a database. We'll use a simple SQLite database for this example.

1. Set Up SQLite Database

Install the necessary SQLite package:

npm install sqlite3

or

yarn add sqlite3

Create a new file lib/db.js to handle the database connection:

// lib/db.js
import { open } from 'sqlite';
import sqlite3 from 'sqlite3';

export async function openDb() {
  return open({
    filename: './mydb.sqlite',
    driver: sqlite3.Database,
  });
}

2. Create CRUD Serverless Functions

  • Create: Add a new item to the database.
  • Read: Fetch items from the database.
  • Update: Update an existing item.
  • Delete: Remove an item from the database.

Create a tasks.js file in the pages/api directory to manage tasks:

// pages/api/tasks.js
import { openDb } from '../../lib/db';

export default async function handler(req, res) {
  const db = await openDb();

  if (req.method === 'GET') {
    const tasks = await db.all('SELECT * FROM tasks');
    res.status(200).json(tasks);
  }

  if (req.method === 'POST') {
    const { title } = req.body;
    const result = await db.run('INSERT INTO tasks (title) VALUES (?)', [title]);
    res.status(201).json({ id: result.lastID, title });
  }

  if (req.method === 'PUT') {
    const { id, title } = req.body;
    await db.run('UPDATE tasks SET title = ? WHERE id = ?', [title, id]);
    res.status(200).json({ id, title });
  }

  if (req.method === 'DELETE') {
    const { id } = req.body;
    await db.run('DELETE FROM tasks WHERE id = ?', [id]);
    res.status(204).end();
  }
}

3. Create a Simple UI for Task Management

In pages/index.js, create a UI to interact with these serverless functions:

// pages/index.js
import { useState, useEffect } from 'react';

export default function Home() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  useEffect(() => {
    fetch('/api/tasks')
      .then((res) => res.json())
      .then((data) => setTasks(data));
  }, []);

  const addTask = async () => {
    const res = await fetch('/api/tasks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: newTask }),
    });

    const task = await res.json();
    setTasks([...tasks, task]);
    setNewTask('');
  };

  const updateTask = async (id, title) => {
    await fetch('/api/tasks', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id, title }),
    });

    setTasks(tasks.map((task) => (task.id === id ? { ...task, title } : task)));
  };

  const deleteTask = async (id) => {
    await fetch('/api/tasks', {
      method: 'DELETE',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id }),
    });

    setTasks(tasks.filter((task) => task.id !== id));
  };

  return (
    <div>
      <h1>Task Manager</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="New Task"
         />
         <button onClick={addTask}>Add Task</button>

         <ul>
           {tasks.map((task) => (
             <li key={task.id}>
               <input
                 type="text"
                 value={task.title}
                 onChange={(e) => updateTask(task.id, e.target.value)}
               />
               <button onClick={() => deleteTask(task.id)}>Delete</button>
             </li>
           ))}
         </ul>
       </div>
     );
   }

4. Test the Task Management Application

  • Add a new task to see it reflected in the list.
  • Update an existing task by editing the text input.
  • Delete a task to remove it from the list.

Each action should trigger the corresponding serverless function, updating the SQLite database.

Step 5: Deploying to Vercel

Once your application is ready, deploying to Vercel is simple. Vercel integrates smoothly with Next.js, offering an optimized platform for hosting serverless functions.

1. Deploying Your Application

If you haven’t already, sign in to Vercel and connect your GitHub, GitLab, or Bitbucket account.In your terminal, run:

vercel

Follow the prompts to deploy your project. Vercel will automatically detect your Next.js application and deploy it with serverless functions.

2. Managing Serverless Functions on Vercel

Vercel offers a dashboard where you can view logs, monitor performance, and manage your serverless functions. This is especially helpful for debugging and optimizing your application.

Conclusion

In this guide, we've explored how to build serverless functions using Next.js 14 and deploy them with Vercel. From handling simple API requests to managing complex CRUD operations with a database, serverless functions offer a scalable and efficient solution for modern web development. By leveraging these functions, you can build powerful applications without the overhead of traditional server management.

Serverless architecture is the future of web development, and with tools like Next.js 14 and Vercel, you're well-equipped to build applications that are not only robust but also easy to maintain and scale. Happy coding!

Muhaymin Bin Mehmood

About Muhaymin Bin Mehmood

Front-end Developer skilled in the MERN stack, experienced in web and mobile development. Proficient in React.js, Node.js, and Express.js, with a focus on client interactions, sales support, and high-performance applications.

Copyright © 2024 Mbloging. All rights reserved.