CORS 'R' US: CouchDB & Nginx teamwork

Many years after CouchDB debuted, we still see developer after developer—manager after manager—bypass CouchDB only to rebuild exactly what CouchDB offers using a collection of other technologies. As a Netflix employee I’m well aware of the diverse set of needs that can lead to an number of different combinations of datastores and web applications fronting them. However, the majority (a deafening majority in fact) of applications need only a somewhat performant storage mechanism paired with a JSON-HTTP transport mechanism. Why use PostgreSQL w/ an ORM and Spring to achieve the exact same thing CouchDB does by itself? I don’t know if I’ll ever understand why so many teams made the wrong decision there.

The good news is that we don’t need to be one of those teams. You can get on the rapid development train with CouchDB and even take it up a notch by letting Nginx take care of a couple little things that you might be tempted to use Node.js for—but really do not need to. For now I’ll leave you with an Nginx site config that will allow you to use cross-origin resource sharing w/ CouchDB, effectively eliminating the need for any database server programming. Without further ado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
# http://wiki.apache.org/couchdb/Nginx_As_a_Reverse_Proxy
location / {
# https://michielkalkman.com/snippets/nginx-cors-open-configuration.html
if ($request_method = 'OPTIONS') {
#
# Om nom nom cookies
#
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'PUT') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
proxy_pass http://localhost:5984;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/(.*)/_changes {
proxy_pass http://localhost:5984;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}