Remembering with Cookies
Part of the Auth and Security section of Coddy's Node.js journey. Lesson 8 of 24.
HTTP forgets you between requests: each one arrives alone, and the server cannot tell it came from the same browser. A cookie fixes that. The server answers with a Set-Cookie header, and the browser sends the value back in a Cookie header on every later request to that site.
res.cookie(name, value) sets one. req.get('cookie') reads the Cookie header the browser sent, as one line of text. Express does not read the Cookie header for you (the cookie-parser package would), so cookies.js gives you parseCookies. Every value arrives as text.
app.get('/theme/:name', (req, res) => {
res.cookie('theme', req.params.name);
res.send('Saved');
});
app.get('/', (req, res) => {
const { theme = 'light' } = parseCookies(req.get('cookie'));
res.send(`Your theme is ${theme}`);
});Challenge
EasyGET /visit should count how many times this browser has called it. The server does not save the count. The count travels in a cookie, and the browser brings it back on every request.
- In
server.js, inGET /visit, read the cookies the browser sent, usingparseCookiesas the lesson shows. The count is in the cookie namedvisits. It arrives as text, like"2". On the first visit it is missing, which counts as 0. - Add 1 to the count as a number, so
"2"becomes3, not"21". - Set the
visitscookie to the new count, so the browser sends it back next time. - Respond with the new count as a number, not as text:
{"visits": 3}.
Try it: Press Run. Send GET /visit: {"visits": 1}. Send it again: {"visits": 2}. Send it a third time: {"visits": 3}. Open the Headers tab of that response: Set-Cookie says visits=3; Path=/.
Try it yourself
import express from 'express';
import { parseCookies } from './cookies.js';
import { notes, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
res.json({ username: user.username });
});
// Counts how many times this browser has called GET /visit.
app.get('/visit', (req, res) => {
const visits = 1;
res.json({ visits });
});
// Notes. Anyone can read and write them for now.
app.get('/notes', async (req, res) => {
res.json(await notes.find().toArray());
});
app.post('/notes', async (req, res) => {
const note = { text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Auth and Security
2Sessions and Cookies
Remembering with CookiesA Session LookupLogin SessionsRoutes Behind a LoginPractice on your own: Express playground