typescript
64 lines · 9 steps
Parsing a Set-Cookie header in TypeScript
Split a raw Set-Cookie string into a typed object by handling the name/value pair first, then each attribute.
Explained by
highlit
1interface ParsedCookie {
2 name: string;
3 value: string;
4 domain?: string;
5 path?: string;
6 expires?: Date;
7 maxAge?: number;
8 secure: boolean;
9 httpOnly: boolean;
10 sameSite?: "Strict" | "Lax" | "None";
11}
12
13export function parseSetCookie(header: string): ParsedCookie {
14 const [pair, ...attrs] = header.split(";");
15 const eq = pair.indexOf("=");
16 if (eq === -1) throw new Error(`Invalid cookie pair: ${pair}`);
17
18 const cookie: ParsedCookie = {
19 name: pair.slice(0, eq).trim(),
20 value: decodeURIComponent(pair.slice(eq + 1).trim()),
21 secure: false,
22 httpOnly: false,
23 };
24
25 for (const attr of attrs) {
26 const idx = attr.indexOf("=");
27 const key = (idx === -1 ? attr : attr.slice(0, idx)).trim().toLowerCase();
28 const raw = idx === -1 ? "" : attr.slice(idx + 1).trim();
29
30 switch (key) {
31 case "domain":
32 cookie.domain = raw.replace(/^\./, "");
33 break;
34 case "path":
35 cookie.path = raw;
36 break;
37 case "expires": {
38 const date = new Date(raw);
39 if (!Number.isNaN(date.getTime())) cookie.expires = date;
40 break;
41 }
42 case "max-age": {
43 const n = Number(raw);
44 if (Number.isInteger(n)) cookie.maxAge = n;
45 break;
46 }
47 case "secure":
48 cookie.secure = true;
49 break;
50 case "httponly":
51 cookie.httpOnly = true;
52 break;
53 case "samesite": {
54 const v = raw.toLowerCase();
55 if (v === "strict") cookie.sameSite = "Strict";
56 else if (v === "lax") cookie.sameSite = "Lax";
57 else if (v === "none") cookie.sameSite = "None";
58 break;
59 }
60 }
61 }
62
63 return cookie;
64}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Splitting on a delimiter and destructuring the head separates the mandatory pair from optional attributes cleanly.
- 2Normalizing keys to lowercase lets a single switch handle case-insensitive HTTP attribute names.
- 3Validating parsed values before assignment keeps malformed dates and numbers out of the typed result.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-a-set-cookie-header-in-typescript-explained-typescript-c494/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.