347 lines
8.1 KiB
Markdown
347 lines
8.1 KiB
Markdown
# Les 7 — Lesstof
|
|
## Supabase Setup + eerste queries
|
|
|
|
**Vak:** Technical Foundations
|
|
**Vorige les:** Les 6 — Next.js QuickPoll deel 2
|
|
**Volgende les:** Les 8 — Supabase CRUD + Mutations
|
|
|
|
---
|
|
|
|
## Inhoud
|
|
|
|
1. [Wat is Supabase](#1-wat-is-supabase)
|
|
2. [Supabase vs alternatieven](#2-supabase-vs-alternatieven)
|
|
3. [Account + project aanmaken](#3-account--project-aanmaken)
|
|
4. [Postgres basics](#4-postgres-basics)
|
|
5. [Tabel aanmaken in Supabase](#5-tabel-aanmaken-in-supabase)
|
|
6. [Foreign keys + relaties](#6-foreign-keys--relaties)
|
|
7. [RLS — Row Level Security (intro)](#7-rls--row-level-security-intro)
|
|
8. [SQL Editor — eerste queries](#8-sql-editor--eerste-queries)
|
|
9. [Supabase client in Next.js](#9-supabase-client-in-nextjs)
|
|
10. [Eerste SELECT vanuit Server Component](#10-eerste-select-vanuit-server-component)
|
|
|
|
---
|
|
|
|
## 1. Wat is Supabase
|
|
|
|
**Supabase** = open-source Backend-as-a-Service. Geef je een Postgres database + auth + storage + realtime + functions via één dashboard. Gratis tier voor leren + kleine projecten.
|
|
|
|
### Wat krijg je
|
|
|
|
- **Postgres database** — echte SQL, geen NoSQL
|
|
- **Auth** — login, signup, magic link, social (Les 9-10)
|
|
- **Storage** — files uploaden (later)
|
|
- **Realtime** — subscribe op DB changes (later)
|
|
- **Edge Functions** — serverless deno-runtime (later)
|
|
- **Dashboard** — visuele tabel-editor, SQL editor, auth-management
|
|
|
|
### Open source
|
|
|
|
Volledige code op GitHub. Self-hostbaar als je wilt. Geen vendor lock-in.
|
|
|
|
---
|
|
|
|
## 2. Supabase vs alternatieven
|
|
|
|
| | Supabase | Firebase | Planetscale | Neon |
|
|
|---|----------|----------|-------------|------|
|
|
| Database | Postgres | NoSQL (Firestore) | MySQL | Postgres |
|
|
| Auth | ✓ | ✓ | ✗ | ✗ |
|
|
| Pricing | Free tier ruim | Pay-per-read | Free tier | Free tier |
|
|
| Open source | ✓ | ✗ | ✗ | Onderdelen |
|
|
|
|
**Voor deze leerlijn: Supabase.** Postgres + auth + dashboard alles-in-één. Geen extra services nodig voor 90% van je app.
|
|
|
|
---
|
|
|
|
## 3. Account + project aanmaken
|
|
|
|
### Account
|
|
|
|
Ga naar supabase.com → Sign Up (mag met GitHub).
|
|
|
|
### Project
|
|
|
|
1. New project
|
|
2. Org kiezen (default = je naam)
|
|
3. **Naam:** `quickpoll`
|
|
4. **Database password:** sla op (lange random string)
|
|
5. **Region:** `Europe (Frankfurt)` voor NL gebruikers
|
|
6. **Plan:** Free
|
|
7. Wacht 1-2 min op provisioning
|
|
|
|
### Wat krijg je
|
|
|
|
- **Project URL:** `https://abc123.supabase.co`
|
|
- **Anon key:** publieke API key (mag in client)
|
|
- **Service role key:** admin key (NIET in client!)
|
|
|
|
Beide te vinden via Settings → API.
|
|
|
|
---
|
|
|
|
## 4. Postgres basics
|
|
|
|
### Wat is Postgres
|
|
|
|
Relationele SQL database. Sinds 1986, krachtige + betrouwbaar. Industrie-standaard.
|
|
|
|
### Tabellen + kolommen
|
|
|
|
```
|
|
polls
|
|
┌────┬──────────────────┬──────────────┐
|
|
│ id │ title │ created_at │
|
|
├────┼──────────────────┼──────────────┤
|
|
│ 1 │ Beste taal? │ 2026-06-04 │
|
|
│ 2 │ Beste IDE? │ 2026-06-04 │
|
|
└────┴──────────────────┴──────────────┘
|
|
```
|
|
|
|
### Veelgebruikte types
|
|
|
|
| Type | Wanneer |
|
|
|------|---------|
|
|
| `bigserial` | Auto-increment ID |
|
|
| `uuid` | Random UUID (default `gen_random_uuid()`) |
|
|
| `text` | Variabele tekst |
|
|
| `integer` | Geheel getal |
|
|
| `boolean` | true/false |
|
|
| `timestamp` | Datum + tijd |
|
|
| `jsonb` | JSON (queryable) |
|
|
|
|
### Constraints
|
|
|
|
- `primary key` — unieke identifier
|
|
- `not null` — verplicht veld
|
|
- `unique` — geen duplicaten
|
|
- `references` — foreign key
|
|
- `default` — automatische waarde bij niet meegegeven
|
|
|
|
---
|
|
|
|
## 5. Tabel aanmaken in Supabase
|
|
|
|
### Via Dashboard
|
|
|
|
1. Table Editor → New table
|
|
2. Naam: `polls`
|
|
3. Kolommen:
|
|
- `id` — bigserial — primary key
|
|
- `title` — text — not null
|
|
- `created_at` — timestamp — default `now()`
|
|
4. Save
|
|
|
|
### Via SQL Editor
|
|
|
|
```sql
|
|
create table polls (
|
|
id bigserial primary key,
|
|
title text not null,
|
|
created_at timestamp default now()
|
|
);
|
|
```
|
|
|
|
SQL is sneller + reproduceerbaar. Voor productie altijd via SQL (migrations).
|
|
|
|
---
|
|
|
|
## 6. Foreign keys + relaties
|
|
|
|
### Voorbeeld — polls + options
|
|
|
|
Een poll heeft meerdere opties. Twee tabellen, relatie via `poll_id`:
|
|
|
|
```sql
|
|
create table options (
|
|
id bigserial primary key,
|
|
poll_id bigint not null references polls(id) on delete cascade,
|
|
text text not null,
|
|
votes integer default 0
|
|
);
|
|
```
|
|
|
|
`references polls(id)` = foreign key naar `polls.id`.
|
|
`on delete cascade` = poll verwijderd → opties ook weg.
|
|
|
|
### One-to-many
|
|
|
|
`polls` → `options` (één poll, meerdere opties)
|
|
|
|
### Querying
|
|
|
|
```sql
|
|
-- Alle polls met opties
|
|
select p.title, o.text, o.votes
|
|
from polls p
|
|
join options o on o.poll_id = p.id;
|
|
```
|
|
|
|
In Supabase JS:
|
|
```typescript
|
|
await supabase.from("polls").select("*, options(*)");
|
|
```
|
|
|
|
Automatisch een join.
|
|
|
|
---
|
|
|
|
## 7. RLS — Row Level Security (intro)
|
|
|
|
**RLS** = Postgres-feature waarmee je per-row regels stelt over wie wat mag.
|
|
|
|
### Zonder RLS
|
|
|
|
Iedereen met je anon key kan ALLE data lezen + schrijven. Voor demo OK, voor productie disaster.
|
|
|
|
### Met RLS
|
|
|
|
Per tabel: enable RLS + maak policies.
|
|
|
|
```sql
|
|
alter table polls enable row level security;
|
|
|
|
create policy "polls zijn publiek leesbaar"
|
|
on polls for select using (true);
|
|
|
|
create policy "alleen ingelogde users mogen polls maken"
|
|
on polls for insert with check (auth.uid() is not null);
|
|
```
|
|
|
|
### Voor vandaag
|
|
|
|
In deze les: tabellen met RLS uit OF met open policies (`using (true)`). Dieper in Les 9-10 (auth + per-user RLS).
|
|
|
|
```sql
|
|
-- Voor demo — alle access open
|
|
alter table polls enable row level security;
|
|
create policy "demo open" on polls for all to anon using (true) with check (true);
|
|
```
|
|
|
|
---
|
|
|
|
## 8. SQL Editor — eerste queries
|
|
|
|
### Insert
|
|
|
|
```sql
|
|
insert into polls (title) values ('Beste programmeertaal?')
|
|
returning id;
|
|
```
|
|
|
|
`returning id` = krijg de nieuwe id terug.
|
|
|
|
### Select
|
|
|
|
```sql
|
|
-- Alle polls
|
|
select * from polls;
|
|
|
|
-- Specifieke poll
|
|
select * from polls where id = 1;
|
|
|
|
-- Sorted op nieuwste
|
|
select * from polls order by created_at desc limit 10;
|
|
```
|
|
|
|
### Update
|
|
|
|
```sql
|
|
update polls set title = 'Nieuwere titel' where id = 1;
|
|
```
|
|
|
|
### Delete
|
|
|
|
```sql
|
|
delete from polls where id = 1;
|
|
```
|
|
|
|
### Aggregates
|
|
|
|
```sql
|
|
select count(*) from polls;
|
|
select count(*) as total, date_trunc('day', created_at) as day
|
|
from polls group by day;
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Supabase client in Next.js
|
|
|
|
### Install
|
|
|
|
```bash
|
|
pnpm add @supabase/supabase-js
|
|
```
|
|
|
|
### `.env.local`
|
|
|
|
```
|
|
NEXT_PUBLIC_SUPABASE_URL=https://abc123.supabase.co
|
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
|
|
```
|
|
|
|
`NEXT_PUBLIC_` prefix want anon key mag in client (RLS beschermt data).
|
|
|
|
### Client setup
|
|
|
|
```typescript
|
|
// lib/supabase.ts
|
|
import { createClient } from "@supabase/supabase-js";
|
|
|
|
export const supabase = createClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
);
|
|
```
|
|
|
|
In Les 9-10 vervangen we dit door SSR-safe variant (`@supabase/ssr`). Voor nu volstaat dit.
|
|
|
|
---
|
|
|
|
## 10. Eerste SELECT vanuit Server Component
|
|
|
|
```tsx
|
|
// app/page.tsx
|
|
import { supabase } from "@/lib/supabase";
|
|
|
|
export default async function Home() {
|
|
const { data: polls, error } = await supabase
|
|
.from("polls")
|
|
.select("*")
|
|
.order("created_at", { ascending: false });
|
|
|
|
if (error) return <p>Error: {error.message}</p>;
|
|
|
|
return (
|
|
<ul>
|
|
{polls?.map((poll) => (
|
|
<li key={poll.id}>
|
|
<a href={`/poll/${poll.id}`}>{poll.title}</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Wat hier gebeurt
|
|
|
|
- Server Component (geen `"use client"`)
|
|
- `await supabase.from(...).select()` rechtstreeks in component
|
|
- Server fetcht data, returnt HTML naar client
|
|
- Geen extra API route, geen useEffect
|
|
|
|
In Les 8: we voegen INSERT toe (create polls) + de /create page.
|
|
|
|
---
|
|
|
|
## Bronnen
|
|
|
|
- **Supabase docs:** https://supabase.com/docs
|
|
- **Supabase voor Next.js:** https://supabase.com/docs/guides/getting-started/quickstarts/nextjs
|
|
- **Postgres tutorial:** https://www.postgresqltutorial.com/
|
|
- **RLS guide:** https://supabase.com/docs/guides/database/postgres/row-level-security
|
|
- **JS client docs:** https://supabase.com/docs/reference/javascript/select
|
|
- **SQL crash course:** https://sqlbolt.com
|