Next.js Server Actions Integration with MySQL to Build a CRUD App

Next.js 13+ introduces Server Actions, an innovative feature that simplifies server-side logic handling while keeping API calls efficient. When combined with MySQL, this enables developers to build powerful, server-driven applications with optimized database interactions.

In this blog post, we'll walk through integrating Next.js Server Actions with MySQL to create a CRUD (Create, Read, Update, Delete) application.


Why Use Server Actions in Next.js?

Traditionally, Next.js applications required API routes (pages/api or app/api) for server-side logic. With Server Actions, you can now handle form submissions and data mutations directly in the component without creating separate API endpoints.

Benefits of Server Actions

  • Simplified codebase: No need for additional API routes.
  • Improved performance: Direct server-side execution reduces unnecessary client-server interactions.
  • Built-in security: Avoids exposing database credentials in the client.

Step 1: Setting Up a Next.js Project

First, create a new Next.js project with TypeScript enabled:

npx create-next-app@latest my-next-crud-app --typescript
cd my-next-crud-app

Step 2: Install MySQL and Prisma ORM

To interact with MySQL, we’ll use Prisma, a powerful ORM for managing database queries in TypeScript.

Install Dependencies

npm install mysql2 @prisma/client
npm install --save-dev prisma

Initialize Prisma

Run the following command to generate a Prisma configuration:

npx prisma init

This will create a .env file and a prisma/schema.prisma file.


Step 3: Configure MySQL Connection

In your .env file, update the DATABASE_URL with your MySQL credentials:

DATABASE_URL="mysql://user:password@localhost:3306/mydatabase"

Replace:

  • user with your MySQL username.
  • password with your MySQL password.
  • mydatabase with the database name.

Step 4: Define a Prisma Model

Edit prisma/schema.prisma to define a User model:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int     @id @default(autoincrement())
  name      String
  email     String  @unique
  createdAt DateTime @default(now())
}

Now, apply the changes to your database:

npx prisma migrate dev --name init

Step 5: Implement Server Actions for CRUD Operations

1. Create a User

In app/actions.ts, define a server action to insert a new user.

"use server";
import prisma from "@/lib/prisma";

export async function createUser(formData: FormData) {
  const name = formData.get("name") as string;
  const email = formData.get("email") as string;

  await prisma.user.create({
    data: { name, email },
  });

  return { message: "User created successfully" };
}

2. Read Users

In app/actions.ts, define a function to fetch users.

export async function getUsers() {
  return await prisma.user.findMany();
}

3. Update a User

export async function updateUser(id: number, name: string) {
  return await prisma.user.update({
    where: { id },
    data: { name },
  });
}

4. Delete a User

export async function deleteUser(id: number) {
  return await prisma.user.delete({ where: { id } });
}

Step 6: Build the Next.js UI with Server Actions

User Form Component

In app/page.tsx, add a simple form to create users.

"use client";
import { useState } from "react";
import { createUser } from "./actions";

export default function Home() {
  const [message, setMessage] = useState("");

  async function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    const formData = new FormData(event.target as HTMLFormElement);
    const response = await createUser(formData);
    setMessage(response.message);
  }

  return (
    <div>
      <h1>Next.js MySQL CRUD</h1>
      <form onSubmit={handleSubmit}>
        <input type="text" name="name" placeholder="Name" required />
        <input type="email" name="email" placeholder="Email" required />
        <button type="submit">Create User</button>
      </form>
      {message && <p>{message}</p>}
    </div>
  );
}

Step 7: Display Users in a List

Modify app/page.tsx to display users:

import { getUsers, deleteUser } from "./actions";

export default async function UserList() {
  const users = await getUsers();

  async function handleDelete(id: number) {
    await deleteUser(id);
  }

  return (
    <div>
      <h2>Users List</h2>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            {user.name} ({user.email}) 
            <button onClick={() => handleDelete(user.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Step 8: Testing and Running the App

To start your Next.js app, run:

npm run dev

Visit http://localhost:3000 to interact with the CRUD application.


Conclusion

With Next.js Server Actions and MySQL, we built a simple CRUD application while keeping the backend logic minimal. Server Actions simplify API handling, reduce client-server interactions, and improve security by avoiding direct exposure of database logic.