GUIDE

Your first app with Next.js

A to-do list where people sign up with their email and see only their own todos. You'll use the dashboard for the database and @espobase/client in the app.

← All guides

1. Create a project and a table

In the dashboard, create a project. Open SQL Editor and run:

create table todos (
  id bigint generated by default as identity primary key,
  owner_id uuid not null default espobase.user_id(),
  title text not null,
  done boolean not null default false,
  created_at timestamptz not null default now()
);

-- Row level security is already on. This rule lets each signed-in user see and change only their own todos:
create policy "Users manage their own todos" on todos
  for all
  using (owner_id = espobase.user_id())
  with check (owner_id = espobase.user_id());

Prefer clicking? Create the table in the Table Editor, then add the “User owns row” template in Database › Policies with owner_id as the owner column.

2. Settings and keys

  • Authentication › Settings: set the Site URL to where your app runs (e.g. http://localhost:3000 while developing). Confirmation and password-reset emails link there.
  • API: create a publishable key. Copy it now; keys are only shown when created.

In your Next.js app, install the client and add the settings to .env.local:

npm install @espobase/client
NEXT_PUBLIC_ESPOBASE_URL=https://api.espobase.com/project/<your project ref>
NEXT_PUBLIC_ESPOBASE_KEY=eb_pk_…   # the PUBLISHABLE key: safe in the browser

Never put a secret key (eb_sk_…) in a NEXT_PUBLIC_ variable: those are shipped to the browser. With the CLI, npx espobase init prints the URL and writes TypeScript types for you.

3. The client

// lib/espobase.ts
import { createClient } from "@espobase/client";

export const espobase = createClient(
  process.env.NEXT_PUBLIC_ESPOBASE_URL!,
  process.env.NEXT_PUBLIC_ESPOBASE_KEY!,
);

4. Sign-up and sign-in

"use client";
import { espobase } from "@/lib/espobase";

// Sign up: Espobase emails a confirmation link to your Site URL.
await espobase.auth.signUp({ email, password });

// Sign in (after confirming): the session is kept and refreshed for you.
await espobase.auth.signInWithPassword({ email, password });

// React to sign-in and sign-out anywhere in your app.
espobase.auth.onAuthStateChange((_event, session) => setSession(session));

The confirmation link opens your Site URL with the token after a #. Confirm it with espobase.auth.verifyEmail({ token }): see Sign-in and security rules for the few lines that do it, and for password reset.

5. Read and write your data

// Only the signed-in user's todos come back: the security rule filters them.
const { data: todos, error } = await espobase.from("todos").select("*").order("created_at");

// owner_id fills itself in (its default is espobase.user_id()).
await espobase.from("todos").insert({ title: "Buy milk" });

await espobase.from("todos").update({ done: true }).eq("id", todo.id);
await espobase.from("todos").delete().eq("id", todo.id);

Every request carries the signed-in user, and the database applies your rule. A user who isn't signed in gets an empty list, and nobody can read or change someone else's todos, even by calling the API directly.