Skip to end of metadata
Go to start of metadata

This document describes the REST API exposed to access Couchbase
resources

What it doesn't describe:

  • auth scheme, on this step you might only protect your API endpoint using nginx built-in means
  • expiration handling. in Couchbase database it is possible to associate with the key expiration time. This feature will be exposed later

There are several terms we are using while describing this API.

Term Definition
document Arbitrary JSON or BLOB value. It could be referenced by its ID
bucket The logical storage, which contains the documents. Like database in SQL world, or collection in mongodb

Lets assume we have a bucket named wonderland.

Document CRUD

This section shows how to perform simple Create, Retrieve, Update and Delete operations

Create document in the bucket

POST /wonderland
> Content-Type: application/json
{"name":"Alice","species":"human"}
< 201
< ETag: "f5b8da2cf1560000"
< Location: http://couchbasenginx.apiary.io/wonderland/0a4683e4b4b161f505d0c2ae0c3a1abf

You can also specify ID with POST request if you need to create the document with given ID.

POST /wonderland/alice
> Content-Type: application/json
{"name":"Alice","species":"human"}
< 201
< ETag: "f5b8da2cf1570000"
< Location: http://couchbasenginx.apiary.io/wonderland/alice

Note that when you are using POST request, the server is always supposed to create the new document. This means that you will get an error if the document already exists.

POST /wonderland/foobar
> Content-Type: application/json
{"foo":"bar"}
< 409
< Content-Type: application/json
< Content-Length: 74
{"error":"key_eexists","reason":"Key exists (with a different ETag value)"}

Update the document

PUT /wonderland/white-rabbit
> Content-Type: application/json
{"name":"Write Rabbit","species":"rabbit"}
< 204
< ETag: "3d24e52cf1570000"
< Location: http://couchbasenginx.apiary.io/wonderland/white-rabbit

You can also specify If-Match header and the server will perform optimistic lock before updating. This operation might fail 412 Precondition Failed

PUT /wonderland/duchess
> Content-Type: application/json
> If-Match: "wrong_etag"
{"name":"Duchess","species":"human"}
< 412
< Content-Type: application/json
< Content-Length: 74
{"error":"key_eexists","reason":"Key exists (with a different ETag value)"}

If the document being updated doesn't exist, you will get 404 Not Found error

PUT /wonderland/sheep
{"name":"Sheep","species":"sheep"}
< 404
< Content-Type: application/json
< Content-Length: 45
{"error":"key_enoent","reason":"No such key"}

Removing document from the bucket

DELETE /wonderland/caterpillar
< 204
< ETag: "7f463c2df1570000"

You can also specify If-Match header if you need to ensure you are removing know version of the document.

DELETE /wonderland/bill-the-lizard
> If-Match: "wrong_etag"
< 412
< Content-Type: application/json
< Content-Length: 74
{"error":"key_eexists","reason":"Key exists (with a different ETag value)"}

If the document being removed doesn't exist, you will get 404 Not Found error

DELETE /wonderland/jabberwock
< 404
< Content-Type: application/json
< Content-Length: 45
{"error":"key_enoent","reason":"No such key"}

Getting the document by ID.

GET /wonderland/hatter
< 200
< Content-Type: application/json
< Content-Length: 36
< ETag: "5d45c301fc570000"
{"name":"Hatter","species":"human"}

In case when the given document ID isn't exist in the bucket you will get 404 Not Found

GET /wonderland/red-queen
< 404
< Content-Type: application/json
< Content-Length: 45
{"error":"key_enoent","reason":"No such key"}

Use HEAD request to skip the document body

HEAD /wonderland/gryphon
< 200
< Content-Type: application/json
< ETag: "55c3a92df1570000"

Each operation is carry ETag, which internally is CAS value. The ETag value can be used in the conditional requests with If-Match, If-None-Match. See examples of If-Match above.

Check if the key has been modified (in this case the server responds that it wasn't modified):

HEAD /wonderland/dormouse
> If-None-Match: "3f5b792df1570000"
< 304

Couchbase Views

This section shows more advanced server feature: View indexes

One of the features of the Couchbase is ability to build efficient indexes leveraging Map/Reduce. They are called Views and you can define them on Admin Console UI.

This module allows you to query your views proxying them to Couchbase. All arguments will be transparently passed to Couchbase and the result will be streamed back.

For example we have a view all defined in the design document characters. It is simple map which will just emit all known characters (without any reduce function):

function (doc, meta) {
  emit(meta.id, null);
}

The result will look like (note: when id isn't accessible like for reduced views, the meta will be missing too)

GET /wonderland/_design/characters/_view/all
< 200
< Transfer-Encoding: chunked
< Content-Type: application/json
{"total_rows":20,"rows":[
{"id":"alice","key":"alice","value":null,"meta":{"etag":"f5b8da2cf1570000"}},
{"id":"bill-the-lizard","key":"bill-the-lizard","value":null,"meta":{"etag":"accb302df1570000"}},
{"id":"caterpillar","key":"caterpillar","value":null,"meta":{"etag":"7f463c2df1570000"}},
{"id":"cheshire-cat","key":"cheshire-cat","value":null,"meta":{"etag":"5b99582df1570000"}},
{"id":"dodo","key":"dodo","value":null,"meta":{"etag":"2fe2ed2cf1570000"}},
{"id":"dormouse","key":"dormouse","value":null,"meta":{"etag":"3f5b792df1570000"}},
{"id":"duchess","key":"duchess","value":null,"meta":{"etag":"a8bc492df1570000"}},
{"id":"duck","key":"duck","value":null,"meta":{"etag":"e076132df1570000"}},
{"id":"eaglet","key":"eaglet","value":null,"meta":{"etag":"b9bd062df1570000"}},
{"id":"gryphon","key":"gryphon","value":null,"meta":{"etag":"55c3a92df1570000"}},
{"id":"hatter","key":"hatter","value":null,"meta":{"etag":"5d45c301fc570000"}},
{"id":"king-of-hearts","key":"king-of-hearts","value":null,"meta":{"etag":"4e1c9f2df1570000"}},
{"id":"knave-of-hearts","key":"knave-of-hearts","value":null,"meta":{"etag":"43fa922df1570000"}},
{"id":"lory","key":"lory","value":null,"meta":{"etag":"01f6fa2cf1570000"}},
{"id":"march-hare","key":"march-hare","value":null,"meta":{"etag":"08f6642df1570000"}},
{"id":"mock-turtle","key":"mock-turtle","value":null,"meta":{"etag":"11a8b52df1570000"}},
{"id":"mouse","key":"mouse","value":null,"meta":{"etag":"fa38ea2cf1570000"}},
{"id":"pat","key":"pat","value":null,"meta":{"etag":"8bd71f2df1570000"}},
{"id":"queen-of-hearts","key":"queen-of-hearts","value":null,"meta":{"etag":"f72c862df1570000"}},
{"id":"white-rabbit","key":"white-rabbit","value":null,"meta":{"etag":"3d24e52cf1570000"}},
]
}

You can pass the any of supported query parameters, like include_docs=true for example:

GET /wonderland/_design/characters/_view/all?include_docs=true
< 200
< Transfer-Encoding: chunked
< Content-Type: application/json
{"total_rows":20,"rows":[
{"id":"alice","key":"alice","value":null,"meta":{"etag":"f5b8da2cf1570000"},"json":{"name":"Alice","species":"human"}},
{"id":"bill-the-lizard","key":"bill-the-lizard","value":null,"meta":{"etag":"accb302df1570000"},"json":{"name":"Bill the Lizard","species":"lizard"}},
{"id":"caterpillar","key":"caterpillar","value":null,"meta":{"etag":"7f463c2df1570000"},"json":{"name":"Caterpillar","species":"caterpillar"}},
{"id":"cheshire-cat","key":"cheshire-cat","value":null,"meta":{"etag":"5b99582df1570000"},"json":{"name":"Cheshire Cat","species":"cat"}},
{"id":"dodo","key":"dodo","value":null,"meta":{"etag":"2fe2ed2cf1570000"},"json":{"name":"Dodo","species":"bird"}},
{"id":"dormouse","key":"dormouse","value":null,"meta":{"etag":"3f5b792df1570000"},"json":{"name":"Dormouse","species":"dormouse"}},
{"id":"duchess","key":"duchess","value":null,"meta":{"etag":"a8bc492df1570000"},"json":{"name":"Duchess","species":"human"}},
{"id":"duck","key":"duck","value":null,"meta":{"etag":"e076132df1570000"},"json":{"name":"Duck","species":"bird"}},
{"id":"eaglet","key":"eaglet","value":null,"meta":{"etag":"b9bd062df1570000"},"json":{"name":"Eaglet","species":"bird"}},
{"id":"gryphon","key":"gryphon","value":null,"meta":{"etag":"55c3a92df1570000"},"json":{"name":"Gryphon","species":"gryphon"}},
{"id":"hatter","key":"hatter","value":null,"meta":{"etag":"5d45c301fc570000"},"json":"foo"},
{"id":"king-of-hearts","key":"king-of-hearts","value":null,"meta":{"etag":"4e1c9f2df1570000"},"json":{"name":"King of Hearts","species":"card"}},
{"id":"knave-of-hearts","key":"knave-of-hearts","value":null,"meta":{"etag":"43fa922df1570000"},"json":{"name":"Knave of Hearts","species":"card"}},
{"id":"lory","key":"lory","value":null,"meta":{"etag":"01f6fa2cf1570000"},"json":{"name":"Lory","species":"bird"}},
{"id":"march-hare","key":"march-hare","value":null,"meta":{"etag":"08f6642df1570000"},"json":{"name":"March Hare","species":"hare"}},
{"id":"mock-turtle","key":"mock-turtle","value":null,"meta":{"etag":"11a8b52df1570000"},"json":{"name":"Mock Turtle","species":"turtle"}},
{"id":"mouse","key":"mouse","value":null,"meta":{"etag":"fa38ea2cf1570000"},"json":{"name":"Mouse","species":"mouse"}},
{"id":"pat","key":"pat","value":null,"meta":{"etag":"8bd71f2df1570000"},"json":{"name":"Pat","species":"pig"}},
{"id":"queen-of-hearts","key":"queen-of-hearts","value":null,"meta":{"etag":"f72c862df1570000"},"json":{"name":"Queen of Hearts","species":"card"}},
{"id":"white-rabbit","key":"white-rabbit","value":null,"meta":{"etag":"3d24e52cf1570000"},"json":{"name":"White Rabbit","species":"rabbit"}}
]
}

Misc

You can find executable spec here: http://docs.ngxcbm.apiary.io/

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Feb 26

    Matt Ingenthron says:

    Is CAS also valid for a DELETE? Also, it may be useful to say something about w...

    Is CAS also valid for a DELETE?

    Also, it may be useful to say something about whether or not results from Couchbase are cached. I think they are not, but I am not certain.

    1. Feb 26

      Sergey Avseyev says:

      The won't be cached, but does it worth to introduce, X-Couchbase-CAS and X-Couch...

      The won't be cached, but does it worth to introduce, X-Couchbase-CAS and X-Couchbase-TTL when we have HTTP abstractions like ETag (If-None-Match) and Expires. Probably we can find a way to reuse HTTP semantic?

    2. Feb 26

      Sergey Avseyev says:

      And yes, it is possible to specify CAS for delete command. And it will fail unle...

      And yes, it is possible to specify CAS for delete command. And it will fail unless it match to the current one

  2. Feb 26

    Sergey Avseyev says:

    Another note: I probably will "normalize" view output, because we anyway should ...

    Another note: I probably will "normalize" view output, because we anyway should intercept the include_docs=true parameter. I'm going to rename "CAS" to "etag" in meta field, and hide flags completely from the user

    1. Feb 26

      Matt Ingenthron says:

      I like the idea of allowing for include_docs, but perhaps in a later implementat...

      I like the idea of allowing for include_docs, but perhaps in a later implementation. We should probably just work out what our initial release criteria is.

  3. Feb 26

    Aleksey Kondratenko says:

    My team has some of the same challenges as part of document editing API. Here's ...

    My team has some of the same challenges as part of document editing API. Here's list of tricky things that has to be addressed:

    • dealing with arbitrary octet strings as keys
    • dealing with user-defined vbucket mapping. I think it implies that either header or query parameter or something specifying vbucket id has to be supported
    • dealing with separate namespaces of design docs and normal docs
    • dealing with separate namespaces of views and normal docs. I.e. view queries should not ever be confused with GETting docs and vice versa
    • dealing with _all_docs use case
    • dealing with non-json docs
    • dealing with doc's metadata (not just CAS, but flags, expiration etc)
    1. Feb 27

      Sergey Avseyev says:

      Thanks, Aleksey I will update spec with the answers

      Thanks, Aleksey I will update spec with the answers

    2. Feb 27

      Sergey Avseyev says:

      The short answers are (for this iteration at least) dealing with arbitrary oct...

      The short answers are (for this iteration at least)

      dealing with arbitrary octet strings as keys

      The key part in URI might be be urlencoded. I don't like idea with base64 with BIN_ prefix

      dealing with user-defined vbucket mapping. I think it implies that either header or query parameter or something specifying vbucket id has to be supported

      On this iteration I would rather skip this feature

      dealing with separate namespaces of design docs and normal docs

      dealing with separate namespaces of views and normal docs. I.e. view queries should not ever be confused with GETting docs and vice versa

      I will just forbid to create documents with _design/ prefix, It won't be possible to perform administrative task via nginx module, only data access, so you won't be able to create design document there.

      dealing with _all_docs use case

      Won't implement this feature

      dealing with non-json docs

      Currently I can use either flags or datatype field to mark key as json in case the use specify application/json content-type on store. and after that I can just render application/octet-stream for all other documents. There no other issues with storing blobs in couchbase. In views they are already base64encoded, but for retrieving data I can just stream the contents

      dealing with doc's metadata (not just CAS, but flags, expiration etc)

      flags won't be exposed at all with nginx module. CAS will be abstracted as ETag with all related semantic. I will rewrite view results to hide flags, expiration (currently) and make CAS (ETag) more accessible. i.e. render {"meta":{"etag":"xxx"}} always, for both include_docs false and true. And pull it to the upper level in json tree (see view examples above).

      For this iteration I probably won't implement TTL, but it probably can be mapped to HTTP headers too.

  4. Feb 27

    Aleksey Kondratenko says:

    Sergey, I'm okay with you doing arbitrary API with arbitrary requirement. Let m...

    Sergey, I'm okay with you doing arbitrary API with arbitrary requirement.

    Let me note however that my team not only has real requirement for HTTP CRUD API, but also long term plan to have a proper, complete and supported HTTP CRUD API implementation.

    So if you build something that can't be compatible with complete implementation, then we'll have 2 incompatible HTTP APIs. I think that's suboptimal. And that's why I believe we should be careful now to have all bases covered.

    Real problem is that design docs/views namespace is separate from docs namespace. And this is not reflected at all in your proposal.

    How about /<bucket/d/<doc-id> as namespace for CRUD ops ?

    1. Feb 28

      Matt Ingenthron says:

      Alk, we should sync up about those requirements at some point. In any case, I ...

      Alk, we should sync up about those requirements at some point.

      In any case, I agree with trying to socialize and come up with one implementation that covers all of the bases, but we may want to do that in stages.

      Toward that end, with Sergey's ideas here, we should come up with what the end looks like and then determine what the first stage may look like I think.

    2. Mar 05

      Sergey Avseyev says:

      How about /<bucket/d/<doc-id> as namespace for CRUD ops ? Maybe bette...

      How about /<bucket/d/<doc-id> as namespace for CRUD ops ?

      Maybe better

      /<bucket>/_doc/<doc-id>

      similar to

      /<bucket>/_design/<ddoc-id>

      1. Mar 05

        Aleksey Kondratenko says:

        +1 for _doc

        +1 for _doc

  5. Apr 16

    Aleksey Kondratenko says:

    Pinging. From brief look at couchbaselabs repository it appears implementation ...

    Pinging.

    From brief look at couchbaselabs repository it appears implementation proceeds for original API which means it'll be inherently incompatible with future implementation(s).