Next js

Module 8: Backend & API Integration in Next.js 14 with TypeScript

Build Full-Stack APIs, Connect to Databases, and Handle CRUD Operations

Next.js 14 makes it easier than ever to create and consume backend APIs using the App Router. In this module, you’ll learn to:

  • Create REST-style API routes under app/api/
  • Connect Prisma ORM to PostgreSQL or MongoDB
  • Build and call API endpoints
  • Integrate CRUD logic into Server Actions or Client Components

🧱 Folder Structure

src/
├── app/
│   ├── api/
│   │   └── users/
│   │       └── route.ts      ← API endpoint (POST, GET)
│   ├── page.tsx
├── lib/
│   └── prisma.ts             ← Prisma client setup
├── server/
│   └── actions/              ← Server Actions
├── components/

✅ Step 1: Set Up Prisma with PostgreSQL or MongoDB

npm install prisma @prisma/client
npx prisma init

🔹 .env (PostgreSQL example)

DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

🔹 prisma/schema.prisma

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

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

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

Run the migration:

npx prisma migrate dev --name init

✅ Step 2: Setup Prisma Client

// src/lib/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };

export const prisma =
  globalForPrisma.prisma || new PrismaClient({ log: ['query'] });

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

✅ Step 3: Create an API Route – POST /api/users

// src/app/api/users/route.ts
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";

export async function POST(req: Request) {
  const body = await req.json();
  const { name, email } = body;

  try {
    const user = await prisma.user.create({ data: { name, email } });
    return NextResponse.json(user, { status: 201 });
  } catch (error) {
    return NextResponse.json({ error: "User already exists." }, { status: 400 });
  }
}

export async function GET() {
  const users = await prisma.user.findMany();
  return NextResponse.json(users);
}

✅ Step 4: Call API from Client Component (e.g. Create User)

"use client";
import { useState } from "react";

export default function UserForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const res = await fetch("/api/users", {
      method: "POST",
      body: JSON.stringify({ name, email }),
      headers: { "Content-Type": "application/json" },
    });

    if (res.ok) {
      alert("User created!");
    } else {
      alert("Something went wrong");
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-md">
      <input
        className="border p-2 w-full"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
        required
      />
      <input
        className="border p-2 w-full"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        type="email"
        required
      />
      <button className="bg-blue-600 text-white px-4 py-2 rounded" type="submit">
        Create User
      </button>
    </form>
  );
}

✅ Step 5: Fetch Users from API

'use client';

import { useEffect, useState } from "react";

type User = { id: number; name: string; email: string };

export default function UserList() {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);

  return (
    <ul className="space-y-2 mt-6">
      {users.map(user => (
        <li key={user.id} className="border p-2 rounded">
          {user.name} ({user.email})
        </li>
      ))}
    </ul>
  );
}

🧠 Optional: Call Prisma Directly from Server Action

// src/server/actions/createUser.ts
'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;

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

Then use in a form:

<form action={createUser}>
  <input name="name" />
  <input name="email" />
  <button type="submit">Submit</button>
</form>

✅ Summary

In this module, you learned how to:

Task Tool/Example
Setup a database Prisma + PostgreSQL or MongoDB
Create backend APIs app/api/route.ts (RESTful APIs)
Connect UI to backend fetch() from Client Component
Full CRUD logic Server Actions or API calls

🔜 Coming Up Next:

In Module 9, we’ll add Authentication & Authorization using next-auth, implement protected routes, and build a role-based system for admin vs user access.

Leave a Reply

Your email address will not be published. Required fields are marked *