Uitleg — Wat zijn API Routes?

In Next.js bouw je backend endpoints in hetzelfde project als je frontend. Geen aparte Express server nodig. API routes maak je door een route.js bestand aan te maken in app/api/.

app/
└── api/
    └── workouts/
        ├── route.js          →  GET /api/workouts en POST /api/workouts
        └── [id]/
            └── route.js      →  GET /api/workouts/:id en DELETE /api/workouts/:id

💡 route.js vs page.js

page.js = een pagina die HTML teruggeeft (voor gebruikers)
route.js = een API endpoint dat JSON teruggeeft (voor code)

Stap 1 — Installeer Mongoose

Stop de dev server eerst (Ctrl+C), voer dan uit:

npm install mongoose

Start daarna de dev server weer op:

npm run dev

Stap 2 — Maak de databaseverbinding aan

Maak een nieuwe map lib aan naast de app/ map (dus in de root, niet erin):

workout-tracker/
├── app/             ← hier zit je al
├── lib/             ← nieuwe map hier aanmaken
│   └── mongodb.js   ← nieuw bestand aanmaken
├── public/
└── package.json
// lib/mongodb.js
import mongoose from 'mongoose';

const MONGO_URI = process.env.MONGO_URI;

if (!MONGO_URI) {
  throw new Error('Stel MONGO_URI in je .env.local in');
}

let cached = global.mongoose;

if (!cached) {
  cached = global.mongoose = { conn: null, promise: null };
}

async function connectDB() {
  if (cached.conn) {
    return cached.conn;
  }

  cached.promise = mongoose.connect(MONGO_URI);
  cached.conn = await cached.promise;

  return cached.conn;
}

export default connectDB;
Waarom deze aanpak?
  • Next.js herlaadt bestanden vaker dan Express
  • Zonder caching maak je elke keer een nieuwe verbinding
  • Met global.mongoose bewaar je de verbinding

Stap 3 — Maak .env.local aan

Maak een nieuw bestand .env.local aan naast de app/ map (dus in de root, niet erin):

workout-tracker/
├── app/
├── lib/
├── .env.local       ← nieuw bestand hier aanmaken
└── package.json
# .env.local
MONGO_URI=mongodb+srv://gebruiker:wachtwoord@cluster0.xxxxx.mongodb.net/jouwDatabaseNaam?retryWrites=true&w=majority

Let op: .env.local vs .env

Next.js gebruikt .env.local, niet .env. Controleer de bestandsnaam!

Stap 4 — Maak het Workout model aan

Maak een nieuwe map models aan naast de app/ map en maak daarin een nieuw bestand:

workout-tracker/
├── app/
├── lib/
├── models/          ← nieuwe map hier aanmaken
│   └── Workout.js   ← nieuw bestand aanmaken
└── package.json
// models/Workout.js
import mongoose from 'mongoose';

const workoutSchema = new mongoose.Schema(
  {
    title: {
      type: String,
      required: true,
    },
    reps: {
      type: Number,
      required: true,
    },
    load: {
      type: Number,
      required: true,
    },
  },
  { timestamps: true }
);

// Voorkom dat Mongoose het model opnieuw aanmaakt bij hot reload
const Workout = mongoose.models.Workout || mongoose.model('Workout', workoutSchema);

export default Workout;

Belangrijk: de laatste regel

mongoose.models.Workout || mongoose.model(...) is essentieel in Next.js. Zonder dit krijg je een "Cannot overwrite model once compiled" fout bij hot reload.

Stap 5 — Maak de GET & POST route aan

Maak de mappen api en workouts aan binnen de app/ map, en maak daarin een nieuw bestand:

app/
├── api/                  ← nieuwe map aanmaken
│   └── workouts/         ← nieuwe map aanmaken
│       └── route.js      ← nieuw bestand aanmaken
└── workouts/
    └── page.js
// app/api/workouts/route.js
import { NextResponse } from 'next/server';
import { revalidatePath } from 'next/cache';
import connectDB from '../../../lib/mongodb';
import Workout from '../../../models/Workout';

// GET /api/workouts — alle workouts ophalen
export async function GET() {
  await connectDB();

  const workouts = await Workout.find({}).sort({ createdAt: -1 });

  return NextResponse.json(workouts, { status: 200 });
}

// POST /api/workouts — nieuwe workout aanmaken
export async function POST(request) {
  await connectDB();

  const body = await request.json();
  const { title, reps, load } = body;

  // Validatie
  if (!title || !reps || !load) {
    return NextResponse.json(
      { error: 'Vul alle velden in' },
      { status: 400 }
    );
  }

  const workout = await Workout.create({ title, reps, load });

  revalidatePath('/workouts'); // ← cache legen na toevoegen

  return NextResponse.json(workout, { status: 201 });
}
Wat is er anders dan Express?
  • Elke HTTP methode (GET, POST) is een aparte export function
  • Je gebruikt NextResponse.json() in plaats van res.json()
  • De request body lees je met await request.json()

Stap 6 — Maak de dynamische route aan (GET & DELETE)

Maak een nieuwe map [id] aan binnen de bestaande app/api/workouts/ map:

app/
└── api/
    └── workouts/
        ├── route.js          ← al aangemaakt
        └── [id]/             ← nieuwe map aanmaken
            └── route.js      ← nieuw bestand aanmaken
// app/api/workouts/[id]/route.js
import { NextResponse } from 'next/server';
import { revalidatePath } from 'next/cache';
import connectDB from '../../../../lib/mongodb';
import Workout from '../../../../models/Workout';

// GET /api/workouts/:id — één workout ophalen
export async function GET(request, { params }) {
  await connectDB();

  const { id } = await params;
  const workout = await Workout.findById(id);

  if (!workout) {
    return NextResponse.json(
      { error: 'Workout niet gevonden' },
      { status: 404 }
    );
  }

  return NextResponse.json(workout, { status: 200 });
}

// DELETE /api/workouts/:id — workout verwijderen
export async function DELETE(request, { params }) {
  await connectDB();

  const { id } = await params;
  const workout = await Workout.findByIdAndDelete(id);

  if (!workout) {
    return NextResponse.json(
      { error: 'Workout niet gevonden' },
      { status: 404 }
    );
  }

  revalidatePath('/workouts'); // ← cache legen na verwijderen

  return NextResponse.json(
    { message: 'Workout verwijderd' },
    { status: 200 }
  );
}

💡 params in API routes

De params staan in het tweede argument van de functie, niet in de request. Let op de destructuring: { params }.

Stap 7 — Testen

GET testen

Open je browser en ga naar: http://localhost:3000/api/workouts

Je ziet een lege array [] (want nog geen data).

POST testen met Postman

Stuur een POST request naar http://localhost:3000/api/workouts met als body:

{
  "title": "Push Day",
  "reps": 10,
  "load": 50
}

✅ Verwacht resultaat

Je ontvangt het aangemaakte workout object terug met een _id en timestamps. Ververs daarna de browser — je ziet het workout in de GET response!

Mappenstructuur tot nu toe

workout-tracker/
├── app/
│   ├── api/
│   │   └── workouts/
│   │       ├── route.js
│   │       └── [id]/
│   │           └── route.js
│   ├── components/
│   │   └── AddWorkoutForm.js
│   ├── workouts/
│   │   ├── page.js
│   │   └── [id]/
│   │       └── page.js
│   ├── layout.js
│   └── page.js
├── lib/
│   └── mongodb.js
├── models/
│   └── Workout.js
└── .env.local

Checklist

✅ Check of je hebt:

  • Mongoose geïnstalleerd
  • lib/mongodb.js aangemaakt
  • MONGO_URI in .env.local
  • models/Workout.js aangemaakt
  • app/api/workouts/route.js met GET en POST
  • app/api/workouts/[id]/route.js met DELETE
  • GET /api/workouts werkt in browser
  • POST via Postman maakt een workout aan

Volgende Stap

API werkt! Nu gaan we data ophalen op de pagina's.

Data Fetching →

Nu de API werkt, haal je de data op in je pagina's