Lektion 17 — Øvelser

Man lærer et repository bedst ved at flytte sin egen route over på det og se listen huske. Detaljerne, der ikke står på slidesne, står her.

Hvis du ikke nåede forberedelsen, så kør hent.js mod din egen database først. Det tager 15 minutter, og så ved du, at forbindelsen virker, før du rører server.js.

Øvelse Hvornår Tid
1 Listen husker Efter quizzen om repository og GET ~35 min
2 POST og testskemaet Efter quizzen om POST ~30 min
3 Gruppens handling Efter øvelse 2 ~30 min

Alle i gruppen laver øvelserne på egen maskine, mod gruppens database fra lektion 15. Når én opretter en patient, ser alle den — og en CPR, som én har oprettet, kan de andre ikke oprette igen. Brug derfor hver jeres egen syntetiske CPR, når I tester POST. Én i gruppen committer gruppens version. De andre puller og laver deres egen .env.


Øvelse 1 — Listen husker

Mål: GET /api/patients i gruppens server.js henter fra tabellen i Neon. Mockuppets tabel viser de fem testpatienter, også efter en genstart.

Arbejd i gruppens projekt, i mappen med server.js fra lektion 13.

Det skal du lave

  1. Installér pg:
npm install pg
  1. Lav .env ved siden af server.js med gruppens connection-string fra lektion 15, øvelse 3. Ret sslmode=require til sslmode=verify-full.
DATABASE_URL=postgresql://neondb_owner:…@…neon.tech/neondb?sslmode=verify-full&channel_binding=require
  1. Tilføj .env i .gitignore, ved siden af node_modules/. Kør git status. .env må ikke stå på listen.
  2. Lav patients.js ved siden af server.js, som på slidesne: Pool, pool.on("error", …) og hentPatienter(). Eksportér den med module.exports.
  3. I server.js:
    • slet arrayet patients. Har I GET /api/patients/:cpr fra lektion 13, skal den kalde hentPatient(cpr) (ekstra-opgaven nedenfor) eller tages ud, indtil den gør det. Ellers læser den et array, der ikke findes.
    • slet den gamle POST-route med patients.push — I skriver den igen i øvelse 2
    • require("./patients") øverst
    • gør GET /api/patients async, kald hentPatienter(), og svar 500 med { error: "serverfejl" } i catch
  4. Start serveren:
node --env-file=.env server.js
  1. Åbn http://localhost:3000/api/patients i browseren. Du skal se de fem testpatienter fra Neon.
  2. Åbn mockuppet lokalt. Tabellen skal vise de samme fem uden at fetch er ændret.
  3. Stop serveren med Ctrl+C, og start den igen. Hent igen. Patienterne er der stadig.
  4. Ejeren af databasen indsætter en patient i Neons SQL-editor. Genindlæs mockuppet hos en anden i gruppen. Den nye patient står i tabellen.

Tjekliste

  • git status viser ikke .env
  • server.js har ikke længere et patients-array
  • server.js indeholder ingen SQL. Al SQL står i patients.js
  • Tabellen i mockuppet viser data fra Neon
  • Genstart af serveren ændrer ikke listen

Øvelsen er i hus, når en anden i gruppen kan pege på, hvor SQL'en står, og en række fra SQL-editoren dukker op i jeres tabel.

Hvis du sidder fast

  • 500 og ECONNREFUSED i terminalen: pg fik ingen connection-string og prøver din egen maskine. Du har glemt --env-file=.env, linjen i .env hedder ikke præcis DATABASE_URL=, eller filen er lavet i PowerShell med > (forkert tegnsæt). Lav .env i VS Code.
  • node: .env: not found: filen findes ikke i den mappe, du står i — eller den hedder .env.txt.
  • password authentication failed: strengen i .env er forkert. Bed ejeren kopiere den igen med Copy snippet.
  • relation "patients" does not exist: du har brugt strengen til dit eget projekt lektion15 eller et projekt uden tabellen. Brug gruppens.
  • Cannot find module 'pg': kør npm install pg i den mappe, hvor server.js ligger.
  • Lang SECURITY WARNING om sslmode: ret sslmode=require til sslmode=verify-full i .env.
  • Serveren svarer langsomt første gang efter en pause: databasen var gået i dvale. Den vågner selv.
  • Serveren dør med Unhandled 'error' event efter en pause: pool.on("error", …) mangler i patients.js.
  • .env står i git status: den er ikke i .gitignore, eller den er allerede committet. Spørg underviseren — passwordet skal så nulstilles.

Hvis du har ekstra tid

  • Tilføj hentPatient(cpr) i repositoryet med WHERE cpr = $1. Hvad returnerer den, når CPR'en ikke findes?
  • Tegn sekvensdiagrammet fra slidesne for jeres egen GET, med jeres egne filnavne.

Øvelse 2 — POST og testskemaet

Mål: POST /api/patients gemmer i tabellen. I har kørt testskemaet fra api.http og skrevet resultatet i projektloggen.

Det skal du lave

  1. Tilføj opretPatient(cpr, navn) i patients.js med INSERT … RETURNING og $1, $2. Eksportér den.
  2. Tilføj POST /api/patients i server.js, over 404-handleren: req.body ?? {}, 400 før databasen, 201 med den nye patient, 500 i catch.
  3. Genstart serveren.
  4. Åbn api.http fra lektion 13, og ret body i POST-requestet til en syntetisk patient, du kan genkende. Brug din egen CPR — skift de to sidste cifre, så den ikke er den samme som gruppens andres:
### Opret patient
POST http://localhost:3000/api/patients
Content-Type: application/json

{"cpr":"0201029994","navn":"Ny Test Person"}
  1. Kør testskemaet. Skriv resultatet i kolonnen Fik.
# Request Forventet Fik
1 GET /api/patients 200 og listen
2 POST /api/patients med body 201 og patienten
3 POST /api/patients uden body 400
4 GET /api/patients efter genstart 200, patienten fra 2 er der
5 GET /api/patients med forkert password i .env 500 og serverfejl
6 GET /findesikke 404

Requests i api.http — de tre første har I fra lektion 13, tilføj den sidste:

### Hele listen
GET http://localhost:3000/api/patients

### Opret patient
POST http://localhost:3000/api/patients
Content-Type: application/json

{"cpr":"0201029994","navn":"Ny Test Person"}

### Uden body — skal give 400
POST http://localhost:3000/api/patients

### Ukendt sti — skal give 404
GET http://localhost:3000/findesikke

Statuskoden står øverst i svaret ved siden af filen.

Til række 5: skift ét tegn i passwordet i .env, genstart, kør GET, og læs fejlen i terminalen — ikke i svaret. Sæt passwordet tilbage, og genstart igen.

  1. Kopiér skemaet med jeres resultater ind i projektloggen.

Tjekliste

  • opretPatient bruger $1 og $2 — ingen + i SQL'en
  • 400 kommer, før databasen bliver kaldt
  • Alle seks rækker i skemaet giver det forventede
  • Svaret på række 5 viser ikke en stacktrace, kun serverfejl
  • Passwordet i .env er sat tilbage

Øvelsen er i hus, når skemaet står i projektloggen med seks rigtige resultater, og I kan forklare forskellen på 400 og 500.

Hvis du sidder fast

  • Ingen Send Request over requestet: REST Client er ikke installeret i VS Code, eller filen ender ikke på .http.
  • 400 med Expected property name: JSON'en i body er ugyldig. Tjek anførselstegn og kommaer.
  • POST giver 404: routen står under 404-handleren. Flyt den op.
  • 500 på et POST, der burde virke: læs fejlen i terminalen. duplicate key betyder, at CPR'en allerede findes — fra en tidligere kørsel eller fra en anden i gruppen. Skift CPR i body i api.http.

Hvis du har ekstra tid

  • Send samme POST to gange. Det andet svar er 500, fordi UNIQUE på cpr afviser rækken. Er 500 den rigtige kode? Klienten har jo sendt noget forkert, ikke serveren. Find fejlkoden i terminalen (23505), og diskutér i gruppen, hvilken statuskode der passer bedre.
  • Skift CPR i body i api.http igen, og sæt navnet til x'); DELETE FROM patients; --. Send den. Hvad står der i tabellen bagefter? Hvorfor er de andre patienter der stadig?

Øvelse 3 — Gruppens handling

Mål: Gruppens centrale brugerhandling når hele vejen ned i tabellen: en formular i mockuppet sender POST, og serveren gemmer i gruppens egen tabel fra lektion 15. Det er kernen i D2.

Brug syntetiske data. Ingen rigtige CPR, navne eller målinger.

Det skal I nå

  1. Installér cors, og erstat jeres egen Access-Control-Allow-Origin-middleware fra lektion 13. app.use(cors()) skal stå før jeres routes:
npm install cors
const cors = require("cors");
app.use(cors());
  1. Lav et repository til gruppens tabel — en ny fil, opkaldt efter tabellen, fx maalinger.js. Én funktion, der opretter en række med INSERT … RETURNING og pladsholdere. Brug patients.js som skabelon.
  2. Lav POST /api/<jeres tabel> i server.js. Samme mønster som patienter: 400 før databasen, 201, 500 i catch.
  3. Test routen fra api.http, før I rører HTML'en.
  4. I mockuppet: på den side, hvor brugeren laver gruppens handling, lav en formular, der sender POST med fetch. Udgangspunktet nedenfor er til patienter — ret felterne og URL'en til jeres tabel.
  5. Åbn DevTools → Network, og send formularen. I skal se OPTIONS med 204 og derefter POST med 201.
  6. Ejeren af databasen finder rækken i Neons SQL-editor med SELECT.

Udgangspunkt

<form id="ny-patient">
  <label for="cpr">CPR</label>
  <input id="cpr" type="text">
  <label for="navn">Navn</label>
  <input id="navn" type="text">
  <p id="fejl" hidden>Kunne ikke gemme. Tjek felterne.</p>
  <p id="ok" hidden>Gemt.</p>
  <button type="submit">Gem</button>
</form>
const form = document.getElementById("ny-patient");
const fejl = document.getElementById("fejl");
const ok = document.getElementById("ok");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  fejl.hidden = true;
  ok.hidden = true;
  try {
    const res = await fetch("http://localhost:3000/api/patients", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        cpr: document.getElementById("cpr").value,
        navn: document.getElementById("navn").value
      })
    });
    if (!res.ok) {
      fejl.hidden = false;
      return;
    }
    ok.hidden = false;
    form.reset();
  } catch (err) {
    fejl.hidden = false;
  }
});

try/catch fanger, at serveren slet ikke svarer — fx fordi den ikke kører. !res.ok fanger 400 og 500.

Tjekliste

  • Gruppens tabel har sin egen repository-fil. server.js indeholder ingen SQL
  • Alle værdier fra requestet går gennem $1, $2, …
  • Formularen viser en fejl, hvis et felt mangler (400)
  • Network viser OPTIONS 204 og POST 201
  • Rækken står i tabellen i Neon

Øvelsen er i hus, når en anden i gruppen kan udfylde formularen og ejeren kan vise rækken i SQL-editoren.

Hvis I sidder fast

  • OPTIONS med 404 i Network: app.use(cors()) mangler, eller den står under 404-handleren.
  • OPTIONS med 204, men POST bliver blokeret af CORS i Console: app.use(cors()) står under jeres routes. Flyt den op, før routes.
  • Siden genindlæser ved submit: event.preventDefault() mangler.
  • 400 hver gang: tjek, at nøglerne i JSON.stringify({ … }) hedder det samme som det, routen læser fra req.body.
  • Fejlbeskeden kommer, og Network viser intet POST: serveren kører ikke.
  • Gruppens tabel har en fremmednøgle til patients: send patient_id med i body, og tjek i routen, at den er der.

Hvis I har ekstra tid

  • Lav GET /api/<jeres tabel> og en funktion i repositoryet, der henter rækkerne. Vis dem på siden, og hent igen efter 201 — samme mønster som patienttabellen fra lektion 7. Genstart serveren, og se, at rækkerne er der stadig.
  • Skriv et testskema som i øvelse 2 for gruppens egne routes, og læg det i projektloggen.
  • Tegn sekvensdiagrammet for jeres handling: bruger, browser, route, repository, Neon. Det kan bruges direkte i D2.