fix: implement lessons feedback
This commit is contained in:
@@ -1,349 +1,412 @@
|
||||
# Les 7: Backend Basics met Supabase
|
||||
|
||||
> 📋 **Lesmateriaal nog niet uitgewerkt**
|
||||
>
|
||||
> De volgende bestanden worden gegenereerd wanneer deze les wordt uitgewerkt:
|
||||
> - Les07-Slide-Overzicht.md
|
||||
> - Les07-Lesplan.md
|
||||
> - Les07-Bijlage-A-Lesopdracht.md
|
||||
> - Les07-Bijlage-B-Huiswerkopdracht.md
|
||||
# Les 7: Next.js Fundamentals 2 - API Routes & Data Fetching
|
||||
|
||||
---
|
||||
|
||||
## Hoofdstuk
|
||||
**Hoofdstuk 2: Intermediate** (Les 4-9)
|
||||
**Deel 2: Technical Foundations** (Les 5-9)
|
||||
|
||||
## Beschrijving
|
||||
Zet je eerste "echte" Next.js project op en koppel het aan Supabase voor database en authenticatie. Je leert de complete flow van lokaal ontwikkelen tot productie deployment.
|
||||
Leer data fetching in Next.js: Server Components, Client Components, API routes en React Query. Bouw een volledig werkende app met data.
|
||||
|
||||
---
|
||||
|
||||
## Te Behandelen
|
||||
|
||||
### Stap 1: Next.js Project Aanmaken
|
||||
### Server Components vs Client Components
|
||||
|
||||
Dit is de eerste keer dat je een volledig Next.js project opzet.
|
||||
**Server Components (default in Next.js):**
|
||||
- Renderen op de server
|
||||
- Geen JavaScript naar de browser
|
||||
- Kunnen direct data fetchen (async/await)
|
||||
- Kunnen NIET: useState, useEffect, event handlers
|
||||
|
||||
```bash
|
||||
# Maak nieuw project
|
||||
npx create-next-app@latest todo-app
|
||||
**Client Components:**
|
||||
- Renderen in de browser
|
||||
- JavaScript naar de browser
|
||||
- Kunnen interactief zijn
|
||||
- Markeer met `'use client'` bovenaan
|
||||
|
||||
# Beantwoord de vragen:
|
||||
# ✔ Would you like to use TypeScript? → Yes
|
||||
# ✔ Would you like to use ESLint? → Yes
|
||||
# ✔ Would you like to use Tailwind CSS? → Yes
|
||||
# ✔ Would you like to use `src/` directory? → Yes
|
||||
# ✔ Would you like to use App Router? → Yes
|
||||
# ✔ Would you like to customize the default import alias? → No
|
||||
---
|
||||
|
||||
# Ga naar project folder
|
||||
cd todo-app
|
||||
|
||||
# Open in je editor
|
||||
code . # of: cursor .
|
||||
```
|
||||
|
||||
### Stap 2: Project Structuur Begrijpen
|
||||
### Wanneer Wat?
|
||||
|
||||
```
|
||||
todo-app/
|
||||
├── src/
|
||||
│ └── app/
|
||||
│ ├── layout.tsx # Root layout (header, footer)
|
||||
│ ├── page.tsx # Homepage (/)
|
||||
│ └── globals.css # Global styles + Tailwind
|
||||
├── public/ # Static files (images, etc.)
|
||||
├── .env.local # Environment variables (maak zelf aan)
|
||||
├── next.config.js # Next.js configuratie
|
||||
├── tailwind.config.ts # Tailwind configuratie
|
||||
├── package.json # Dependencies
|
||||
└── tsconfig.json # TypeScript configuratie
|
||||
Server Component → Data tonen, geen interactie
|
||||
Client Component → Interactie nodig (forms, buttons, state)
|
||||
```
|
||||
|
||||
### Stap 3: Lokaal Draaien
|
||||
|
||||
```bash
|
||||
# Start development server
|
||||
npm run dev
|
||||
|
||||
# Open browser: http://localhost:3000
|
||||
```
|
||||
|
||||
### Stap 4: Supabase Project Aanmaken
|
||||
|
||||
**Op supabase.com:**
|
||||
|
||||
1. Ga naar [supabase.com](https://supabase.com) en maak account (gratis)
|
||||
2. Klik "New Project"
|
||||
3. Kies een naam (bijv. `todo-app`)
|
||||
4. Kies een database wachtwoord (bewaar deze!)
|
||||
5. Kies region: `West EU (Frankfurt)` (dichtst bij NL)
|
||||
6. Wacht ~2 minuten tot project klaar is
|
||||
|
||||
**Credentials ophalen:**
|
||||
|
||||
1. Ga naar Settings → API
|
||||
2. Kopieer:
|
||||
- `Project URL` → dit wordt `NEXT_PUBLIC_SUPABASE_URL`
|
||||
- `anon public` key → dit wordt `NEXT_PUBLIC_SUPABASE_ANON_KEY`
|
||||
|
||||
### Stap 5: Environment Variables (Lokaal)
|
||||
|
||||
Maak `.env.local` in je project root:
|
||||
|
||||
```bash
|
||||
# .env.local (NOOIT committen naar Git!)
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx
|
||||
```
|
||||
|
||||
Maak ook `.env.example` (deze WEL committen):
|
||||
|
||||
```bash
|
||||
# .env.example (template voor anderen)
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
```
|
||||
|
||||
**Check .gitignore:**
|
||||
```
|
||||
# .gitignore moet bevatten:
|
||||
.env*.local
|
||||
```
|
||||
|
||||
### Stap 6: Supabase SDK Installeren
|
||||
|
||||
```bash
|
||||
npm install @supabase/supabase-js
|
||||
```
|
||||
|
||||
### Stap 7: Supabase Client Maken
|
||||
|
||||
Maak `src/lib/supabase.ts`:
|
||||
|
||||
```typescript
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
|
||||
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
|
||||
```
|
||||
|
||||
### Stap 8: Database Tabel Maken (via UI)
|
||||
|
||||
**In Supabase Dashboard:**
|
||||
|
||||
1. Ga naar Table Editor
|
||||
2. Klik "New Table"
|
||||
3. Naam: `todos`
|
||||
4. Kolommen:
|
||||
| Name | Type | Default | Primary |
|
||||
|------|------|---------|---------|
|
||||
| id | int8 | - | ✓ (auto) |
|
||||
| title | text | - | |
|
||||
| completed | bool | false | |
|
||||
| created_at | timestamptz | now() | |
|
||||
| user_id | uuid | auth.uid() | |
|
||||
|
||||
5. Klik "Save"
|
||||
|
||||
### Stap 9: CRUD Operaties
|
||||
|
||||
```typescript
|
||||
// CREATE - nieuwe todo toevoegen
|
||||
const { data, error } = await supabase
|
||||
.from('todos')
|
||||
.insert({ title: 'Nieuwe taak' })
|
||||
|
||||
// READ - todos ophalen
|
||||
const { data, error } = await supabase
|
||||
.from('todos')
|
||||
.select('*')
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
// UPDATE - todo afvinken
|
||||
const { data, error } = await supabase
|
||||
.from('todos')
|
||||
.update({ completed: true })
|
||||
.eq('id', todoId)
|
||||
|
||||
// DELETE - todo verwijderen
|
||||
const { error } = await supabase
|
||||
.from('todos')
|
||||
.delete()
|
||||
.eq('id', todoId)
|
||||
```
|
||||
|
||||
### Stap 10: Authenticatie Setup
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-ui-react @supabase/auth-ui-shared
|
||||
```
|
||||
|
||||
**Login component:**
|
||||
**Voorbeeld:**
|
||||
```tsx
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
import { ThemeSupa } from '@supabase/auth-ui-shared'
|
||||
import { supabase } from '@/lib/supabase'
|
||||
// Server Component - data ophalen
|
||||
async function ProductList() {
|
||||
const products = await fetchProducts() // Direct fetchen!
|
||||
return <ul>{products.map(p => <li>{p.name}</li>)}</ul>
|
||||
}
|
||||
|
||||
// Client Component - interactie
|
||||
'use client'
|
||||
function AddToCartButton({ productId }) {
|
||||
const [added, setAdded] = useState(false)
|
||||
return <button onClick={() => setAdded(true)}>Add</button>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Data Fetching in Server Components
|
||||
|
||||
Simpelweg `async/await` gebruiken:
|
||||
|
||||
```tsx
|
||||
// app/products/page.tsx
|
||||
interface Product {
|
||||
id: number
|
||||
name: string
|
||||
price: number
|
||||
}
|
||||
|
||||
async function getProducts(): Promise<Product[]> {
|
||||
const res = await fetch('https://api.example.com/products')
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export default async function ProductsPage() {
|
||||
const products = await getProducts()
|
||||
|
||||
export function LoginForm() {
|
||||
return (
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
providers={[]}
|
||||
magicLink={true}
|
||||
/>
|
||||
<div>
|
||||
<h1>Producten</h1>
|
||||
<ul>
|
||||
{products.map(product => (
|
||||
<li key={product.id}>
|
||||
{product.name} - €{product.price}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Session checken:**
|
||||
```typescript
|
||||
const { data: { user } } = await supabase.auth.getUser()
|
||||
---
|
||||
|
||||
if (user) {
|
||||
// User is ingelogd
|
||||
} else {
|
||||
// Redirect naar login
|
||||
### API Routes (Route Handlers)
|
||||
|
||||
Bouw je eigen API in Next.js:
|
||||
|
||||
**Folder structuur:**
|
||||
```
|
||||
app/
|
||||
└── api/
|
||||
└── products/
|
||||
└── route.ts → /api/products
|
||||
```
|
||||
|
||||
**GET request (`app/api/products/route.ts`):**
|
||||
```typescript
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
const products = [
|
||||
{ id: 1, name: 'Laptop', price: 999 },
|
||||
{ id: 2, name: 'Phone', price: 699 },
|
||||
]
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json(products)
|
||||
}
|
||||
```
|
||||
|
||||
**POST request:**
|
||||
```typescript
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json()
|
||||
|
||||
const newProduct = {
|
||||
id: Date.now(),
|
||||
name: body.name,
|
||||
price: body.price,
|
||||
}
|
||||
|
||||
products.push(newProduct)
|
||||
|
||||
return NextResponse.json(newProduct, { status: 201 })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment naar Vercel (Productie)
|
||||
### 'use client' Directive
|
||||
|
||||
### Stap 1: GitHub Repository
|
||||
Wanneer je interactie nodig hebt:
|
||||
|
||||
```bash
|
||||
# In je project folder
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Initial commit"
|
||||
```tsx
|
||||
'use client' // MOET bovenaan!
|
||||
|
||||
# Maak repo op GitHub, dan:
|
||||
git remote add origin https://github.com/jouw-username/todo-app.git
|
||||
git push -u origin main
|
||||
import { useState } from 'react'
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<button onClick={() => setCount(count + 1)}>
|
||||
Count: {count}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Stap 2: Vercel Deployment
|
||||
|
||||
1. Ga naar [vercel.com](https://vercel.com)
|
||||
2. "Add New Project"
|
||||
3. Import je GitHub repository
|
||||
4. **BELANGRIJK:** Voeg Environment Variables toe:
|
||||
- `NEXT_PUBLIC_SUPABASE_URL` → je Supabase URL
|
||||
- `NEXT_PUBLIC_SUPABASE_ANON_KEY` → je anon key
|
||||
5. Klik "Deploy"
|
||||
|
||||
### Stap 3: Supabase URL Toestaan
|
||||
|
||||
In Supabase Dashboard:
|
||||
1. Ga naar Authentication → URL Configuration
|
||||
2. Voeg je Vercel URL toe aan "Redirect URLs":
|
||||
- `https://jouw-app.vercel.app/**`
|
||||
|
||||
---
|
||||
|
||||
## Overzicht: Lokaal vs Productie
|
||||
### React Query (TanStack Query)
|
||||
|
||||
| Aspect | Lokaal | Productie |
|
||||
|--------|--------|-----------|
|
||||
| URL | `localhost:3000` | `jouw-app.vercel.app` |
|
||||
| Env vars | `.env.local` | Vercel Dashboard |
|
||||
| Database | Supabase (zelfde) | Supabase (zelfde) |
|
||||
| Command | `npm run dev` | Automatisch via Vercel |
|
||||
**Waarom React Query?**
|
||||
- Automatische caching
|
||||
- Loading en error states
|
||||
- Refetching (focus, interval)
|
||||
- Optimistic updates
|
||||
|
||||
**Let op:** Je gebruikt dezelfde Supabase database voor lokaal en productie. Voor een echt project zou je aparte databases hebben, maar voor deze cursus is dat niet nodig.
|
||||
**Installatie:**
|
||||
```bash
|
||||
npm install @tanstack/react-query
|
||||
```
|
||||
|
||||
**Setup Provider (`app/providers.tsx`):**
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
const [queryClient] = useState(() => new QueryClient())
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**In Layout:**
|
||||
```tsx
|
||||
import { Providers } from './providers'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### useQuery - Data Ophalen
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
interface Product {
|
||||
id: number
|
||||
name: string
|
||||
price: number
|
||||
}
|
||||
|
||||
async function fetchProducts(): Promise<Product[]> {
|
||||
const res = await fetch('/api/products')
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export function ProductList() {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['products'],
|
||||
queryFn: fetchProducts,
|
||||
})
|
||||
|
||||
if (isLoading) return <div>Laden...</div>
|
||||
if (error) return <div>Error: {error.message}</div>
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{data?.map(product => (
|
||||
<li key={product.id}>{product.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### useMutation - Data Wijzigen
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
interface NewProduct {
|
||||
name: string
|
||||
price: number
|
||||
}
|
||||
|
||||
async function createProduct(product: NewProduct) {
|
||||
const res = await fetch('/api/products', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(product),
|
||||
})
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export function AddProductForm() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: createProduct,
|
||||
onSuccess: () => {
|
||||
// Invalidate and refetch
|
||||
queryClient.invalidateQueries({ queryKey: ['products'] })
|
||||
},
|
||||
})
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
mutation.mutate({ name: 'New Product', price: 99 })
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<button type="submit" disabled={mutation.isPending}>
|
||||
{mutation.isPending ? 'Toevoegen...' : 'Voeg toe'}
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Combineren: Server + Client
|
||||
|
||||
```tsx
|
||||
// app/products/page.tsx (Server Component)
|
||||
import { ProductList } from './product-list'
|
||||
import { AddProductForm } from './add-product-form'
|
||||
|
||||
export default function ProductsPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Producten</h1>
|
||||
<AddProductForm /> {/* Client Component */}
|
||||
<ProductList /> {/* Client Component */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
- Next.js
|
||||
- Supabase (gratis tier)
|
||||
- Vercel (gratis tier)
|
||||
- Cursor/OpenCode
|
||||
- Next.js 14
|
||||
- React Query (TanStack Query)
|
||||
- TypeScript
|
||||
- OpenCode/WebStorm
|
||||
|
||||
---
|
||||
|
||||
## Lesopdracht (2 uur)
|
||||
|
||||
### Bouw een Todo App met Supabase
|
||||
### Bouw CRUD App met API Routes
|
||||
|
||||
**Deel 1: Project Setup (30 min)**
|
||||
- Run `npx create-next-app@latest todo-app` met juiste opties
|
||||
- Maak Supabase account en project aan
|
||||
- Configureer `.env.local` met credentials
|
||||
- Installeer `@supabase/supabase-js`
|
||||
- Maak `src/lib/supabase.ts`
|
||||
- Test: `npm run dev` werkt zonder errors
|
||||
**Deel 1: API Routes (40 min)**
|
||||
|
||||
**Deel 2: Database (20 min)**
|
||||
- Maak `todos` tabel via Supabase Table Editor
|
||||
- Voeg 3 test todos toe via de UI
|
||||
- Test: data is zichtbaar in Table Editor
|
||||
1. Maak `app/api/products/route.ts`
|
||||
2. Implementeer GET (alle producten)
|
||||
3. Implementeer POST (product toevoegen)
|
||||
4. Test met browser/Postman: `/api/products`
|
||||
|
||||
**Deel 3: CRUD Interface (50 min)**
|
||||
- Bouw UI om todos te tonen (lijst)
|
||||
- Voeg form toe om nieuwe todo te maken
|
||||
- Voeg checkbox toe om todo af te vinken
|
||||
- Voeg delete button toe
|
||||
- Test: alle CRUD operaties werken
|
||||
**Deel 2: React Query Setup (20 min)**
|
||||
|
||||
**Deel 4: Authenticatie (20 min)**
|
||||
- Installeer auth packages
|
||||
- Maak login pagina met Auth UI
|
||||
- Toon alleen todos voor ingelogde user
|
||||
- Test: login met magic link werkt
|
||||
1. Installeer `@tanstack/react-query`
|
||||
2. Maak `app/providers.tsx`
|
||||
3. Wrap app in `QueryClientProvider`
|
||||
|
||||
**Deel 3: Data Tonen met useQuery (30 min)**
|
||||
|
||||
1. Maak `ProductList` Client Component
|
||||
2. Gebruik `useQuery` om data te fetchen
|
||||
3. Toon loading state
|
||||
4. Toon error state
|
||||
5. Render product lijst
|
||||
|
||||
**Deel 4: Data Toevoegen met useMutation (30 min)**
|
||||
|
||||
1. Maak `AddProductForm` Client Component
|
||||
2. Gebruik `useMutation` voor POST
|
||||
3. Invalidate query na success
|
||||
4. Toon "Adding..." state
|
||||
|
||||
### Deliverable
|
||||
- Werkende Todo app lokaal
|
||||
- GitHub repository met code
|
||||
- Screenshot van werkende app
|
||||
- Werkende API routes (GET, POST)
|
||||
- ProductList met useQuery
|
||||
- AddProductForm met useMutation
|
||||
- Loading en error states
|
||||
|
||||
---
|
||||
|
||||
## Huiswerk (2 uur)
|
||||
|
||||
### Deploy naar Productie + Uitbreiden
|
||||
### Volledige CRUD Interface
|
||||
|
||||
**Deel 1: Deployment (30 min)**
|
||||
- Push code naar GitHub
|
||||
- Deploy naar Vercel
|
||||
- Configureer environment variables in Vercel
|
||||
- Voeg Vercel URL toe aan Supabase Redirect URLs
|
||||
- Test: app werkt op productie URL
|
||||
**Deel 1: PUT en DELETE Routes (45 min)**
|
||||
|
||||
**Deel 2: Features Uitbreiden (1 uur)**
|
||||
- Filter buttons: Alle / Actief / Voltooid
|
||||
- Sorteer op datum (nieuwste eerst)
|
||||
- Toon alleen todos van ingelogde user (filter op `user_id`)
|
||||
- Loading state tijdens data ophalen
|
||||
- Error state bij problemen
|
||||
- Empty state: "Geen todos gevonden"
|
||||
1. Maak `app/api/products/[id]/route.ts`
|
||||
2. Implementeer PUT (update product)
|
||||
3. Implementeer DELETE (verwijder product)
|
||||
4. Test beide endpoints
|
||||
|
||||
**Deel 3: Polish (30 min)**
|
||||
- Styling verbeteren met Tailwind
|
||||
- Responsive design (mobile friendly)
|
||||
- Kleine animaties (fade in/out)
|
||||
**Deel 2: Update Functionaliteit (45 min)**
|
||||
|
||||
1. Maak edit form in ProductList
|
||||
2. Gebruik useMutation voor PUT
|
||||
3. Inline editing OF modal
|
||||
4. Invalidate query na success
|
||||
|
||||
**Deel 3: Delete Functionaliteit (30 min)**
|
||||
|
||||
1. Voeg delete button toe per product
|
||||
2. Gebruik useMutation voor DELETE
|
||||
3. Voeg confirmation dialog toe
|
||||
4. Invalidate query na success
|
||||
|
||||
**Bonus:** Optimistic Updates
|
||||
- Product direct uit UI verwijderen
|
||||
- Rollback als server faalt
|
||||
|
||||
### Deliverable
|
||||
- Deployed app op Vercel (werkende URL)
|
||||
- Alle features werken in productie
|
||||
- Screenshot van productie app
|
||||
- Complete CRUD API (GET, POST, PUT, DELETE)
|
||||
- UI voor alle operaties
|
||||
- Error handling
|
||||
- Optimistic updates (bonus)
|
||||
|
||||
---
|
||||
|
||||
## Leerdoelen
|
||||
Na deze les kan de student:
|
||||
- Een Next.js project opzetten met `npx create-next-app`
|
||||
- De project structuur begrijpen en navigeren
|
||||
- Een Supabase project aanmaken en configureren
|
||||
- Environment variables correct beheren (lokaal en productie)
|
||||
- De Supabase client installeren en configureren
|
||||
- Tabellen maken via de Supabase UI
|
||||
- CRUD operaties uitvoeren met de Supabase SDK
|
||||
- Authenticatie implementeren met Auth UI
|
||||
- Deployen naar Vercel met environment variables
|
||||
- Het verschil tussen lokale en productie omgeving begrijpen
|
||||
- Uitleggen wanneer Server vs Client Components
|
||||
- De 'use client' directive correct gebruiken
|
||||
- Data fetchen in Server Components met async/await
|
||||
- API routes maken met Route Handlers
|
||||
- GET en POST requests implementeren
|
||||
- React Query installeren en configureren
|
||||
- useQuery gebruiken voor data fetching
|
||||
- useMutation gebruiken voor data mutations
|
||||
- Loading en error states afhandelen
|
||||
- Query invalidation toepassen
|
||||
|
||||
Reference in New Issue
Block a user