In October, I wrote about cross-origin resource sharing (CORS). I explained that if you want a resource on your server to be accessible from other origins, you need to set the Access-Control-Allow-Origin HTTP response header. I offered some examples using a simple HTTP server in Node.js. Because it's more common to use Express, I thought I'd explain how to write a simple CORS middleware function.

The cors package

The cors package is an official middleware function, much like the express.json() method. The only difference is that the former isn't part of the core Express framework, so you have to install it via npm. With that in mind, I'm explaining how to write your own CORS middleware for two reasons:

  1. It's a learning experience.
  2. You probably don't need the cors package if you're only handling simple requests. You don't want to be one of those developers who installs npm packages like they're popping painkillers.

If you need robust CORS middleware, you should probably just use the cors package. It handles all aspects of CORS including preflight requests.

A simple Express server

The following is a simple Express server with a single GET handler for the root route (/). It sends a simple JSON response with .code and .message properties. We'll add a CORS middleware function to this server.

const http = require("http");
const express = require("express");

let app = express();

app.set("json spaces", 2);

app.get("/", (_req, res) => {
let code = 200;
res.send({ code, message: http.STATUS_CODES[code] });
});

let port = 8080;

app.listen(port, () => {
console.log(`Listening on port ${port}`);
});

Allowing any origin

Let's declare a middleware function, cors(), with three parameters:

  1. _req - The Request object.
  2. res - The Response object.
  3. next - The next middleware function in the stack.

When Express calls the cors() middleware function, it will pass in arguments for the _req, res, and next parameters in that order. Because we won't be reading the value of the _req parameter, we've prefixed it with an underscore. This is merely a convention that tells well-meaning developers: I know I'm not reading the value of this parameter, but the parameter needs to be in this position, so please don't remove it.

function cors(_req, res, next) {}

As I wrote in my previous post, To allow CORS requests from any origin, you can use the * wildcard as the value of the Access-Control-Allow-Origin header. To set the header, let's invoke the res.set() method, passing in strings for the header name and value. Then we'll invoke next(), i.e. the next middleware function in the stack.

function cors(_req, res, next) {
res.set("Access-Control-Allow-Origin", "*");
next();
}

Allowing a single origin

In my previous post, I wrote To allow CORS requests from a single origin, you can use the origin as the value of the Access-Control-Allow-Origin header. For example, to allow CORS requests from my website only, you would use https://barker.codes as the value. To handle this, let's update the call to the res.set() method.

function cors(_req, res, next) {
res.set("Access-Control-Allow-Origin", "https://barker.codes");
next();
}

In my previous post, I also quoted the MDN Web Docs on the significance of the Vary header in this situation:

Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard). In that case, the response should also include a Vary response header with the value Origin — to indicate to browsers that server responses can differ based on the value of the Origin request header.

Let's update the call to the res.set() method again. Instead of passing in strings for the header name and value, we'll pass in an object literal. This lets us set the Access-Control-Allow-Origin and Vary headers at the same time.

function cors(_req, res, next) {
res.set({
"Access-Control-Allow-Origin": "https://barker.codes",
Vary: "Origin",
});

next();
}

Allowing a list of origins

In my previous post, I went on to quote the MDN Web Docs again, this time about allowing a list of origins:

Limiting the possible Access-Control-Allow-Origin values to a set of allowed origins requires code on the server side to check the value of the Origin request header, compare that to a list of allowed origins, and then if the Origin value is in the list, set the Access-Control-Allow-Origin value to the same value as the Origin value.

Let's update our code to handle this. Just before the middleware function, we'll declare an ALLOWED_ORIGINS constant with a Set of allowed origins. Declaring the constant outside the middleware function is more performant because it prevents it from being declared every time the function is invoked.

In the body of the middleware function, we'll get the value of the Origin header by invoking the req.get() method and assigning the return value to the origin variable. If the set of allowed origins .has() the value of the origin variable, we'll set the Access-Control-Allow-Origin header to the value. Then we'll set the Vary header and invoke next() as before.

const ALLOWED_ORIGINS = new Set([
"https://barker.codes",
"https://gomakethings.com",
]);

function cors(_req, res, next) {
let origin = req.get("Origin");

if (ALLOWED_ORIGINS.has(origin)) {
res.set("Access-Control-Allow-Origin", origin);
}

res.set("Vary", "Origin");
next();
}

Using the middleware function

To use the middleware function for every route, you can pass it into the app.use() method.

app.use(cors);

app.get("/", (_req, res) => {
res.send({ code: 200, message: http.STATUS_CODES[200] });
});

To use the middleware function for a single route, you can pass it into appropriate HTTP request method, e.g. app.get() or app.post(). You should place it after the path but before your route handler. In this example, the route handler is the next middleware function in the stack, i.e. the value of the next parameter in the middleware function!

app.get("/", cors, (_req, res) => {
res.send({ code: 200, message: http.STATUS_CODES[200] });
});

Summary

In today's post, you learned how to write a simple CORS middleware function in Express. If you're only handling simple requests, this could be a better option than installing yet another dependency. If you're using more complex features of CORS, such as handling requests that need to be preflighted, it would probably be worth installing the official cors package.