November 22, 2017

How to enable CORS for Express-GraphQL & Apollo Server

Learn how to configure cross-origin resource sharing on your express-based GraphQL servers.

What is CORS?

Cross-origin resource sharing, short CORS, is a protection mechanism for web pages, allowing the browser to safely load resources from domains that are different from the one it originally loaded the page from.

As an example, say you’re accessing https://www.prisma.io/ in your browser. The browser downloads the HTML and JavaScript for the site in order to render it for you. Now, some data to be displayed on the site actually is stored somewhere else, on a different server — a different origin.

Two URLs are said to have the same origin if the following three properties are identical for them: domain, protocol and port: http://localhost:3000, ws://localhost:3000 and http://localhost:4000 all have different origins. http://localhost:3000 and http://localhost:3000/graphql have the same origin.

In order for your browser to load the data from that other server, the other server needs to set Access-Control headers properly in order to determine its policy regarding cross-origin resource access. For example, by simply specifying Access-Control-Allow-Origin: *, the server indicates to the browser that it will allow CORS anywhere.

How about an example

Now, imagine the following scenario. You’re starting out with a new project and for now are only developing locally on your machine. You used create-react-app to bootstrap your frontend and for the backend you setup a simple express.js server (either based on express-graphql, graphql-yoga or apollo-server).

In fact, we prepared an example that mimics this exact scenario:

Server

const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const { graphqlExpress, graphiqlExpress } = require('apollo-server-express')
const { makeExecutableSchema } = require('graphql-tools')

const typeDefs = `
type Query {
  hello(name: String): String!
}
`

const resolvers = {
  Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
  },
}

const myGraphQLSchema = makeExecutableSchema({ typeDefs, resolvers })
const PORT = 4000
const app = express()

// app.use(cors()) // not having cors enabled will cause an access control error
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema }))
app.get('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))

console.log(`Server listening on http://localhost:${PORT} ...`)
app.listen(PORT)

Notice that line 23 is commented out — CORS is not enabled!

Frontend

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import { ApolloProvider } from 'react-apollo'
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-client-preset'

const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' })

const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache()
})

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>, 
  document.getElementById('root')
)
registerServiceWorker()

Standard setup for using Apollo Client 2.0

import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'

class App extends Component {

  render() {
    if (this.props.data.loading) {
      return <div>Loading</div>
    }
    return (
      <div className="App">
        <header className="App-header">
          <h1 className="App-title">{this.props.data.hello}</h1>
        </header>
      </div>
    )
  }
}

export default graphql(gql`{hello}`)(App)

The App component sends a simple hello query using Apollo Client

In your local development setup, where the React app is loaded from http://localhost:3000 and the GraphQL server is serving at http://localhost:4000/graphql, you’ll now get an access control error if you’re trying to run the app:

What exactly is the issue when CORS is not enabled? Well, CORS is in fact a specification for a communication flow between client (a browser) and server. In some situations, this flow requires the server to process HTTP OPTIONS requests as can be seen from this flowchart:

The CORS flow might required additional HTTP requests with the [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) method ([source](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing#/media/File:Flowchart_showing_Simple_and_Preflight_XHR.svg))
The CORS flow might required additional HTTP requests with the [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS) method ([source](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing#/media/File:Flowchart_showing_Simple_and_Preflight_XHR.svg))
The CORS flow might required additional HTTP requests with the OPTIONS method (source)

The problem is that neither express-graphql nor apollo-server accept HTTP requests other than GET and POST — which is why the request fails in our scenario. This is also indicated by the error message we saw in the console: OPTIONS [http://localhost:4000](http://localhost:4000) 405 (Method not allowed).

Here is the GitHub discussion on express-graphql where this issue first came up.

Luckily, the solution is very simple. As express-graphql and apollo-server are both based on express.js, you can simply use its standard cors middleware to fix the issue.

Uncommenting line 23 in server.js will enable the cors middleware for your express server: app.use(cors()). Having the middleware enabled ensures your express server sets the proper HTTP header, enabling your React app to load data from it:

Using the [cors](https://github.com/expressjs/cors) middleware, the server sets the correct HTTP header enabling cross-origin resource sharing
Using the [cors](https://github.com/expressjs/cors) middleware, the server sets the correct HTTP header enabling cross-origin resource sharing
Using the cors middleware, the server sets the correct HTTP header enabling cross-origin resource sharing

Summary

CORS is an important protection mechanism preventing websites from downloading malicious resources, but from a developer standpoint it can be a pain to properly configure it.

When using express-graphql and apollo-server, all you need to do is include the standard cors middleware used in express.js apps and you’re good to go:

// other imports ...
const cors = require('cors')

const app = express()

app.use(cors()) // enable `cors` to set HTTP response header: Access-Control-Allow-Origin: *
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }))

app.listen(PORT)

Comments

Comments

Don’t miss the next post!