Web DevelopmentApr 24, 20262 min read9 views

Supabase Setup for Authentication and Database

Wire up Supabase as your backend in under an hour — authentication, Postgres, row-level security, and a typed client. Real-world patterns for a Next.js app.

A
Admin
Supabase Setup for Authentication and Database

Supabase is the open-source Firebase alternative — managed Postgres, built-in auth, generated REST and GraphQL APIs, real-time subscriptions, and edge functions. For a small SaaS or side project it removes most of the backend boilerplate. This tutorial sets it up cleanly with a Next.js front end.

Create a project

  1. Sign in to app.supabase.com.
  2. Click "New project", pick a region close to your users, set a strong database password, and wait two minutes for provisioning.
  3. Copy the project URL and anon key from Project Settings → API.

Wire up Next.js

pnpm add @supabase/supabase-js @supabase/ssr
# .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=ey...
// src/lib/supabase/server.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

export async function supabaseServer() {
  const cookieStore = await cookies();
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll: () => cookieStore.getAll(),
        setAll: (cs) => cs.forEach((c) => cookieStore.set(c.name, c.value, c.options)),
      },
    },
  );
}

Authentication in three lines

import { supabaseServer } from "@/lib/supabase/server";

export default async function Page() {
  const supabase = await supabaseServer();
  const { data } = await supabase.auth.getUser();
  return <p>Hello, {data.user?.email ?? "guest"}</p>;
}

Tables with row-level security

Open the Supabase dashboard → SQL editor. Create a table and turn on RLS so users can only access their own rows.

CREATE TABLE notes (
  id BIGSERIAL PRIMARY KEY,
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  body TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE notes ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users see only their notes"
  ON notes FOR SELECT
  USING (auth.uid() = user_id);

CREATE POLICY "Users insert their own notes"
  ON notes FOR INSERT
  WITH CHECK (auth.uid() = user_id);

Generated TypeScript types

pnpm dlx supabase gen types typescript --project-id <ref> > src/lib/supabase/types.ts

Now supabase.from("notes").select() is fully typed — no manual interfaces to maintain.

Real-time subscriptions

supabase
  .channel("notes")
  .on("postgres_changes", { event: "INSERT", schema: "public", table: "notes" }, (payload) => {
    console.log("new note:", payload.new);
  })
  .subscribe();

When Supabase fits and when it doesn't

  • Great for: SaaS MVPs, side projects, anything CRUD-heavy with auth.
  • Less great for: heavy custom backend logic, intensive write workloads at scale, multi-region needs that Supabase doesn't yet cover natively.
#supabase#postgres#nextjs#typescript#database