Lektion 11 — Client-server
Christian Budtz — chbu@dtu.dk
1 / 27
F fuld skærm · B sort · O overblik
Christian Budtz — chbu@dtu.dk
server.jsPauser lægges ind undervejs.
Efter lektionen skal du kunne:
GET /api/patients og 404 ellerslocalhost og se, at listen bor i server-processenKlienten starter samtalen. Serveren venter og svarer. Uden et request sker der ingenting på serveren.
Klienten behøver ikke vide, hvordan svaret blev fundet. Den skal kende protokollen.
sequenceDiagram
participant Klient
participant Server
Klient->>Server: request
Server-->>Klient: response
En kliniker slår en patient op. Journalens webserver er server for browseren. Samme webserver er klient, når den spørger databasen efter patientens række.
sequenceDiagram
participant Browser
participant Journalserver
participant Patientdatabase
Browser->>Journalserver: hent patienten
Journalserver->>Patientdatabase: hent rækken
Patientdatabase-->>Journalserver: data
Journalserver-->>Browser: patienten
Det er separation of concerns. Hvert led svarer kun på det, det blev spurgt om.
En tynd klient viser. Serveren beslutter.
En tyk klient kan noget uden serveren. Wikipedia kalder det rich client. Vi siger tyk.
Jeres mockup er tykt. Præsentation og beslutninger — login-tjek, render, kladde — ligger i browseren. Login og kladde gemmes i localStorage, og localStorage er ikke en server. Patientlisten gemmes ikke i browseren. Den hentes fra underviserens server.
Et program, der aldrig sender eller modtager noget over nettet, er ikke en klient. Jeres fetch til underviser-API'et var allerede et request. Login og kladde i localStorage var det ikke.
Et lag er et ansvar. En tier er et sted, det kører. Samme tre lag kan ligge på én maskine.
I mockuppet gør de det næsten:
flowchart TB
subgraph browser [Browseren]
direction TB
P[Præsentation] --> A[Applikation] --> D["Data: localStorage (login, kladde)"]
end
A -->|fetch| API["Underviserens API: patientlisten"]
Præsentation er det, brugeren ser. Applikation er beslutningen. Data er det, der skal kunne hentes igen. Patientlisten kom allerede fra en anden maskine — men ikke fra jeres egen.
Præsentationen bliver i browseren. Applikationen flytter over i serveren. I dag er den stadig bare en liste i processen. Datalaget bliver PostgreSQL i lektion 15. Jeres eget diagram tegner I, når serveren svarer.
flowchart LR
subgraph klient [Browser]
P[Præsentation]
end
subgraph server [Node]
A[Applikation]
end
subgraph database [PostgreSQL]
D[Data]
end
P --> A --> D
JavaScript kan køre uden for browseren. Det er Node.
Browseren har DOM. Node har den ikke. Node kan tage imod HTTP.
I har kørt en fil med node. Processen skrev en linje og stoppede. Serveren er den proces, der bliver ved med at vente.
Gå til /quiz og indtast koden fra tavlen.
Lektion 11: forberedelse — client-server, tynd og tyk, tre lag.
const res = await fetch("https://it3e26.vercel.app/api/patients");
Browseren anmoder. En server svarer med JSON. I dag skriver I den server, for én liste.
GET henter. POST sender noget, der skal gemmes. I dag er det kun GET. Stien /api/patients er den ressource, requestet gælder. En anden sti er et andet request.
200 betyder: her er indholdet. 404 betyder: den sti findes ikke. Statuskoden er første linje i svaret. Den er ikke en del af JSON-listen.
På et GET, der lykkes, er kroppen listen. Samme felter som tabellen allerede læser:
[{ "cpr": "2512489996", "navn": "Nancy Ann Test Berggren" }]
HTTP er stateless. Næste request starter forfra. Derfor er localStorage ikke en session. Sessioner er lektion 19.
Siden og localhost:3000 er to oprindelser. Browseren spørger kun på tværs, hvis svaret tillader det. Samme aside som i lektion 7:
res.setHeader("Access-Control-Allow-Origin", "*");
server.js i det projekt, I allerede har. Ingen Express, ingen database, ingen HTML fra serveren. Lektion 13 åbner den samme fil igen.
Listen bor i processen. Det er ikke datalaget. Det er en liste i hukommelsen, så I kan se grænsen.
const http = require("node:http");
const patients = [
{ cpr: "2512489996", navn: "Nancy Ann Test Berggren" },
{ cpr: "0107729995", navn: "Max Test Berggren" }
];
const server = http.createServer((req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Content-Type", "application/json");
if (req.method === "GET" && req.url === "/api/patients") {
res.writeHead(200);
res.end(JSON.stringify(patients));
return;
}
res.writeHead(404);
res.end(JSON.stringify({ error: "not found" }));
});
server.listen(3000);
Uden listen er der ingen server. Metode og sti skal begge passe. En forkert sti må ikke ligne en tom patientliste.
Det kald, der lykkes, ser sådan ud. Jeres diagram tegner I først, når I har kørt det:
sequenceDiagram
participant Browser
participant Server
Browser->>Server: GET /api/patients
Server-->>Browser: 200 og JSON
Start med node server.js. Åbn http://localhost:3000/api/patients og se JSON, før I rører ved tabellen.
Skift URL'en i det fetch, I allerede har, fra https://it3e26.vercel.app/api/patients til http://localhost:3000/api/patients. Tabellen kender felterne. Den skal ikke skrives om.
Ret et navn i listen, genstart processen, og hent igen. Navnet skifter, fordi listen nu kommer fra den proces, I startede. Ikke fra underviserens server.
Filen er ikke tre lag. Listen og svaret ligger samme sted. Express er lektion 13. PostgreSQL er lektion 15.
Skriv server.js. GET /api/patients svarer 200 og JSON. Alt andet svarer 404.
Tabellen henter fra localhost:3000. Et ændret navn ses efter genstart. Åbn HTML-filen lokalt. Siden på Vercel kan ikke nå den bærbare.
Tegn det kald, I lige har kørt. Livlinjer: bruger, browser, server. Databasen er ikke med.
sequenceDiagram
participant Bruger
participant Browser
participant Server
Bruger->>Browser: åbn patientlisten
Browser->>Server: GET /api/patients
Server-->>Browser: 200 og JSON
Browser-->>Bruger: tabel
Navngiv præsentation, applikation og data ud fra koden. Skriv, at HTML-filen og Node kører på den bærbare, og at Vercel ikke er med i kaldet.
Diagrammet skal handle om jeres kald, ikke om journal-eksemplet. Detaljerne står i øvelsesarket.