Enable CORS on Node.js/Express HTTP/API Server

The following code shows how to implement CORS on a Node.js/Express (v.4.x) server. If not familiar with JavaScript then read this tutorial. If not familiar with Node.js or NPM Express then read this tutorial which covers them both and more.

See this article for adding CORS to a Node.js server without NPM Express.

Here is a very basic Node.js/Express server that implements CORS. CORS is only enabled for the RESTful API.

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

const app = express();


// body parser middleware
app.use(express.json());
app.use(express.urlencoded( { extended: false } )); // this is to handle URL encoded data
// end parser middleware


// custom middleware to enable CORS
const cors = function(request, response, next) {
    if(request.url.substring(0, 5).toLowerCase() == "/api/") { // only enable CORS on the RESTful API
        response.header("Access-Control-Allow-Origin", "*"); // CORS HEADER
        response.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // CORS HEADER
    }
    next();
};
app.use(cors);
// end custom middleware


// this array functions as a database
const users = [ { id: 1, username: "admin", deleted: false } ];


// enable static files pointing to the folder "public"
// this can be used to serve html, css and client-side js files, for example
app.use(express.static(path.join(__dirname, "public")));


// HTTP GET
// get all users that are not deleted
app.get("/api/users", function(request, response) {
    response.json(users.map((element) => {
        if(!element.deleted) {
            return { id: element.id, username: element.username };
        }
    }));
});


// HTTP GET
// returns a user with the matching ID
app.get("/api/users/:id", function(request, response) {
    const user = users.find(u => u.id === parseInt(request.params.id) && u.deleted === false);
    if(!user) // return 404 Object Not Found
        return response.status(404).json( { message: `id ${request.params.id} not found`} );
    response.json( { id: user.id, username: user.username } );
});


// HTTP POST
// create a new user (this code does NOT check for duplicate usernames)
app.post("/api/users", function(request, response) {
    const newUser = {
        id: users.length + 1,
        username: request.body.username,
        deleted: false
    };
    if(!newUser.username)
        return response.status(400).json( { message: "\"username\" required" } );
    users.push(newUser);
    response.json( { id: newUser.id, username: newUser.username } ); // send back the new user
});


// HTTP PUT
// update user
app.put("/api/users/:id", function(request, response) {
    const user = users.find(u => u.id === parseInt(request.params.id) && u.deleted === false);
    if(!user) // return 404 Object Not Found
        return response.status(404).json( { message: `id ${request.params.id} not found`} );
    user.username = request.body.username ? request.body.username : user.username;
    response.json( { id: user.id, username: user.username } ); // send back the updated user
});


// HTTP DELETE
// delete user
app.delete("/api/users/:id", function(request, response) {
    const user = users.find(u => u.id === parseInt(request.params.id) && u.deleted === false);
    if(!user) // return 404 Object Not Found
        return response.status(404).json( { message: `id ${request.params.id} not found`} );
    user.deleted = true;
    response.json( { message: `id ${request.params.id} deleted` } );
});


// set port from environment variable, or 8000
const PORT = process.env.PORT || 8000;

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

Testing CORS Endpoints

Run the server and try accessing the method(s) that have CORS enabled. The following code can be used to access an API endpoint.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>JSON-MAN</title>
    <style>
        * {
            font-family: sans-serif;
            font-size: 30px;
            color: blue;
        }

        body {
            padding: 3%;
        }

        input, table, td:last-of-type {
            width: 100%;
        }

        textarea {
            width: 100%;
            height: 300px;
        }
    </style>
    <script type="text/javascript">
/*
request = {
	verb: "GET POST PUT PATCH DELETE",
	path: "/api/",
	headers: {"header1":"value1","header2":"value2"},
	data: "{'is':'json'}",
	onprogress: function(percent){}
};
*/
function ajax2(request) {
	var obj = "object";
	if (typeof request != obj) { request = {}; }
	var undef = "undefined";
	var canPromise = (typeof Promise != undef);
	var xmlobj;
	if (typeof XMLHttpRequest != undef) {
		xmlobj = new XMLHttpRequest();
	}
	else if (typeof window.ActiveXObject != undef) {
		var aVersions = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"];
		for (var i = 0; i < aVersions.length; i++) {
			try {
				xmlobj = new ActiveXObject(aVersions[i]);
				break;
			} catch (err) {
				//void
			}
		}
	}
	if (typeof xmlobj != obj) {
		return {then:function(){return{catch:function(ca){ca("XMLHttpRequest object could not be created");}}}};
	}
	if(typeof request.onprogress == "function" && typeof xmlobj.upload == obj) {
		xmlobj.upload.addEventListener("progress", function (event) {
			request.onprogress(Math.floor(event.loaded / event.total * 100));
		});
	}
	// if no verb is specified then use "get"; if no path is specified then use the current file
	xmlobj.open(request.verb || "get", request.path || location.pathname, canPromise);
	xmlobj.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
	if(typeof request.headers == obj) {
		for(var prop in request.headers) {
			xmlobj.setRequestHeader(prop, request.headers[prop]);
		}
	}
	xmlobj.send(request.data || null);
	if(canPromise) {
		return new Promise(function (resolve, reject) {
			xmlobj.onreadystatechange = function () {
				if (xmlobj.readyState == 4) {
					if (xmlobj.status >= 200 && xmlobj.status < 300) {
						resolve(xmlobj.responseText);
					}
					else {
						reject(xmlobj.responseText);
					}
				}
			};
		});
	}
	else {
		if (xmlobj.status >= 200 && xmlobj.status < 300) {
			return {then:function(th){th(xmlobj.responseText);return{catch:function(){}}}};
		}
		else {
			return {then:function(){return{catch:function(ca){ca(xmlobj.responseText);}}}};
		}
	}
}
var headersobj = null;
function setHeadersColor(input) {
	try {
		headersobj = JSON.parse(input.value);
		if (Array.isArray(headersobj)) {
			headersobj = null;
			input.style.color = "red";
		}
		else {
			input.style.color = "#0b0";
		}
	}
	catch {
		headersobj = null;
		input.style.color = "red";
	}
}
function setBodyColor(input) {
	try {
		JSON.parse(input.value);
		input.style.color = "#0b0";
	}
	catch {
		input.style.color = "red";
	}
}
function submitRequestForm(form) {
	ajax2({
		verb: form.requestmethod.value,
		path: form.endpoint.value,
		headers: headersobj,
		data: form.requestbody.value
	}).then(function (txt) {
		form.responsebody.value = txt;
		return false;
	}).catch(function (err) {
		alert("ERROR");
		form.reset();
		return false;
	});
	return false;
}
    </script>
</head>
<body>
    <h1>JSON-MAN</h1>
    <form method="get" action="" onsubmit="return submitRequestForm(this);">
        <div>
            <table><tr><td><select name="requestmethod"><option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option><option>DELETE</option></select></td><td><input type="text" name="endpoint" placeholder="ENDPOINT" /></td></tr></table>
        </div>
        <div>
            <input type="text" name="headers" placeholder='HEADERS EXAMPLE: {"header1":"value1","header2":"value2"}' onchange="setHeadersColor(this);" onkeyup="setHeadersColor(this);" autocomplete="off" />
        </div>
        <div>
            <textarea name="requestbody" placeholder="REQUEST BODY" onchange="setBodyColor(this);" onkeyup="setBodyColor(this);"></textarea>
        </div>
        <div>
            <textarea name="responsebody" placeholder="RESPONSE BODY" readonly></textarea>
        </div>
        <div>
            <button type="submit">submit</button>
        </div>
    </form>
</body>
</html>

Coding Video

https://youtu.be/3DfVHGrCIHA