Make a Small Web App Faster with Practical Caching

Computer Science

Make a Small Web App Faster with Practical Caching

JavaScriptNode.jsNode.js built-in http module

Make a Small Web App Faster with Practical Caching

You will build a small Node.js web app that retrieves product data, then measure and improve it using browser/HTTP caching, server-side caching, database-style caching, and in-memory caching. Each step leaves the app faster or more observable, so you finish with a project you can run and demonstrate.

4 modules10 lessonsComputer ScienceJavaScriptNode.jsNode.js built-in http moduleJSONHTTP Cache-Control headersETagin-memory Map

What you learn by building this

  • Explain what a cache stores, why it improves performance, and when it can return stale data
  • Inspect browser and HTTP caching behavior using response headers and repeated requests
  • Add and verify server-side in-memory caching for a slow data lookup
  • Use a local JSON file as a simple database-like source and cache its results safely
  • Choose cache duration, keys, and invalidation behavior for a small web application
  • Measure the visible difference between cold-cache and warm-cache requests

Learning Journey

1

See the Cost of Repeated Work

2 lessons

Create a small web app whose repeated product lookups are visibly slower than necessary. The learner first gets a working baseline and simple measurements before adding any cache.

2

Cache at the Browser and HTTP Boundary

2 lessons

Use HTTP semantics to let a client reuse a response instead of downloading unchanged data every time. The learner verifies the behavior through observable headers and conditional requests.

3

Cache Work Inside the Server

3 lessons

Move caching into the application so repeated requests avoid the slow data-loading step even when the client asks for the resource again. The learner builds a small, understandable in-memory cache rather than hiding the behavior behind a service.

4

Choose and Prove a Caching Strategy

3 lessons

Connect the layers into a practical design and finish with a visible comparison. The learner makes caching decisions based on freshness and workload rather than adding cache code blindly.

Public lesson

Create the baseline product app

Baseline product app: data from a local JSON file

A product app has two separate jobs:

  1. Load product data from somewhere.
  2. Choose a response when a request arrives.

For this first version, keep both parts deliberately small: the data stays in a local JSON file, and a Node.js HTTP server returns either the whole list or one product by ID. The important detail is that the server should not return the same response for every URL. It needs to inspect the request path and make a decision.

Tasks

1. Inspect the ready project

Open the existing project and identify:

  • the server entry file
  • the local JSON file intended for product data
  • the documented command for starting the app

Do not add setup or scaffolding. Use the project’s existing structure and start command.

If the project does not yet contain product data, add a small list to its existing local JSON file. Use a shape like this, adapting the fields to the project:

[
  {
    "id": "1",
    "name": "Notebook",
    "price": 8.5
  },
  {
    "id": "2",
    "name": "Desk lamp",
    "price": 24
  }
]

Keep the IDs as strings. A URL such as /products/2 gives the server "2" as part of the path, so comparing strings avoids an unnecessary type conversion.

2. Make the server read the local data

In the existing server entry file, begin with this incomplete shape. Replace the JSON import path with the actual path used by the project:

const http = require("node:http");
const products = require("./REPLACE_WITH_THE_LOCAL_JSON_PATH");

const server = http.createServer((request, response) => {
  const url = new URL(request.url, `http://${request.headers.host}`);
  const pathParts = url.pathname.split("/").filter(Boolean);

  response.setHeader("Content-Type", "application/json");

  // TODO:
  // - return the complete product list for the collection route
  // - return one product for a route containing an ID
  // - return a 404 response for anything else
});

server.listen(/* use the project's existing port convention */);

The URL object gives you a reliable pathname. Splitting that pathname turns:

/products/2

into:

["products", "2"]

That makes the two routes easy to distinguish:

  • ["products"] means “return the collection”
  • ["products", id] means “find one product”

Tasks

Now complete the request handling. Your code should:

  • respond only to GET requests
  • send the full array for the collection route
  • find a matching product when an ID is present
  • send status 404 when the route or product does not exist
  • serialize response data with JSON.stringify(...)

A useful incomplete decision block to adapt is:

if (request.method !== "GET") {
  // send a method-not-allowed response
} else if (pathParts.length === 1 && pathParts[0] === "products") {
  // send the complete products array
} else if (pathParts.length === 2 && pathParts[0] === "products") {
  const id = pathParts[1];

  // Find the product whose id matches `id`.
  // Then send it, or send a 404 if there is no match.
} else {
  // send a 404 response
}

For each response, remember that a response needs both a status and a body. For example, the general pattern is:

response.statusCode = 200;
response.end(JSON.stringify(value));

For a missing product, use a non-success status and a small JSON error object rather than returning an empty success response. That distinction lets a browser, test, or future client tell “there are no matching products” apart from “the request succeeded.”

Tasks

3. Start and check the app

Run the project using its documented start command.

Request the collection route in a browser or with the project’s usual request tool:

/products

You should see the JSON array from the local file.

Then request a product that exists:

/products/2

You should see one object rather than the whole array.

Finally, request an ID that is not in the file:

/products/does-not-exist

This should produce a 404 response and a JSON error body. Also try a path such as:

/not-a-product-route

It should not accidentally return the product list.

4. Notice what the route decision buys you

Without the route check, the server could load the data, but it would not yet be an API: every request would receive the same thing. The small pathParts decision creates two useful meanings from one data source:

  • /products asks for the collection
  • /products/:id asks for a member of that collection

The local JSON file remains the source of truth, while the HTTP server becomes the boundary that presents that data to a browser or another program.

Tasks

After the checks pass, change one product’s name or price in the JSON file, restart the app if the project caches the imported file, and request the product again. The response should reflect the local file rather than a hard-coded value in the server. That is the baseline product app working end to end: local data is loaded, a request selects the right product view, and the result is observable over HTTP.

Course Outline

4 modules · 10 lessons

See the Cost of Repeated Work

Cache at the Browser and HTTP Boundary

Cache Work Inside the Server

Choose and Prove a Caching Strategy

Learn by building your own version.

Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.