Pular para o conteúdo
▲ QUICKSTART

Next.js + SuperDB.

Sessão em cookies httpOnly que se renova sozinha, Server Actions e dados protegidos por RLS. Comece pelo template oficial, ou siga o passo a passo.

O caminho mais rápido: o template oficial

O template Next.js já vem com tudo desta página funcionando: login e cadastro, sessão que se renova sozinha e uma lista de tarefas protegida por RLS. Ele é compilado a cada mudança contra a versão publicada do SDK.

terminal
npx create-next-app@latest meu-app --example https://github.com/superdb-br/templates/tree/main/nextjs
cd meu-app
cp .env.example .env.local
npm run dev

Preencha o .env.local com os dados do painel (projeto → Conectar), rode o arquivo superdb/schema.sql do template no SQL Editor e abra http://localhost:3000. A tela inicial confere a conexão e diz exatamente o que falta.

💡

Há templates para sete stacks: Next.js, React, Vue, SvelteKit, Nuxt, HTML puro e Expo. O mesmo app em todos.

Como funciona

No Next, quem busca os dados é o servidor (Server Components e Server Actions). O servidor não enxerga o localStorage do navegador, mas recebe os cookies em toda requisição. Por isso a sessão mora em cookies httpOnly, que nenhum JavaScript da página consegue ler.

O SDK @superdb/client guarda a sessão num armazenamento que você escolhe (getItem/setItem/removeItem). O template passa um armazenamento feito de cookies, e o resto do SDK funciona sem saber disso. Não existe pacote de SSR à parte: é esse adaptador de uma página.

Passo a passo (o que o template faz por você)

1Instalar o SDK

O @superdb/client segue a API do supabase-js: .auth, .from(), .rpc() e .storage.

terminal
npm install @superdb/client server-only

2Variáveis de ambiente

Os três dados vêm do painel: projeto → Conectar. O slug é o que aparece no endereço do projeto.

⚠️

A chave anon é pública: vai no código do app. Quem protege os dados é o RLS. A service key nunca vai num app.

.env.local
NEXT_PUBLIC_SUPERDB_URL=https://auth.superdb.com.br
NEXT_PUBLIC_SUPERDB_ANON_KEY=cole-a-chave-anon-aqui
NEXT_PUBLIC_SUPERDB_PROJECT=slug-do-projeto

3Criar a tabela com RLS

No SQL Editor do projeto. Toda tabela nova no SuperDB nasce fechada (RLS ligado, nenhuma policy): sem as últimas linhas, ninguém além do dono do projeto enxerga nada. Elas abrem para cada pessoa só as próprias linhas, e o revoke tira da chave anon até a permissão na tabela.

superdb/schema.sql
create table if not exists tarefas (
  id         uuid        primary key default gen_random_uuid(),
  titulo     text        not null check (char_length(btrim(titulo)) between 1 and 200),
  feita      boolean     not null default false,
  criada_em  timestamptz not null default now(),
  user_id    uuid        not null default auth.uid()
);

-- A lista do app sempre busca "as minhas, das mais novas para as mais antigas".
create index if not exists tarefas_por_dono on tarefas (user_id, criada_em desc);

alter table tarefas enable row level security;

-- Uma regra só, para ler, criar, alterar e apagar: a linha é sua ou não existe.
-- "using" filtra o que você vê; "with check" recusa gravar linha de outra pessoa.
drop policy if exists tarefas_do_dono on tarefas;
create policy tarefas_do_dono on tarefas
  for all to authenticated
  using (user_id = auth.uid())
  with check (user_id = auth.uid());

revoke all on tarefas from anon;
grant select, insert, update, delete on tarefas to authenticated;

4Um cliente por requisição, lendo os cookies

Crie o cliente dentro de cada Server Component ou Server Action: ele lê a sessão dos cookies desta requisição. Nunca o guarde numa variável de módulo, porque no servidor ela seria compartilhada entre visitantes.

A sessão vai em quatro cookies (acesso, renovação, dados e vencimento) porque a sessão inteira em JSON passaria de 4 KB, o limite de um cookie. O adaptador completo está em lib/superdb/cookies.ts.

lib/superdb/servidor.ts
import 'server-only'
import { cookies } from 'next/headers'
import { createClient } from '@superdb/client'
import { OPCOES_DO_COOKIE, armazenamentoEmCookies } from './cookies'

export async function superdbDoServidor() {
  const pote = await cookies()
  const armazenamento = armazenamentoEmCookies({
    ler: (nome) => pote.get(nome)?.value,
    // Só grava em Server Action e Route Handler; numa página o SDK só lê.
    gravar: (nome, valor) => pote.set(nome, valor, OPCOES_DO_COOKIE),
    apagar: (nome) => pote.delete(nome),
  })
  return createClient(
    process.env.NEXT_PUBLIC_SUPERDB_URL!,
    process.env.NEXT_PUBLIC_SUPERDB_ANON_KEY!,
    // Quem renova é o proxy.ts: renovar aqui trocaria o refresh token sem
    // poder regravar o cookie, e o próximo pedido cairia como reuso.
    { project: process.env.NEXT_PUBLIC_SUPERDB_PROJECT!, storage: armazenamento, autoRefreshToken: false },
  )
}

5Login e cadastro com Server Action

A senha vai direto do formulário para o servidor, e os tokens voltam só como cookies httpOnly. O erro do SDK vem em inglês (invalid_credentials); o template traduz em erros.ts.

app/acoes.ts
'use server'
import { redirect } from 'next/navigation'
import { superdbDoServidor } from '@/lib/superdb/servidor'

export async function entrar(_: unknown, form: FormData) {
  const superdb = await superdbDoServidor()
  const { error } = await superdb.auth.signInWithPassword({
    email: String(form.get('email')),
    password: String(form.get('senha')),
  })
  if (error) return { erro: error.code }
  redirect('/tarefas')
}

export async function sair() {
  const superdb = await superdbDoServidor()
  await superdb.auth.signOut()
  redirect('/')
}

6Renovar a sessão no proxy

O token vale 1 hora. No servidor, quem renova é o proxy.ts, e não o SDK: renovar troca o refresh token, e só o proxy consegue regravar o cookie antes de a página ler. Por isso o client do servidor nasce com autoRefreshToken: false (a opção existe desde a 0.3 do SDK). O proxy.ts (o antigo middleware.ts, renomeado no Next 16) roda antes de cada página e de cada Server Action, e é o lugar em que dá para regravar cookies antes de a página ler.

A armadilha: o SuperDB troca o refresh token a cada renovação e trata reuso como roubo, derrubando a sessão. Uma página dispara várias requisições quase juntas com o mesmo cookie. O renovar.ts do template faz uma renovação por refresh token e entrega a sessão nova, por 30 segundos, a quem chegar com o token antigo.

proxy.ts
import type { NextRequest } from 'next/server'
import { renovarSessao } from './lib/superdb/renovar'

export function proxy(request: NextRequest) {
  return renovarSessao(request)
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|.*\\.(?:svg|png|jpg|ico|css|js)$).*)'],
}

7Página protegida, com os dados buscados no servidor

A página confere a sessão por conta própria: o proxy renova, não protege. A consulta sai com o token de dados de quem entrou, e o RLS devolve só as linhas dessa pessoa, sem filtro nenhum no código.

app/tarefas/page.tsx
import { redirect } from 'next/navigation'
import { superdbDoServidor } from '@/lib/superdb/servidor'

export default async function PaginaTarefas() {
  const superdb = await superdbDoServidor()
  const { data: { session } } = await superdb.auth.getSession()
  if (!session) redirect('/')

  const { data: tarefas, error } = await superdb
    .from('tarefas')
    .select('id,titulo,feita')
    .order('criada_em', { ascending: false })

  if (error) return <p>{error.message}</p>
  return <ul>{tarefas?.map((t) => <li key={t.id}>{t.titulo}</li>)}</ul>
}

8Rodar

Crie uma conta na tela inicial e adicione tarefas. Server Actions de escrita seguem o mesmo molde: conferem a sessão, chamam superdb.from(...) e devolvem { erro } em vez de lançar exceção.

terminal
npm run dev
# → http://localhost:3000

Próximos passos

Essa página ajudou?