143 lines
4.1 KiB
Plaintext
143 lines
4.1 KiB
Plaintext
---
|
|
alwaysApply: true
|
|
---
|
|
# Banco de Dados: Frontend → Remote Functions (SvelteKit) + Drizzle (Server)
|
|
|
|
Neste projeto, **o frontend NÃO se comunica diretamente com o banco**. Toda leitura/escrita no DB deve acontecer **no servidor**, usando **SvelteKit Remote Functions** (`*.remote.ts`) e **Drizzle ORM**.
|
|
|
|
## Regras obrigatórias
|
|
|
|
- **NUNCA** importe/instancie `db` ou tabelas do Drizzle no client:
|
|
- Proibido em `.svelte`, `+page.ts`, `+layout.ts`, `src/lib/**` (código compartilhado) etc.
|
|
- `db` e o schema (`$lib/server/db/**`) são **server-only**.
|
|
- Qualquer acesso ao DB deve ficar em **`*.remote.ts` co-localizado com a rota**.
|
|
- Remote functions devem:
|
|
- Importar `query`, `command`, `form` de `$app/server`
|
|
- Validar input com `z` de `zod/v4`
|
|
- Usar Drizzle via `db` de `$lib/server/db` e tabelas de `$lib/server/db/schema`
|
|
- O frontend deve consumir **apenas** as remote functions, e usar:
|
|
- `$derived(remoteQuery(args))` para promessas reativas
|
|
- `.updates(remoteQuery(args))` após `command()`/`form()` para invalidar seletivamente
|
|
|
|
## Exemplo 1: Query (leitura) com Drizzle
|
|
|
|
```ts
|
|
// src/routes/admin/users/users.remote.ts
|
|
import { query } from '$app/server';
|
|
import z from 'zod/v4';
|
|
import { ilike, or } from 'drizzle-orm';
|
|
|
|
import { db } from '$lib/server/db';
|
|
import { user } from '$lib/server/db/schema';
|
|
|
|
export const listUsers = query(
|
|
z.object({
|
|
search: z.string().trim().min(1).optional()
|
|
}),
|
|
async ({ search }) => {
|
|
const where =
|
|
search
|
|
? or(ilike(user.name, `%${search}%`), ilike(user.email, `%${search}%`))
|
|
: undefined;
|
|
|
|
return await db
|
|
.select({
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
createdAt: user.createdAt
|
|
})
|
|
.from(user)
|
|
.where(where);
|
|
}
|
|
);
|
|
```
|
|
|
|
## Exemplo 2: Command (mutação) com Drizzle + invalidação
|
|
|
|
```ts
|
|
// src/routes/admin/users/users.remote.ts
|
|
import { command } from '$app/server';
|
|
import z from 'zod/v4';
|
|
import { eq } from 'drizzle-orm';
|
|
|
|
import { db } from '$lib/server/db';
|
|
import { user } from '$lib/server/db/schema';
|
|
|
|
export const deleteUser = command(z.object({ id: z.string().min(1) }), async ({ id }) => {
|
|
await db.delete(user).where(eq(user.id, id));
|
|
// Se fizer sentido, você pode forçar refresh global da query para todos os clientes:
|
|
// await listUsers({ search: undefined }).refresh();
|
|
});
|
|
```
|
|
|
|
## Exemplo 3: Form (submit) com Drizzle
|
|
|
|
```ts
|
|
// src/routes/admin/users/users.remote.ts
|
|
import { form } from '$app/server';
|
|
import z from 'zod/v4';
|
|
import { eq } from 'drizzle-orm';
|
|
|
|
import { db } from '$lib/server/db';
|
|
import { user } from '$lib/server/db/schema';
|
|
|
|
export const updateUserName = form(
|
|
z.object({
|
|
id: z.string().min(1),
|
|
name: z.string().trim().min(2).max(100)
|
|
}),
|
|
async ({ id, name }) => {
|
|
await db.update(user).set({ name }).where(eq(user.id, id));
|
|
return { success: true as const };
|
|
}
|
|
);
|
|
```
|
|
|
|
## Exemplo 4: Frontend consumindo remote functions (sem acesso ao DB)
|
|
|
|
```svelte
|
|
<!-- src/routes/admin/users/+page.svelte -->
|
|
<script lang="ts">
|
|
import { listUsers, deleteUser, updateUserName } from './users.remote';
|
|
|
|
let search = $state('');
|
|
let usersPromise = $derived(listUsers({ search: search || undefined }));
|
|
</script>
|
|
|
|
<input bind:value={search} placeholder="Buscar por nome ou email" />
|
|
|
|
{#await usersPromise then users}
|
|
<ul>
|
|
{#each users as u}
|
|
<li>
|
|
<strong>{u.name}</strong> ({u.email})
|
|
<button
|
|
type="button"
|
|
onclick={async () => {
|
|
await deleteUser({ id: u.id }).updates(listUsers({ search: search || undefined }));
|
|
}}
|
|
>
|
|
Remover
|
|
</button>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/await}
|
|
|
|
<form
|
|
{...updateUserName.enhance(async ({ submit }) => {
|
|
await submit().updates(listUsers({ search: search || undefined }));
|
|
})}
|
|
>
|
|
<!-- inputs do updateUserName.fields.* -->
|
|
<button type="submit">Salvar</button>
|
|
</form>
|
|
```
|
|
|
|
## Checklist rápido (antes de abrir PR)
|
|
|
|
- A leitura/escrita no DB está **somente** em `*.remote.ts`?
|
|
- O `.svelte` importa apenas remote functions (e não `db`/schema)?
|
|
- Inputs das remote functions estão validados com `zod/v4`?
|
|
- Mutação faz `.updates()` (ou `.refresh()` server-side) quando precisa atualizar listas? |