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!
Learn how to integrate GraphQL with Next.js 14
Learn How to use TypeScript with React: Benefits and Best Practices
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.