Da jeg første gang åbnede Chrome DevTools Network og alt gav mening

Da jeg første gang åbnede Chrome DevTools Network og alt gav mening

Har du nogensinde stirret på et fetch-kald der bare fejler, mens du tænker: “Hvorfor sker der ingenting, og hvorfor hjælper stack traces mig ikke?”

Chrome DevTools Network som sandhedskilde

Jeg vil starte med en lidt kontant påstand: Hvis du bygger noget med API-kald i browseren, og du ikke kigger i Chrome DevTools Network, så gætter du. Og gætning er en meget dyr sport, når man er udvikler.

Network-tabben er det sted, hvor virkeligheden bor. Ikke din teori om, hvad der burde blive sendt. Ikke den fine kode du har skrevet. Men det der faktisk forlader browseren, og det der faktisk kommer tilbage.

Det er også her, Chrome viser dig CORS-problemer, auth-fejl, payload-rod og små detaljer som cache og redirects, som ofte er årsagen til at ting “pludselig” virker lokalt men ikke i produktion.

Så lad os gøre det her konkret: Jeg går igennem en fast måde at bruge Chrome DevTools Network på, når et API-kald fejler. Ikke som et opslagsværk, men som en arbejdsgang du kan gentage.

En fast fejlfinding-rytme i Network

1. Åbn Network før du tester

Det første jeg gør, når noget med API’er driller, er faktisk ret simpelt: Jeg åbner Network-tabben før jeg prøver igen.

I Chrome:

Højreklik i siden → Inspect → fanen “Network”.

To små indstillinger jeg næsten altid tænder:

“Preserve log”: Så forsvinder requests ikke, når siden refresher eller routeren i din SPA skifter view.
“Disable cache”: Så du er sikker på, at browseren rent faktisk laver nye requests, og du ikke bare ser et cached svar.

Det lyder banalt, men jeg har siddet alt for mange gange og fejlsøgt på en request, som slet ikke blev sendt, fordi browseren servede et gammelt svar fra cache. Den slags fejl føles virkelig dumme, når man opdager dem.

2. Find den rigtige request

Når Network-tabben er åben og du har trigget din handling (klikket på knappen, loadet siden, hvad det nu er), så kommer der typisk mange linjer. Billeder, fonts, scripts, alt muligt støj.

Jeg starter næsten altid med at trykke på filteret “Fetch/XHR”. Så ser du kun ting som fetch, axios, XMLHttpRequest osv. Det er oftest her dine API-kald bor.

Hvis du kender noget af URL’en, kan du skrive et stykke af den i det lille filterfelt. Hvis din API fx ligger på /api/users, kan du skrive users og se listen blive kortere.

Til sidst klikker jeg på den request, der matcher tidspunktet for min handling, og som ligner den route jeg forventer at ramme. Det er det ene kolonnenavn, du skal have styr på: Name (eller “Path” afhængigt af visning).

Nu er scenen sat. Resten foregår i de detaljer, der dukker op i højre side.

Statuskode og timing: Er det overhovedet serveren, der siger nej?

3. Læs statuskode som første hint

Når du har klikket en request op, står statuskoden ret tydeligt. Det er dit første signal om, hvilken type fejl du har gang i. Nogle få koder går igen 90 % af tiden, når jeg hjælper folk med fejl:

200: Alt gik igennem. Kaldet “virkede” teknisk set. Hvis din app stadig brokker sig, er det nok din egen frontend-logik eller datahåndtering.

201: Noget blev oprettet (typisk POST). Igen: teknisk set virker det.

301 / 302: Redirect. Kan være helt fint (login-flow), kan også være årsagen til at du rammer en anden URL, end du troede.

400: Din request er forkert på en eller anden måde. Manglende felt, forkert format osv.

401: Du er ikke logget ind, eller din token er ugyldig. “Uautoriseret”.

403: Du er logget ind, men må ikke det her. “Forbudt”.

404: URL’en findes ikke. Forkert path, forkert base-URL, eller serveren har ikke det endpoint.

500: Fejl i backend. Din request kom igennem, men noget crashede.

Der er en vigtig mental øvelse her: Når du ser en statuskode, så beslut dig for, hvad du ikke gider gætte på. Fx:

Ser du 404? Så lad være med at bruge en time på at pille ved request-body. Det handler om URL’en, routing eller deploy.

Ser du 401? Så er det ikke din JSON-encoding der er problemet. Kig på auth-token, cookies, login.

Ser du 500? Så skal du ikke omskrive hele din fetch-kode. Fejlen er på serveren, og du skal bruge logfiler eller stack trace.

Det lyder næsten for simpelt, men det sparer virkelig tid.

4. Timing-fanen: Er det netværk eller langsom server?

I Network kan du også se Timing-fanen for en request. Den er god til at aflive myter om “serveren er nede”.

Eksempler:

Hvis “Waiting (TTFB)” (Time To First Byte) er meget lang, er serveren langsom om at svare. Det kan være tung backend-logik.

Hvis det meste af tiden ligger i “Stalled” eller “DNS Lookup”, er det mere netværksrelateret, eller der køres for mange requests på én gang.

Jeg bruger den mest som sanity check: Er det her et “min fetch er forkert” problem, eller et “den her server har det ikke godt” problem.

Headers, CORS og preflight: Hvad din browser prøver at fortælle dig

5. Request-headers: Hvad sender du faktisk?

Gå til Headers-fanen og rul til “Request Headers”. Her kan du se, hvad der reelt bliver sendt. Ikke hvad du tror din fetch-kode sender.

Et par vigtige felter, jeg ofte kigger på:

Content-Type: Matcher den din body? Hvis du sender JSON, vil du typisk have Content-Type: application/json og bruge JSON.stringify i din fetch.

Authorization: Er der overhovedet en token på? Står den i det format backend forventer, fx Bearer <token>?

Cookie: Hvis din auth kører på session cookies, kan du se her, om cookies faktisk følger med requesten.

Eksempel på klassisk fejl: Du har skrevet noget ala:

fetch("https://api.example.com/data", {
  method: "POST",
  body: { name: "Sara" }
});

Her vil Content-Type typisk være text/plain eller en generisk type, og serveren får en body, den ikke kan parse. I så fald skal du ændre det til:

fetch("https://api.example.com/data", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "Sara" })
});

Og ja, du kan se forskellen på de to kald inde i Network: både på headers og på request payload.

6. Response-headers: CORS og cookies i virkeligheden

I samme Headers-fane kan du se “Response Headers”. Det er især her, CORS og auth-fejl stikker hovedet frem.

Når CORS er i spil, kigger jeg efter:

Access-Control-Allow-Origin: Står din frontend-origin der? Ofte ser du Access-Control-Allow-Origin: * under udvikling eller en specifik URL i produktion.

Access-Control-Allow-Methods: Indeholder den den metode du bruger? Fx GET, POST, PUT.

Access-Control-Allow-Headers: Hvis du sender brugerdefinerede headers (Authorization, X-Custom-Header osv.), skal de stå her, ellers blokerer browseren.

Hvis du arbejder med login via cookies på tværs af domæner, kigger jeg også efter:

Set-Cookie: Sætter serveren overhovedet en cookie? Mangler SameSite=None; Secure når du kører på https på tværs af domæner?

Her kan du spare dig selv for meget frustration. Mange artikler om CORS er teoretiske. Network-tabben viser, hvad din egen server rent faktisk svarer med. MDN har en ok ressource om CORS-fejl, hvis du vil nørde detaljerne: MDN CORS errors.

7. OPTIONS / preflight: Det skjulte kald

Noget der forvirrer mange, er de der mystiske OPTIONS-requests. Du ser måske, at din frontend laver to requests: først en OPTIONS, så en POST/PUT/DELETE.

Det er browserens “preflight”. Den spørger serveren: “Hvis jeg nu sender et request med de her metoder og headers, er du så ok med det?”

I praksis sker det typisk når:

Du bruger en metode som ikke er simpel (fx PUT, DELETE).

Du sender custom headers, fx Authorization.

Du rammer et andet origin (andet domæne/port/protokol) end din frontend.

Når jeg debuggger CORS, kigger jeg på OPTIONS-requesten. Hvis den fejler (4xx eller ingen svar), bliver det egentlige kald aldrig kørt. Browseren stopper det.

I Network ser det typisk sådan her ud:

En OPTIONS request med status 200 og korrekte CORS-headers → din rigtige request bliver lavet bagefter.

En OPTIONS request med 404 eller 500 → du har ikke et endpoint der svarer på OPTIONS, eller din CORS-middleware er sat forkert op.

Hvis du aldrig har åbnet en OPTIONS-request i detaljer, er det en god lille øvelse: Find en, klik på den, og se hvilke request- og response-headers der bliver udvekslet. Det er der, CORS-samtalen foregår.

Body og response: Er det dig eller backend der misforstår data?

8. Request payload: Hvad du sender ned i røret

I Network findes en fane der hedder Payload (i ældre versioner ligger det under “Request Payload” eller “Form Data” i Headers-fanen). Her kan du se præcis, hvad browseren har sendt.

Tre typiske scenarier:

JSON: Du ser et objekt repræsenteret pænt. Matcher det backendens forventede struktur? Stavefejl i feltnavne? Manglende felter?

Form data: Hvis du bruger FormData eller en form med enctype="multipart/form-data", ser du nøgler og værdier enkeltvis.

Ingen body: Du troede måske, du sendte noget, men der er ingenting. Det sker ofte, hvis man har glemt at returnere noget fra et event-handler eller har fejl i fetch-kaldet.

Eksempel: Du kalder en API der forventer { email: string, password: string }, men i Payload-fanen ser du:

{
  "mail": "test@example.com",
  "password": "secret"
}

Her kan backend sagtens svare med 400 “invalid body” eller noget i den stil. Du behøver ikke gætte. Network viser dig, at du sender det forkerte felt.

9. Response: Læs fejlbeskeden som data, ikke skældud

Til sidst er der Response-fanen. Mange klikker aldrig på den, fordi de ser fejlen i konsollen og stopper der.

Men mange backend-API’er returnerer faktisk nyttige fejlformater, fx:

{
  "error": "INVALID_TOKEN",
  "message": "Token is expired"
}

eller

{
  "errors": [
    { "field": "email", "message": "Email is required" },
    { "field": "password", "message": "Password is too short" }
  ]
}

Her kan du direkte mappe det til din UI-logik. Hvis du ved, hvordan API’et at se ud, når det fejler, kan du også hurtigere se, når det er en forventet fejl, men en reel server-crash. Nogle gange ligger der endda en stack trace i response, især på staging eller lokale miljøer.

Hvis du bygger både frontend og backend selv, kan du bevidst designe fejlformater, som er nemme at forstå i Network. Det gør fejlfinding senere meget mindre smertefuld.

Hvis du vil se flere eksempler på at læse API-responses og bygge ovenpå dem, har vi også artikler om API-arbejde og debugging under webudvikling på Coding Class.

Tre mini-cases direkte fra Network

Case 1: 401 vs 403 – hvorfor må jeg ikke komme ind?

Forestil dig et SPA-dashboard. Du logger ind, får en token, og så henter appen dine data med:

fetch("https://api.example.com/me", {
  headers: {
    Authorization: "Bearer " + token
  }
});

En dag viser appen bare en generisk fejl. I Network ser du:

Request: GET /me
Status: 401
Response body: { "error": "TOKEN_EXPIRED" }

Konklusion: Din token er udløbet. Fejlen løses ikke ved at “fixe fetch”. Du skal enten logge brugeren ud, refresh’e token, eller ændre backendens udløbslogik.

Samme setup, men nu ser du:

Status: 403
Response body: { "error": "INSUFFICIENT_ROLE" }

Nu er problemet ikke login, men rettigheder. Brugeren er logget ind, men må ikke se den her resource. Det er et autorisationsproblem, ikke et auth-problem.

Network viser begge scenarier tydeligt. Du slipper for at gætte på “nogle gange virker det, nogle gange ikke”.

Case 2: CORS preflight dræber din POST

Du har en frontend på http://localhost:5173 (Vite) og en backend på http://localhost:3000. Du laver et POST-kald med Authorization-header. I konsollen står der noget i stil med:

Access to fetch at 'http://localhost:3000/api/data' from origin 'http://localhost:5173' has been blocked by CORS policy...

I Network ser du:

OPTIONS /api/data404
POST /api/data → står som “(canceled)” eller kommer aldrig afsted

Hvis du klikker på OPTIONS-requesten, ser du måske, at backend slet ikke håndterer OPTIONS på den route. Din CORS-middleware er kun sat op på nogle endpoints eller kun på GET.

Løsningen lever i backend-konfigurationen, ikke i fetch-kaldet. Når du får OPTIONS til at svare med de rigtige CORS-headers og en 200, vil POST’en pludselig dukke op i Network.

Case 3: 404 efter deploy – men alt virkede lokalt

Du har deployet din frontend til fx Netlify, og din backend kører på en hosted URL. Lokalt virkede alt fint. I produktion får du 404, når du kalder API’et.

I Network i produktion ser du noget ala:

Request URL: https://din-frontend.netlify.app/api/users
Status: 404

Du havde måske forestillet dig, at du ramte https://din-backend.com/api/users, men i din fetch har du skrevet en relativ URL:

fetch("/api/users")

Lokalt har du haft en proxy i din dev-server, der sendte /api videre til backend. I produktion findes der ingen sådan proxy, så du rammer frontendens eget domæne.

Network viser dig den fulde URL, så du hurtigt kan se, at problemet er base-URL og miljøopsætning, ikke selve API’et.

Her er mønstret i mange deploy-fejl: Network fortæller dig, du faktisk sender requestet hen. Når du først ser hele URL’en, falder brikkerne ofte på plads.

Sådan spørger du om hjælp med Network som bevis

Den sidste ting jeg vil give dig, er ikke et teknisk trick, men en arbejdsvane. Når du sidder fast og skal spørge en ven, lærer, Discord eller Stack Overflow om hjælp, så gør dig selv (og dem) en tjeneste:

Tag et par målrettede screenshots fra Network.

Jeg plejer at samle:

1) Et screenshot af Network-listen hvor du har markeret den relevante request (og fanen Fetch/XHR er valgt).
2) Et screenshot af Headers-fanen for den request, hvor man kan se:

– Request URL
– Method
– Status code
– De vigtigste request-headers (Content-Type, Authorization osv.)
– De vigtigste response-headers (Access-Control-Allow-*, Set-Cookie hvis relevant)

3) Et screenshot af Payload (hvis der er body) og evt. Response hvis der er en JSON-fejlbesked.

Når du så skriver din besked, kan du være specifik:

“Jeg prøver at POST’e til https://api.example.com/login fra http://localhost:5173. I Network kan jeg se en OPTIONS med 404 og ingen CORS-headers. Min fetch ser sådan her ud: … Hvad overser jeg i min backend-CORS-konfiguration?”

Det er niveauet hvor folk faktisk kan hjælpe dig, i stedet for at skyde i blinde med “har du prøvet at…”-forslag. Og du træner samtidig din egen evne til at læse Network som et værktøj, ikke som et mystisk ekstra lag.

Hvis du vil blive bedre til generel debugging og ikke bare CORS, har vi også artiklen om at debugge rigtigt i stedet for at stirre på console, som spiller ret godt sammen med det, du lærer i Network.

Tilbage til første gang jeg åbnede Network

Jeg kan stadig huske første gang jeg forstod, hvad der faktisk foregik i Network. Jeg sad med et lille projekt til en ven, et simpelt login, der “nogle gange” virkede. Jeg skiftede tilfældigt mellem at være sikker på, at det var serveren, og helt sikker på, at det var min JavaScript.

Da jeg endelig åbnede Network og så, at min token slet ikke blev sendt med i Authorization-headeren, var det næsten pinligt. Fejlen var ikke magisk. Den var synlig. Jeg havde bare ikke kigget det rigtige sted.

Siden da har jeg haft en ret simpel refleks: Hvis noget med API’er driller, åbner jeg Chrome DevTools Network, før jeg åbner Google. Det føles lidt som at tjekke om stikket sidder i, før man ringer til internetudbyderen. Kedeligt måske, men det virker.

Og ja, katten ligger stadig halvt oven på tastaturet, mens jeg klikker mig igennem headers og payloads. Men nu er det sjældent, at jeg sidder og gætter. Jeg ser, hvad der faktisk sker.

Klik på en request og brug fanerne Headers, Payload og Response. Headers viser forespørgsel- og svar-headers plus query-params, Payload viser form-data eller JSON du sendte, og Response eller Preview viser svaret fra serveren. Du kan også højreklikke og vælge 'Copy' - 'Copy as cURL' for at reproducere kaldet lokalt.
Tjek response-headers for Access-Control-Allow-Origin og relaterede CORS-headers, og se om preflight OPTIONS-kaldet lykkes eller fejler. Manglende eller for snævre Access-Control-headers på serveren er ofte årsagen, og console-fejlmeddelelsen (No 'Access-Control-Allow-Origin' header) kombineret med Network-detaljerne fortæller præcis hvad der mangler.
Ja - højreklik på en request og vælg 'Replay XHR' for at sende samme kald igen, eller 'Edit and Resend' for at ændre headers eller body før du sender. Alternativt brug 'Copy as fetch' eller 'Copy as cURL' for at teste kaldet uden for browseren.
Tjek at Authorization-header eller Cookie-header reelt sendes med requesten og at serveren returnerer forventede Set-Cookie-headers. Vær opmærksom på SameSite, Secure og missing credentials - ved fetch skal du f.eks. bruge credentials: 'include' for at sende cookies.

Sara Vestergaard er selvlært kode-nørd, der stille og roligt er gået fra at rode med en enkelt HTML-side til at bygge små værktøjer, scripts og hjemmesider til sig selv og vennerne. Hun startede med at lave en simpel band-hjemmeside som teenager og opdagede, hvor tilfredsstillende det er, når noget, du har skrevet, pludselig lever på skærmen.

For Sara handler kodning ikke om store ord eller imponerende titler, men om meget konkrete problemer: den kedelige opgave, der tager for lang tid, den ven der mangler en lille porteføljeside, eller den liste, der burde sortere sig selv. Hun elsker at pille ting fra hinanden – også kode – for at se, hvad der egentlig foregår, og hun har brugt utallige aftener på at google fejlbeskeder, teste små eksempler og langsomt bygge sin forståelse op.

På Coding Class deler hun den tilgang videre. Hun skriver til dig, der gerne vil lære at kode ved at gøre det i praksis: små projekter, korte kodebidder og forklaringer, der hænger sammen med det, du faktisk sidder med på skærmen. Hun skærer ind til benet, viser typiske fejl og deres løsninger og giver altid et forslag til, hvordan du kan bygge en tand videre, når grundideen først virker.

Når hun ikke skriver til Coding Class eller nørkler med nye små projekter, hænger Sara på klatrevæggen, vander sine altanplanter eller spiller gamle Nintendo-spil. Men hun ender næsten altid tilbage ved tasterne – for der er altid endnu en lille ting, der kunne være smartere, hurtigere eller bare lidt sjovere at bruge.

1 kommentar

comments user
Jens

altså gemmer denne om Network-tabben – sååå brugbart, deler med min datter senere

Send kommentar

You May Have Missed