Pular para o conteúdo

Estrutura do Projeto

Um projeto inicial do Rebase possui três pacotes interconectados:

my-app/
├── .env # Generated for you: JWT_SECRET, a database password, a free port
├── rebase.json # Which apps this repository contains, and how each is built
├── package.json # Root workspace config
├── tsconfig.json # Typechecks scripts/; each workspace typechecks on its own
├── docker-compose.yml # Self-hosting: Postgres + the published runtime image
├── scripts/
│ └── example.ts # The SDK end to end against the running backend: `pnpm example`
│
├── config/ # Shared by the backend and the admin panel
│ ├── index.ts # Re-exports `collections`; the runtime reads `storageAuthorize` from here
│ ├── collections/ # Your data model
│ │ ├── index.ts # Sort order (`collections`) and `defaultSecurityRules` — not a registry
│ │ ├── posts.ts # Example collections
│ │ └── users.ts # The auth collection
│ ├── resources.ts # What this project needs from wherever it runs
│ ├── storage.ts # Who may read, write and list files
│ └── cms.d.ts # One line that makes the `admin` block legal here
│
├── backend/
│ ├── functions/ # Custom API routes, auto-mounted at /api/functions/<name>
│ │ └── hello.ts
│ └── src/
│ └── schema.generated.ts # Drizzle schema, regenerated from your collections
│
└── frontend/ # The admin panel (React + Vite)
├── src/App.tsx
├── src/main.tsx
└── vite.config.ts

O tsc --noEmit na raiz verifica scripts/; em config/, backend/ ou frontend/ ele verifica aquele workspace. Desde 0.25 Na 0.24 a raiz não tem tsconfig.json, e o tsc ali imprime a sua ajuda e não verifica nada.

O frontend é uma aplicação padrão Vite + React + TypeScript. O arquivo principal é o App.tsx, que conecta todos os controladores do Rebase:

import React from "react";
import "@fontsource/jetbrains-mono";
import "@fontsource-variable/inter";
import "@fontsource-variable/instrument-sans";
import { Rebase, RebaseAuth, useRebaseAuthController } from "@rebasepro/app";
import { RebaseCMS, RebaseShell } from "@rebasepro/cms";
import { ErrorBoundary } from "@rebasepro/ui";
import { RebaseStudio } from "@rebasepro/studio";
import { createRebaseClient } from "@rebasepro/client";
import { collections } from "virtual:rebase-collections";
// `rebase dev` injects VITE_API_URL with the port it actually bound, and that
// port is derived from this project's path rather than fixed — so a
// `http://localhost:3001` fallback here names a port nothing is listening on.
// A deployed build serves the admin from the same origin as the API, where an
// empty value is exactly what you want.
const API_URL = import.meta.env.VITE_API_URL;
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
export function App() {
const rebaseClient = React.useMemo(() => createRebaseClient({
baseUrl: API_URL,
// Store the refresh token in an httpOnly cookie (XSS-safe) rather than
// localStorage. The backend issues it via `auth.cookieAuth`.
auth: { authFlowMode: "cookie" }
}), []);
const authController = useRebaseAuthController({
client: rebaseClient,
googleClientId: GOOGLE_CLIENT_ID
});
return (
<ErrorBoundary fullPage>
<Rebase
client={rebaseClient}
authController={authController}
>
{/* The sign-in screen. On its own this changes nothing —
it is where you pass `loginView` to replace it. */}
<RebaseAuth />
<RebaseCMS
collections={collections}
/>
<RebaseStudio/>
<RebaseShell title="Rebase"/>
</Rebase>
</ErrorBoundary>
);
}

O main.tsx o monta sob um basename do react-router obtido a partir de import.meta.env.BASE_URL, que o rebase build define a partir do path declarado por este aplicativo no rebase.json — para que os assets, o roteador e o servidor concordem em um único valor sem que ele precise ser escrito três vezes.

  • createRebaseClient — Cria o cliente SDK que lida com requisições HTTP, conexões WebSocket e gerenciamento de tokens de autenticação
  • virtual:rebase-collections — Um plugin do Vite que importa automaticamente suas coleções compartilhadas em tempo de build
  • useRebaseAuthController — Mantém o usuário autenticado e o ciclo de vida do token, sendo o que o <Rebase> distribui para tudo abaixo dele

Não há nenhum arquivo de servidor para ler, e esse é o design pretendido: um projeto gerado declara runtime: "managed", portanto a imagem publicada de rebasepro/server inicializa seu projeto. O que a pasta backend/ contém é o código que o runtime utiliza:

Caminho O que é
backend/functions/ Rotas personalizadas, montadas automaticamente em /api/functions/<filename>
backend/crons/ Tarefas agendadas, descobertas da mesma forma (crie quando precisar de uma)
backend/src/schema.generated.ts O schema do Drizzle das suas coleções: escrito pelo rebase init e regenerado a cada rebase dev e rebase build

O runtime configura:

  • API REST em /api/data/* — CRUD gerado para cada coleção
  • Autenticação em /api/auth/* — cadastro, login, atualização de token (refresh), OAuth
  • Armazenamento (Storage) em /api/storage/* — upload e download
  • WebSocket — sincronização em tempo real via Postgres LISTEN/NOTIFY
  • Suas funções e crons, a partir dos diretórios acima

A configuração vem de rebase.json, do diretório config/ e de variáveis de ambiente. Consulte Ambiente e Configuração.

O rebase build transforma tudo isso em um bundle — as coleções, funções e crons compilados, além de um manifesto — que o runtime gerenciado inicializa. Nada no bundle é escrito manualmente; se você quiser ver um, Runtime e Bundles mostra o que há nele.

O painel fornecido pelo frontend tem duas metades. O Studio é a parte voltada ao desenvolvedor — o editor de schema, o console SQL, o navegador de políticas de RLS — e está localizado atrás do toggle na gaveta lateral (drawer), não sendo uma implantação separada. Consulte Studio.

Para assumir o controle do processo — seus próprios middlewares, rotas e estrutura de autenticação —, execute rebase eject. Tudo abaixo deste parágrafo aplica-se apenas a projetos ejetados (ejected-only): um projeto recém-gerado não possui nenhum desses arquivos, e nada nele chama initializeRebaseBackend. O comando cria um ponto de entrada que chama initializeRebaseBackend diretamente, além de um Dockerfile e um arquivo compose que o compila; a partir de então, você mantém o servidor e as atualizações do runtime da plataforma não afetam mais o projeto. Essa interface está documentada em Integração de Servidor Personalizado.

As coleções são a única fonte da verdade para o seu modelo de dados. Elas são definidas em TypeScript e consumidas tanto pelo frontend (para geração da UI) quanto pelo backend (para geração do schema e roteamento da API).

import { defineCollection } from "@rebasepro/cms-types";
const productsCollection = defineCollection({
slug: "products",
name: "Products",
properties: {
name: { type: "string", name: "Name" },
price: { type: "number", name: "Price" }
}
});
// The default export is what the loader picks up — every file in this directory
// is loaded, listed in index.ts or not, and every collection in the scaffold is
// written this way.
export default productsCollection;

O slug torna-se o caminho da URL na interface administrativa e o endpoint da API REST (/api/data/products), e o nome da tabela do PostgreSQL é definido com ele por padrão. Adicione table apenas quando forem diferentes.

  1. Você define as coleções em config/collections/
  2. O backend as lê para gerar schemas do Drizzle e montar rotas REST
  3. O frontend as lê (via plugin do Vite) para renderizar tabelas, formulários e navegação
  4. A CLI as lê para gerar o schema do Drizzle (rebase schema generate), os tipos do SDK (rebase generate-sdk) e, a partir desse schema, arquivos de migração (rebase db generate)

Enquanto o rebase dev estiver em execução, salvar um arquivo em config/collections/ regenera backend/src/schema.generated.ts e os tipos do SDK em generated/sdk/, reinicia o backend, e a inicialização cria as tabelas e colunas que estiverem faltando. Fora do rebase dev, essas mesmas etapas são realizadas com rebase schema generate e rebase generate-sdk.

Na versão 0.23, o rebase dev regenera apenas o schema, e o rebase generate-sdk é uma etapa própria.