Ploy
Ploy
Next.js

Database

Use SQLite databases in your Next.js app with Ploy.

Database

Add SQLite databases to your Next.js application with the Cloudflare D1-compatible API.

Configuration

Add a database binding in your ploy.yaml:

ploy.yaml
kind: dynamic
build: pnpm build
out: dist
db:
  DB: default

Generate types:

pnpm ploy types

API Route Example

app/api/users/route.ts
import { NextResponse } from "next/server";
import { getPloyContext } from "@meetploy/nextjs";

interface User {
	id: number;
	name: string;
	email: string;
}

export async function GET() {
	const { env } = getPloyContext();

	const result = await env.DB.prepare("SELECT * FROM users").all<User>();

	return NextResponse.json({ users: result.results });
}

export async function POST(request: Request) {
	const { env } = getPloyContext();
	const { name, email } = await request.json();

	const result = await env.DB.prepare(
		"INSERT INTO users (name, email) VALUES (?, ?)",
	)
		.bind(name, email)
		.run();

	return NextResponse.json({ success: true, meta: result.meta });
}

Server Component Example

app/users/page.tsx
import { getPloyContext } from "@meetploy/nextjs";

interface User {
  id: number;
  name: string;
}

export default async function UsersPage() {
  const { env } = getPloyContext();

  const { results: users } = await env.DB.prepare(
    "SELECT * FROM users"
  ).all<User>();

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Query Methods

all() - Get All Rows

const { results } = await env.DB.prepare("SELECT * FROM users").all<User>();

first() - Get First Row

const user = await env.DB.prepare("SELECT * FROM users WHERE id = ?")
	.bind(1)
	.first<User>();

run() - Execute Statement

const result = await env.DB.prepare("INSERT INTO users (name) VALUES (?)")
	.bind("Alice")
	.run();

exec() - Run Raw SQL

await env.DB.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
  )
`);

Always use prepared statements with .bind() to prevent SQL injection.

Initialize Tables

Create an API route to initialize your database schema:

app/api/init/route.ts
import { NextResponse } from "next/server";
import { getPloyContext } from "@meetploy/nextjs";

export async function GET() {
	const { env } = getPloyContext();

	await env.DB.exec(`
    CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      email TEXT UNIQUE NOT NULL,
      created_at TEXT DEFAULT CURRENT_TIMESTAMP
    )
  `);

	return NextResponse.json({ success: true });
}

Local Development

During development, the local Ploy dashboard at http://localhost:4000 lets you:

  • Browse table contents
  • Execute SQL queries
  • Inspect database structure

Data persists across restarts in the .ploy directory.

Next Steps

  • Queues - Process background jobs
  • Workflows - Build durable multi-step processes

How is this guide?

Last updated on