Introduction to CouchDB with .NET part 3: starting with the CouchDB HTTP API
May 19, 2017 Leave a comment
Introduction
In the previous post we looked at the CouchDB administrative UI called Fauxton. The UI that shipped with earlier versions of CouchDB is called Futon so you will still come across that name if you work with older CouchDB projects. Fauxton is quite easy and straightforward to use. The database section allows us to create, update and delete databases and documents. Many functions are meant to be handled by database admins such as CouchDB users, access rights, processes and configuration.
In this post we’ll start exploring the CouchDB HTTP-based API.
The CouchDB HTTP API
We saw that the Fauxton UI starts on the localhost endpoint http://localhost:5984/_utils/ . It responds with a HTML page. However, that URL is also the gateway into a HTTP-based API which communicates heavily with JSON web requests and responses. The general rule is that the URL includes the resource on which we want to execute some operation. The type of operation is governed by the HTTP verb such as GET, POST, PUT etc. We’re also expecting a web response from a web request. Responses will come in the form of HTTP status codes such as 200 OK, 201 Created and 500 Internal Server Error. The web responses can often also include a body with a message in the form of a JSON or XML string. This is nothing special, that’s how HTTP-based API’s are supposed to work and the CouchDB API follows these rules. We can get a couple of hints about the API endpoints from Fauxton actually. The UI header includes a button called API which opens a small dialog with a URL. Here’s an example from the Databases section:
That API endpoint returns all databases of course. Copy the URL and open it in a web browser. You should get a JSON response similar to the following:
["_global_changes","_replicator","_users","customers"]
…i.e. an array of strings with the database names.
The following example appears after selecting a specific database:
The _all_docs endpoint of a database returns the same basic set of properties of all documents that we see in the UI:
{"total_rows":2,"offset":0,"rows":[ {"id":"0c458e911e3ff5b4e423bfe06c00133c","key":"0c458e911e3ff5b4e423bfe06c00133c","value":{"rev":"5-db3fe5937ed8ee9de36aaddc8ab3647e"}}, {"id":"77802a7eaffab7458a00f1f6cf00027c","key":"77802a7eaffab7458a00f1f6cf00027c","value":{"rev":"2-303ba1f5ada852ba09de8a6f6d4bed2e"}} ]}
Check the Include Docs option in the UI and open the API dialog again. The URL will be updated to the following:
http://localhost:5984/customers/_all_docs?include_docs=true&conflicts=true
…which returns a more complete set of properties from each document:
{ "total_rows":2, "offset":0, "rows":[ { "id":"0c458e911e3ff5b4e423bfe06c00133c", "key":"0c458e911e3ff5b4e423bfe06c00133c", "value":{ "rev":"5-db3fe5937ed8ee9de36aaddc8ab3647e" }, "doc":{ "_id":"0c458e911e3ff5b4e423bfe06c00133c", "_rev":"5-db3fe5937ed8ee9de36aaddc8ab3647e", "name":"New customer Inc", "preferred":false, "address":{ "street":"New street", "city":"Birmingham", "country":"UK", "postal_code":22222 }, "telephone":{ "office":"34543534", "sales":"576545645", "customer_care":"456456567" }, "year_joined":2017, "current_status":"approved", "_attachments":{ "Administrative databases created on single node CouchDB installation.PNG":{ "content_type":"image/png", "revpos":4, "digest":"md5-zHrwohh+oRdL29TExyrhrA==", "length":33926, "stub":true } } } }, { "id":"77802a7eaffab7458a00f1f6cf00027c", "key":"77802a7eaffab7458a00f1f6cf00027c", "value":{ "rev":"2-303ba1f5ada852ba09de8a6f6d4bed2e" }, "doc":{ "_id":"77802a7eaffab7458a00f1f6cf00027c", "_rev":"2-303ba1f5ada852ba09de8a6f6d4bed2e", "name":"Great customer Inc", "preferred":true, "address":{ "street":"Victory avenue", "city":"New York", "country":"USA", "postal_code":11111 }, "telephone":{ "office":"34543534", "sales":"576545645", "customer_care":"456456567" }, "year_joined":2015 } } ] }
We can get some basic information about a database with the /[database_name] endpoint like http://localhost:5984/customers:
{ "db_name":"customers", "update_seq":"9-g1AAAAFTeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rkQGPoiQFIJlkT1idA0hdPFgdKz51CSB19WB1THjU5bEASYYGIAVUOh-_3RC1CyBq9xOj9gBE7X1i1D6AqAW5NwsA2g1vNw", "sizes":{ "file":103698, "external":34877, "active":37595 }, "purge_seq":0, "other":{ "data_size":34877 }, "doc_del_count":1, "doc_count":2, "disk_size":103698, "disk_format_version":6, "data_size":37595, "compact_running":false, "instance_start_time":"0" }
If we try the same with a non-existent database then we get 404 Not Found with the following response body:
{ "error": "not_found", "reason": "Database does not exist." }
We can get a specific document from a database by attaching an ID to the request, e.g. http://localhost:5984/customers/0c458e911e3ff5b4e423bfe06c00133c :
{ "_id": "0c458e911e3ff5b4e423bfe06c00133c", "_rev": "5-db3fe5937ed8ee9de36aaddc8ab3647e", "name": "New customer Inc", "preferred": false, "address": { "street": "New street", "city": "Birmingham", "country": "UK", "postal_code": 22222 }, "telephone": { "office": "34543534", "sales": "576545645", "customer_care": "456456567" }, "year_joined": 2017, "current_status": "approved", "_attachments": { "Administrative databases created on single node CouchDB installation.PNG": { "content_type": "image/png", "revpos": 4, "digest": "md5-zHrwohh+oRdL29TExyrhrA==", "length": 33926, "stub": true } } }
We can extend the above result to see the available revisions like this:
http://localhost:5984/customers/0c458e911e3ff5b4e423bfe06c00133c?revs_info=true
The revision information is presented in an array like this:
"_revs_info": [ { "rev": "5-db3fe5937ed8ee9de36aaddc8ab3647e", "status": "available" }, { "rev": "4-5edf1bd512b5cacb447e944c7305c3e1", "status": "available" }, { "rev": "3-4eb766f3e74f5ae77a7d0d8c1797a5e8", "status": "available" }, { "rev": "2-51be660bcbc93e6eec7b1d6f9b46afd0", "status": "deleted" }, { "rev": "1-12a90088a7b760c892bca04efa017b94", "status": "available" } ]
We can then use these revision IDs to retrieve a specific revision, e.g. http://localhost:5984/customers/0c458e911e3ff5b4e423bfe06c00133c?rev=3-4eb766f3e74f5ae77a7d0d8c1797a5e8.
These are all examples of GET requests for various resources in CouchDB and the UI is also dependent on these requests. One way of finding a HTTP API endpoint is Fauxton as we’ve just seen it. There’s also a reference manual available here. This Wiki page is also a good resource. GET requests that don’t require any authentication can be directly tested in a web browser. Let’s try some of them:
- http://localhost:5984/_active_tasks: returns the array of active tasks which currently resolves to an empty array in our case
- http://localhost:5984/_node/couchdb@localhost/_config: returns the complete list of settings of the CouchDB server, i.e. what’s visible in the Configuration section of Fauxton. We can extract a specific section by attaching the section name to the URL such as http://localhost:5984/_node/couchdb@localhost/_config/chttpd. We can also read a specific setting value by including the property name, e.g.
http://localhost:5984/_node/couchdb@localhost/_config/chttpd/bind_address - http://localhost:5984/_node/couchdb@localhost/_stats: returns a large number of database statistics
- http://localhost:5984/_uuids: generate one ore more random UUIDs from CouchDB. This is useful when inserting documents via HTTP and you want to get a new globally unique ID. This URL accepts the count query parameter to generate more than one UUID, e.g. http://localhost:5984/_uuids?count=10
Here’s an example of the _uuids response:
{ "uuids":[ "3559d9c81c785b6bfc27a34904001cf9", "3559d9c81c785b6bfc27a349040028e9", "3559d9c81c785b6bfc27a34904002b66", "3559d9c81c785b6bfc27a349040033aa", "3559d9c81c785b6bfc27a34904004181", "3559d9c81c785b6bfc27a34904004287", "3559d9c81c785b6bfc27a349040047a9", "3559d9c81c785b6bfc27a349040047ca", "3559d9c81c785b6bfc27a349040048ab", "3559d9c81c785b6bfc27a34904004cab" ] }
For other types of requests, like PUT or POST it’s best to use a HTTP client like Postman or Fiddler. I’ll use Postman to test various HTTP requests against the CouchDB server. However, it’s really up to you to pick your favourite tool. This is what executing the UUID generation endpoint in Postman looks like:
Let’s now try some of the non-GET requests. We’ll first try and add a new configuration value:
PUT http://localhost:5984/_node/couchdb@localhost/_config/custom_category/custom_key
The URL means that we want to set the value of key “custom_key” in the category “custom_category”. The value for the key is provided in the body as a valid JSON argument. Strings must be passed within quotations:
PUT in CouchDB generally means that the resource should be updated if it already exists or inserted if it doesn’t. In the above case there’s no such category or key so it will be inserted. We can check it in Fauxton:
The API responds with the previous value of the setting which is an empty string in the above example since the config resource didn’t exist before. Let’s see if we can update the custom configuration value:
Update the configuration screen in Fauxton and you’ll see the change. The API responds with “custom value” since that was the value of this configuration key before it was updated. Let’s now delete this dummy configuration:
The configuration is now gone.
We can check if a database exists with the following request: HEAD http://localhost:5984/customers , i.e. the database name is simply attached to the domain. CouchDB responds with 200 OK if the database exists or 404 Not Found if it doesn’t like in the case of HEAD http://localhost:5984/products .
We’ll continue in the next post with the HTTP API. We’ll concentrate more on the DB and document related operations.
You can view all posts related to data storage on this blog here.