I had a Go, CORS and Single Page App Ordeal So You Don’t Have To
Microservices are awesome, until you need to serve them up in a Single Page Application (SPA). That’s when you need to tame the CORS (cross-origin resource sharing) beast so your fancy new microservices can actually be used by front-end developers.
Don’t Have a CORS Ordeal
I just spent days on this one, so I thought I’d relay how my final solution works. In my case, I have a swagger-first approach to API development, where I feed a YAML file into go-swagger
and then go-swagger
generates a REST server framework. This works great with lots of microservices that don’t have any problem communicating using HTTP.
The problem we encountered was using Angular to make a single-page web application that would then use the APIs directly in the browser. That’s when CORS comes into play. I couldn’t get the headers to work right in Go using the go-swagger
framework.

Making CORS Work!
I have a swagger-first, also known as an OpenAPI standards, approach to API development. This is where I feed a YAML file into and then go-swagger
generates a REST server framework. This works great with lots of microservices that don’t have any problem communicating using HTTP.
I could access my microservices externally on a server-to-server basis using api-umbrella
as a API gateway. I even think I may have been able to solve my CORS problem with api-umbrella
configuration, but I didn’t see it.
I had this problem with Angular, but anyone can have a CORS ordeal when you connect any new REST service to a JavaScript SPA.
My Toolchain
We all get attached to the chain of events that goes from a source code change to finished deployments. I needed my solution to work with a simple API gateway, like api-umbrella
. Fancy solutions like AWS API Gateway don’t work for me due to lock-in and cost issues.
In a nutshell, here are the steps I use to build a microservice:
- Hand-write the swagger (OpenAPI v2) file in YAML
- Use go-swagger to generate the REST server
- Update the code to implement handler functions
- Test in VS Code
- Use Drone CI/CD to generate Docker images in a private registry
- Use docker-compose to orchestrate service startup on a private host, in a private VPC, and in a private datacenter
- Deploy
api-umbrella
as a the API gateway between public URI’s and the backend host
The Journey To Victory
After quite a journey, I finally came upon the solution at the swagger docs. The real trick in taming CORS, though, is to handle the “pre-flight” checks a web client makes before the actual REST method is invoked.
The solution for us is to create an OPTIONS
method and write the response methods for them in Go using the go-swagger
framework.

Make a swagger YAML file
This sample OpenAPI (swagger) YAML file is a very simple representation of a single endpoint REST server with two methods.
To get acquainted with this swagger definition, please note that I’ve used references rather heavily throughout the file. For example, if you look at the GET /coordinate
path definition, you’ll see a reference to #/responses/CoordinateResponse
.
Look in the responses section of the file, and you’ll see where CoordinateResponse
includes both a JSON response body and header definitions.
Another handy definition in the responses section of the swagger file is CORSResponse
, which is used to define the OPTIONS /coordinate
response.
In my go-swagger
environment, this YAML file generates the core HTTP services and frameworks for servicing inbound requests.
Notice how GET /coordinates
has an authentication specification and OPTIONS /coordinates
has no authentication. I need this because when Angular (or any SPA) causes the web browser to make an outbound call to a CORS-compliant REST server that is not in current origin, the browser expects to receive CORS OPTIONS response without any authentication.
By the way, you might be able to get around a CORS issue as an API consumer by including a local API proxy that doesn’t need to use CORS when make a server-to-server HTTP call. But, this technique requires that the API consumer include a proxy in their front-end code.
I also used a separate “cors” tag which helped organize and separate my “preflight” CORS options.
Next, I needed to write a preflight CORS handler function in the Go server. That is done by creating Go functions that conform to the go-swagger function conventions, draw from the OpenAPI (swagger) file.
Add CORS Header to Secure Response
Almost done. Next I need to modify my GetCoordinate
handler to add the WithAccessControlAllowHeaders
modifier.
Thank You!

I hope you liked my “little” story on how I solved my CORS problem.
I’m sure I didn’t get it right, so let me know how I could have done it better!