3. Route handlers
3. Route handlers
Tijdens deze oefeningenreeks oefen je op
- Het maken en testen van enkele API-routes
- Het instellen van CORS
Voorbereiding
Download de startbestanden.
Startbestanden
Voer de compose uit op je Docker container, je pnpm install, Prisma migration en seed script, Prisma generate, ... (zie README.md).
Deze oefening bouwt verder op de oefening van de vorige les met een kleine uitbreiding. De helper functies voor ok, created, badRequest, unauthorized, forbidden en withProxy zijn toegevoegd. De startbestanden bevatten daarbovenop een extra Vite applicatie waarmee je de CORS-instellingen kunt testen.
Routes voor CRUD acties op series
Maak voor volgende CRUD-acties routes aan en test die uit via Postman (je gaat nog enkele DAL-functies moeten toevoegen).
- Alle series ophalen
- Voorzie ook de mogelijkheid om optioneel te filteren op het aantal boeken in de serie.
- Als de parameter aanwezig is, maar niet geconverteerd kan worden naar een getal, geef je een bad request terug.
- Je kan via Prisma voorlopig nog geen group by query uitvoeren die relaties teruggeeft. Schrijf een typed SQL query die gebruikt kan worden om alle series met een opgegeven aantal boeken op te halen.
- Voorzie ook de mogelijkheid om optioneel te filteren op het aantal boeken in de serie.
- 1 serie ophalen, op basis van de id (gebruik een dynamische route)
- 1 serie toevoegen
- Als de JSON-body van het request afwezig is of geen geldige JSON-code bevat, geef je een bad request terug.
- 1 serie updaten
- Als de JSON-body van het request afwezig is of geen geldige JSON-code bevat, geef je een bad request terug.
- 1 serie deleten (test door een serie te verwijderen die je hebt aangemaakt, om FK constraints te vermijden)
Gebruikt hiervoor rechtstreekst de DAL-functies en spreek de server functions/actions niet aan. Als je de server functions/actions aanspreekt, maak je een extra netwerk request (naar je eigen server).
Logging
Voeg aan elke route handler, zorgt dat elke actie gelogd wordt via een trace log, aan de start en het einde van de route handler moet een info uitgeprint worden. In het geval dat een bad request teruggegeven wordt of een internal server error gegenereerd wordt, log je een gepaste error uit.
Hieronder zie je een voorbeeld voor de POST /series route.
[12:38:29.680] INFO (69266): Route handler called
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"
[12:38:29.680] TRACE (69266): Parsing request body
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"
[12:38:29.681] TRACE (69266): Request body successfully parsed
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"
[12:38:29.681] TRACE (69266): Creating a new Series
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"
[12:38:29.722] TRACE (69266): Series created successfully
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"
POST /api/series 201 in 302ms (compile: 218ms, proxy.ts: 28ms, render: 55ms)
[12:38:29.722] INFO (69266): Route handler exited successfully
method: "POST"
pathname: "/api/series"
requestId: "04577070-2b00-4307-b0fd-b9ff3785bd02"[12:42:06.628] INFO (69266): Route handler called
method: "POST"
pathname: "/api/series"
requestId: "997c14cb-cbe3-4723-a74f-99cfb864e5bc"
[12:42:06.629] TRACE (69266): Parsing request body
method: "POST"
pathname: "/api/series"
requestId: "997c14cb-cbe3-4723-a74f-99cfb864e5bc"
[12:42:06.629] ERROR (69266): Bad request: Unexpected end of JSON input
method: "POST"
pathname: "/api/series"
requestId: "997c14cb-cbe3-4723-a74f-99cfb864e5bc"[12:43:01.507] INFO (69266): Route handler called
method: "POST"
pathname: "/api/series"
requestId: "bc7925e0-b9a9-414b-bd13-565375109087"
[12:43:01.507] TRACE (69266): Parsing request body
method: "POST"
pathname: "/api/series"
requestId: "bc7925e0-b9a9-414b-bd13-565375109087"
[12:43:01.507] ERROR (69266): Bad request: Bad control character in string literal in JSON at position 69 (line 4 column 26)
method: "POST"
pathname: "/api/series"
requestId: "bc7925e0-b9a9-414b-bd13-565375109087"[12:46:33.946] INFO (69266): Route handler called
method: "POST"
pathname: "/api/series"
requestId: "befc6661-b44c-493c-87b1-0b0a4cef4bf2"
POST /api/series 500 in 11ms (compile: 1810µs, proxy.ts: 4ms, render: 5ms)
[12:46:33.946] TRACE (69266): Parsing request body
method: "POST"
pathname: "/api/series"
requestId: "befc6661-b44c-493c-87b1-0b0a4cef4bf2"
[12:46:33.947] TRACE (69266): Request body successfully parsed
method: "POST"
pathname: "/api/series"
requestId: "befc6661-b44c-493c-87b1-0b0a4cef4bf2"
[12:46:33.947] TRACE (69266): Creating a new Series
method: "POST"
pathname: "/api/series"
requestId: "befc6661-b44c-493c-87b1-0b0a4cef4bf2"
[08:58:49.969] INFO (55301): PrismaClientValidationError:
Invalid `__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$dal$2f$prismaClient$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["prismaClient"].series.create()` invocation in
/Users/sebastiaan/Projects/course-material-javascript/exercise-solutions/backend/backend_lecture3_exercise_complete/backend/.next/dev/server/chunks/[root-of-the-server]__3f4d2ab2._.js:497:160
494 });
495 }
496 async function createSeries(series) {
→ 497 return __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$dal$2f$prismaClient$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["prismaClient"].series.create({
data: {
titl: "Test",
genre: "Test",
wikipediaUrl: "sfsf",
+ title: String
}
})
Argument `title` is missing.
method: "POST"
pathname: "/api/series"
requestId: "bff913bd-31f4-4171-9dd8-25406263580d"Proxy
Pas de proxy code aan zodat de logger proxy verplaatst wordt naar een aparte functie. Maak hiervoor gebruik van de withProxy utility uit de startbestanden.
Authenticatie
Alhoewel we nog geen echte authenticatie kunnen implementeren, kunnen we wel een eenvoudige API key gebruiken om te bepalen wie de API mag aanspreken.
In de .env file is een API_KEY voorzien. Voeg een proxy functie toe die een unauthorized terug stuurt als de API key niet meegegeven wordt via de X-Api-Key header. Natuurlijk moet de API key, die in de header meegegeven wordt, ook overeenkomen met de waarde in de .env file (in de startbestanden is dit 'api-key').
Tip
Als je het request wilt afsluiten in de proxy functie, gebruik je NextResponse.next() niet, maar geef je een nieuw response object terug. Je moet nog wel steeds NextResponse.next() teruggeven in het geval de API key geldig is.

CORS headers
Gebruik de CORS app om dit stuk te testen. Je moet zelf niets aanpassen aan deze code, je moet enkel op de knoppen drukken om je CORS settings te testen.
Breid de proxy functie uit zodat aan alle vereisten voldaan is:
- Start de CORS app op en check op welk adres deze draait (kopieer zonder de slash op het einde)
- Zorg ervoor dat gebruikers van de CORS app de data kunnen raadplegen, wijzigen en deleten, maar ENKEL als de request van dat specifieke adres vertrekt. Je kan testen door in de CORS-header (in je oefening zelf) het adres eens juist en fout te zetten, en dan de request uit te voeren in de CORS app.
- Zorg dat de X-Api-Key header ook vanuit de browser meegestuurd kan worden.
Tip
De x-Api-Key header wordt niet meegestuurd met OPTIONS requests, zorg dat je authentication proxy hier rekening mee houd.