bun add rebun-sdkSetup
import { createClient } from "rebun-sdk"
type Schema = { posts: { id: string; title: string | null } }
const rebun = createClient<Schema>({ apiKey: process.env.REBUN_API_KEY })Guarde a chave em variável de ambiente, nunca no código. No navegador, use uma chave criada com "Permitir uso no navegador".
No navegador, use um único cliente no app inteiro. No servidor, um cliente atende todos os usuários, então ele nunca guarda sessão em memória: passe cookies (veja Servidor) ou use withToken. Sem nenhum dos dois, os métodos de login e o setToken lançam erro no servidor.
Resultados e erros
Nenhum método lança: todos resolvem { data, error }, e checar error estreita o tipo de data. Só o uso errado lança um Error: chamada sem apiKey (checado na primeira chamada, não no createClient, para um cliente de servidor poder ser importado no bundle do navegador) ou login no servidor sem cookies. As listagens trazem também o total (do header X-Total-Count), null no erro.
const { data, total, error } = await rebun.db.from("posts")
if (error) {
console.error(error.status, error.code)
return
}Banco de dados
const posts = rebun.db.from("posts")
await posts // os 20 primeiros, todas as colunas: { data: Post[], total }
await posts
.select(["title", "views"]) // ou "*" (padrão)
.where("published", "==", "true")
.or((q) => q.where("views", ">=", "100").where("title", "ilike", "%rebun%"))
.orderBy("views", "desc") // direção padrão "asc"; cada chamada soma um critério
.skip(20)
.take(10)
await posts.get(id)
await posts.create({ title: "Hello" })
await posts.update(id, { title: "New" })
await posts.delete(id)A listagem sai no await, e cada await faz um request novo. Operadores: ==, !=, >, >=, <, <=, in, not in (com lista), like e ilike (só em colunas text). null só com ==/!=. where seguidos são AND; .or(q => ...) e .and(q => ...) criam um grupo unido por esse operador, e grupos aninham. Colunas array e jsonb não podem ser filtradas. Colunas jsonb são lidas e gravadas como JSON (tipo Json), e uma string gravada nelas é lida como texto JSON. .orderBy(coluna, "asc" | "desc") ordena por qualquer coluna, até 5 critérios, com null no fim em asc e no começo em desc; sem ele, a ordem é created_at desc. Consulta inválida volta com error.code INVALID_QUERY (422). .take(n) vai até 100; acima disso, a API responde 422: pagine com .skip(n).
Um caminho <fk>.<coluna> no select traz o registro que a FK referencia: .select(["title", "author.name"]) devolve { title, author: { name } }, tipado como Record<string, unknown> | null. author vem null quando a referência está vazia ou quem consulta não pode ler aquele registro. Só um nível, com até 5 FKs por select; where e orderBy continuam nas colunas da própria tabela.
Storage
const avatars = rebun.storage.bucket("avatars")
await avatars.upload("users/me.png", blob)
await avatars.list({ search: "users/" })
await avatars.url("users/me.png")
await avatars.delete("users/me.png")O upload assina a URL, envia os bytes e confirma o arquivo numa chamada só.
Auth
O SDK roda o PKCE e guarda o access token (em memória no navegador, em cookie no servidor), que todas as chamadas do db passam a enviar.
await rebun.auth.signIn("otp", { email, redirectUri: "https://app.com/callback" })
await rebun.auth.verify({ email, code })
await rebun.auth.signUp({ email, password, redirectUri: "https://app.com/callback" })
await rebun.auth.verifySignUp({ email, code })
await rebun.auth.signIn("password", { email, password, redirectUri: "https://app.com/callback" })
await rebun.auth.resetPassword({ email })
await rebun.auth.confirmReset({ email, code, password })
const { data } = await rebun.auth.signIn("google", { redirectUri: "https://app.com/callback" })
await rebun.auth.exchangeCode(code)
await rebun.auth.getSession()
await rebun.auth.signOut()Na senha, o cadastro só vale depois do código enviado por e-mail, e o signIn("password") já devolve a sessão. Confirmar o reset encerra todas as sessões do usuário.
No navegador, o token fica em memória e some ao recarregar a página, a não ser que o app o devolva com rebun.auth.setToken(token).
Servidor com cookies httpOnly
No servidor, dê ao cliente os cookies da requisição atual. O login grava o access token no cookie httpOnly rebun-token (e o verificador PKCE em rebun-pkce até o callback), toda chamada o envia, e o signOut o apaga. O JS do navegador nunca vê o token.
Quando o framework sabe qual é a requisição atual (o cookies() do Next), passe uma função: ela roda a cada chamada, então um único cliente atende todos os usuários.
// lib/rebun.ts
import { cookies } from "next/headers"
export const rebun = createClient({
apiKey: process.env.REBUN_API_KEY!,
cookies: async () => {
const jar = await cookies()
return {
get: (name) => jar.get(name)?.value,
set: (name, value, options) => jar.set(name, value, options),
}
},
})
// app/callback/route.ts
await rebun.auth.exchangeCode(code) // grava rebun-token nesta requisiçãoQuando o handler recebe a requisição como argumento (ex. Express), crie um cliente por requisição com os cookies em objeto: createClient({ apiKey, cookies: { get, set } }).
Os cookies saem com httpOnly, secure, sameSite: "lax", path: "/" e maxAge igual ao expiresIn do token. Server Components do Next não gravam cookies: entre e saia por uma Route Handler ou Server Action. Se uma chamada logo depois do login, na mesma requisição, já enxerga o cookie novo depende do framework; a requisição seguinte sempre enxerga. Com a função, o setToken lança erro, porque o cliente é compartilhado.
Se você já tem o token do usuário, mantenha um cliente global e use rebun.withToken(accessToken) a cada requisição.