Imagine you've just built a great API using Node.js, Express, and TypeScript. You've also built a client-side web app and are ready to fire it up in your browser, get it talking to your server, and hopefully share it with the world.
You open up a browser window and navigate to where your app is running, then you pop open the console in your developer tools. Your app makes its first API call to your server...but then, instead of the data you're expecting to see populate your app, you see an error like the one below in your console:
Access to fetch at 'http://localhost:5000/rooms' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Yikes! This error is due to Cross-Origin Resource Sharing (CORS) restrictions. While this may be frustrating, it's a situation many a developer has found themselves in.
But what is CORS? And how can you get around this error message and get the data from your server into your app? This tutorial will show you how to add CORS support to your Express + TypeScript API so you can keep moving forward with your exciting projects.
What is Cross-Origin Resource Sharing (CORS)?
Cross-Origin Resource Sharing (CORS) is a security protocol in modern browsers that allows or restricts resources from being shared between different origins, depending on which origin initiated the HTTP request.
An origin describes where a request is initiated. Origins have two required parts (scheme and hostname) and one optional part (port), as shown below:
https://www.twilio.com ^ ^ | | scheme hostname http://localhost:5000 ^ ^ ^ | | | scheme hostname port
The browser adds an Origin header to all of the requests it makes. When a request arrives at the server, if the origin in the request is included in the list of origins that are allowed to retrieve resources from that server, the server will add an Access-Control-Allow-Origin
header to its response to let the browser know that the content is accessible to this specific origin.
For example, the following shows that all origins are allowed to request resources:
Access-Control-Allow-Origin : *
However, the header below tells the browser that only requests from https://www.twilio.com
are allowed resources:
Access-Control-Allow-Origin : https://www.twilio.com
You are likely to be using different ports during your local development work. If you take a look at the example error message from the introduction of this post, you can see that in this example, the app is being served at localhost:3000
but is trying to fetch data from localhost:5000
. Because the two ports are different, this means they are from different origins, so by default, the browser will deny this request:
Access to fetch at 'http://localhost:5000/rooms' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
To fix this and to allow data to flow between your server and client, you can add CORS support to your server. For this tutorial, you'll use the cors
npm package to add middleware that will set the Access-Control-Allow-Origin
header and specify the domains that are allowed to access your server's resources.
If you want to read even more about CORS, check out the Mozilla documentation about this subject here.
Configure CORS in your Express server
To get started, you will need:
- Node.js (version 14.16.1 or higher) and npm installed on your machine.
- An Express project, such as the video API on the getting-started branch of this repository.
The CORS information in this tutorial can be used for any Express project. However, for the purposes of this example, you may want to follow along with the code in the Express project listed above. This video API is built with Express and TypeScript, so it makes for a great sample project to work with.
If you're following along with the example project, follow the instructions in the repository's README.md to get up and running.
Once you've run the command npm run start
, you should see a log statement in your terminal that lets you know that your server is running on port 5000:
Express server listening on port 5000
If instead, you're using your own Express API, that's great too! Your cURL requests will likely be different, so substitute in the parameters that make sense for your project.
Run a simulation with a cURL command
If you're using the example code from the repository above, open a second terminal window and try running the following cURL command:
curl -H "Origin: http://localhost:3000" --head http://localhost:5000/rooms
With this cURL command, you are simulating how the browser issues a request. In this case, your server is running on http://localhost:5000
and your app is running on and making a request from the origin localhost:3000
. In this example, your “app” is trying to request a list of video chat rooms.
The response that appears in your terminal window will be similar to the one below:
HTTP/1.1 200 OK X-Powered-By: Express Vary: Origin Content-Type: application/json; charset=utf-8 Content-Length: 52 ETag: <ETag> Date: Tue, 30 Mar 2021 16:45:54 GMT Connection: keep-alive Keep-Alive: timeout=5
If you examine this output, you'll notice that there is no Access-Control-Allow-Origin
header in this response. If this were actually a response received by a browser, the browser would block the request.
It's time to update your server so that your server responds with the Access-Control-Allow-Origin
header for specific cross-origin requests.
Add the cors
npm package to your Express project
In your terminal window, navigate to the root of your project. If you're following along with the sample code, the root of the project is the express-video-api directory.
Install the cors
package and its TypeScript types by running the following commands:
npm install cors npm install --save-dev @types/cors
If you take a look at the package.json file, you will find that cors
was added as a dependency
and @types/cors
was added to devDependencies
.
Configure CORS options
Next, open the file that is the entry point to your app. If you're following with the sample code, this file is src/index.ts.
In your code editor, and below the line where you've imported express
, import cors
as well:
Add the following line above app.use(express.json());
to allow your Express server to use the cors
middleware:
app.use(cors); /* NEW */ app.use(express.json());
Then add a list of the origins you want to allow to access resources on your server, and pass this list into your CORS options. For this tutorial, you'll add localhost:3000
as the origin you want to allow:
// Add a list of allowed origins. // If you have more origins you would like to add, you can add them to the array below. const allowedOrigins = ['http://localhost:3000']; const options: cors.CorsOptions = { origin: allowedOrigins };
Next, pass these options in as an argument for your cors
middleware:
Your Express server will notice the changes and update.
At this point, if you are following along with the example project, your code should look like the code below:
import express from 'express'; import cors from 'cors'; import config from './config'; import roomsRouter from './routes/room'; import { Twilio } from 'twilio'; // Initialize Twilio client const getTwilioClient = () => { if (!config.TWILIO_ACCOUNT_SID || !config.TWILIO_API_KEY || !config.TWILIO_API_SECRET) { throw new Error(`Unable to initialize Twilio client`); } return new Twilio(config.TWILIO_API_KEY, config.TWILIO_API_SECRET, { accountSid: config.TWILIO_ACCOUNT_SID }) } export const twilioClient = getTwilioClient(); const app = express(); // Add a list of allowed origins. // If you have more origins you would like to add, you can add them to the array below. const allowedOrigins = ['http://localhost:3000']; const options: cors.CorsOptions = { origin: allowedOrigins }; // Then pass these options to cors: app.use(cors(options)); app.use(express.json()); // Forward requests for the /rooms URI to our rooms router app.use('/rooms', roomsRouter); app.listen(5000, () => { console.log('Express server listening on port 5000'); });
Test that CORS is working
Now that you have configured CORS options on your server, try running the following cURL command simulation in your terminal again:
curl -H "Origin: http://localhost:3000" --head http://localhost:5000/rooms
If you check the response, you'll notice that there is now an Access-Control-Allow-Origin
header with the origin http:://localhost:3000
! This means that when you run your client-side app on localhost:3000
, the app will be able to retrieve resources from your server.
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: http://localhost:3000 Vary: Origin Content-Type: application/json; charset=utf-8 Content-Length: 52 ETag: <ETag> Date: Tue, 30 Mar 2021 16:46:39 GMT Connection: keep-alive Keep-Alive: timeout=5
As another test, try running the following cURL command as well:
curl -H "Origin: http://localhost:4000" --head http://localhost:5000/rooms
Notice that in this command, you've changed the origin in your request to http://localhost:4000
, which was not in your list of allowed origins. The response that comes back does not have the Access-Control-Allow-Origin
header:
HTTP/1.1 200 OK X-Powered-By: Express Vary: Origin Content-Type: application/json; charset=utf-8 Content-Length: 52 ETag: <ETag> Date: Tue, 30 Mar 2021 16:40:47 GMT Connection: keep-alive Keep-Alive: timeout=5
This means that if someone tries to access your server from an app running on localhost:4000
, they will not be able to access resources. Instead, a similar CORS error as before will appear in their console.
What's next for your Express API project?
If you want to take a look at the updated sample code from this tutorial in its entirety, you can find that code on the added-cors branch of this repository on GitHub. Or, if you just want a cheat sheet of how to implement CORS in your own app, check out the gist here.
Now that you know how to add CORS support to your Express + TypeScript server, you're all set to connect your server to a client-side application. What kind of app do you have in mind? I can't wait to see what you build.
Mia Adjei is a Software Developer on the Developer Voices team. They love to help developers build out new project ideas and discover aha moments. Mia can be reached at madjei [at] twilio.com.