javascript
35 lines · 8 steps
Conditional GET caching in Express
An Express route serves an article but returns 304 Not Modified when the client's cached copy is still fresh.
Explained by
highlit
1const express = require('express');
2const router = express.Router();
3
4router.get('/articles/:slug', async (req, res, next) => {
5 try {
6 const article = await Article.findOne({ slug: req.params.slug });
7
8 if (!article) {
9 return res.status(404).json({ error: 'Article not found' });
10 }
11
12 const lastModified = article.updatedAt;
13 res.set('Last-Modified', lastModified.toUTCString());
14 res.set('Cache-Control', 'public, max-age=0, must-revalidate');
15
16 const ifModifiedSince = req.get('If-Modified-Since');
17 if (ifModifiedSince) {
18 const since = new Date(ifModifiedSince);
19 if (!Number.isNaN(since.getTime()) && lastModified.getTime() <= since.getTime() + 999) {
20 return res.status(304).end();
21 }
22 }
23
24 res.json({
25 slug: article.slug,
26 title: article.title,
27 body: article.body,
28 updatedAt: lastModified.toISOString(),
29 });
30 } catch (err) {
31 next(err);
32 }
33});
34
35module.exports = router;
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Sending Last-Modified lets clients revalidate cheaply instead of refetching unchanged data.
- 2A 304 response has no body, so you save bandwidth when nothing has changed.
- 3Comparing timestamps needs a small tolerance because HTTP dates have second-level precision.
Related explainers
javascript
const express = require('express'); const app = express();
Enforcing HTTPS with Express middleware
middleware
https
security
Intermediate
6 steps
rust
use std::time::Duration; #[derive(Debug, PartialEq)] pub enum ParseDurationError {
Parsing duration strings safely in Rust
parsing
error-handling
checked-arithmetic
Intermediate
8 steps
go
package handlers import ( "net/http"
Custom validators and binding in Gin
validation
struct-tags
error-handling
Intermediate
8 steps
ruby
class ImageNormalizer ORIENTATION_TRANSFORMS = { 1 => ->(img) {}, 2 => ->(img) { img.flop },
Correcting EXIF orientation in Ruby
lookup-table
lambdas
image-processing
Intermediate
7 steps
javascript
function parseHexColor(hex) { const cleaned = hex.trim().replace(/^#/, ''); const expand = (short) =>
Parsing hex colors into RGBA channels
parsing
bitwise
regex
Intermediate
7 steps
javascript
import { useReducer, useCallback } from 'react'; function historyReducer(state, action) { const { past, present, future } = state;
Undo/redo form state with a React reducer
undo-redo
reducer
immutability
Intermediate
10 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/conditional-get-caching-in-express-explained-javascript-4aca/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.