Thuta Learning
AdvancedWeb Developmentintermediate

Separating Out a Database Layer

Relax. We'll talk through this in plain words — no textbook voice.

What you'll walk away with

  • Create a data access layer
  • Protect server-only code
  • Avoid tightly coupling the UI to the database schema

Writing database queries directly in every single page is fast at first, but once you need to change a query, add permissions, or write tests, you end up with duplicated code everywhere. A thin data layer pays off once the project grows.

The mental model

Put use-case functions like list, detail, and create in lib/data/notes.ts. Importing the server-only package means a Client Component that accidentally imports it fails fast with a build error. Instead of sending the entire ORM result to the page, select only the fields you need. Follow the ORM's official Next.js pattern for reading the connection string from an environment variable and storing it on a global object.

Let's build it together

typescript
// lib/data/notes.ts
import "server-only";
import { db } from "@/lib/db";

export async function getPublishedNotes() {
  return db.note.findMany({
    where: { published: true },
    select: { id: true, title: true, updatedAt: true },
    orderBy: { updatedAt: "desc" },
  });
}

export async function getNoteById(id: string) {
  return db.note.findUnique({ where: { id } });
}

How the code works

getPublishedNotes selects only published records and returns just the three fields the UI needs. The page can call this function without knowing anything about the database implementation.

You should see
The UI can get typed note data from the data layer without knowing any database details.

5-Minute Try-It

Write a getNotesByUser(userId) function that returns an error instead of querying when userId is missing.

Next.js — Fetching DataNext.js

Easy traps

  • Importing the ORM client and database secret into a Client Component
  • Selecting way more fields than needed and sending sensitive data along with it

Exercise

Write a getNotesByUser(userId) function that returns an error instead of querying when userId is missing.

You'll know it worked when: The UI can get typed note data from the data layer without knowing any database details.