Caching Example

  • how-to
    +
    A walk-through of the basics of Key-Value operations with Couchbase, through the lens of a REST api caching layer.

    This example uses Flask as a web-framework for a Python REST API. Flask has no direct support for async, but another code sample is available demonstrating this whole application using the Couchbase async API here. You can also find the full code for this example here.

    Basic Endpoint

    Our first basic endpoints will be a get and set call, using HTTP methods GET and POST and Couchbase methods get and insert respectively:

    GET
    @app.route('/<key>', methods=['GET'])
    def get(key):
        try:
            res = cb.get(key)
            return jsonify(res.content_as[dict])
        except DocumentNotFoundException:
            return 'Key not found', 404
        except CouchbaseException as e:
            return 'Unexpected error: {}'.format(e), 500
    POST
    @app.route('/<key>', methods=['POST'])
    def post(key):
        try:
            cb.insert(key, request.json, expiry=EXPIRY)
            return 'OK'
        except DocumentExistsException:
            return 'Key already exists', 409
        except CouchbaseException as e:
            return 'Unexpected error: {}'.format(e), 500

    This is the simplest API we can make — allowing us to set and get arbitrary JSON from any key we specify. We also include the expiry parameter, which will automatically delete the document (invalidate the cache) after a set amount of time.

    Cache Miss

    There are many ways this can and could be improved upon for real world use. What happens in the case of a cache miss? With this code, we handle the DocumentNotFoundException and respond with a HTTP 404.

    @app.route('/<key>', methods=['GET'])
    def get(key):
        try:
            res = cb.get(key)
            return jsonify(res.content_as[dict])
        except DocumentNotFoundException:
            return 'Key not found', 404
        except CouchbaseException as e:
            return 'Unexpected error: {}'.format(e), 500

    Error Handling

    We can also improve the POST function to deal with some of the errors it may encounter. Even if something unexpected happens, we can still be helpful by including the error in the 500 response, and by catching any CouchbaseException as a fallback:

    @app.route('/<key>', methods=['POST'])
    def post(key):
        try:
            cb.insert(key, request.json, expiry=EXPIRY)
            return 'OK'
        except DocumentExistsException:
            return 'Key already exists', 409
        except CouchbaseException as e:
            return 'Unexpected error: {}'.format(e), 500

    The last thing we’ll do is add PUT and DELETE endpoints, matching up to the Couchbase operations upsert and remove, and apply the same error handling once more:

    PUT
    @app.route('/<key>', methods=['PUT'])
    def put(key):
        try:
            cb.upsert(key, request.json, expiry=EXPIRY)
            return 'OK'
        except CouchbaseException as e:
            return 'Unexpected error: {}'.format(e), 500
    DELETE
    @app.route('/<key>', methods=['DELETE'])
    def delete(key):
        try:
            cb.remove(key)
            return 'OK'
        except DocumentNotFoundException:
            # Document already deleted / never existed
            return 'Key does not exist', 404

    Additional Resources

    • You can find the full contextualized code from this sample here.

    • Selecting an ephemeral or Memcached bucket for the advantages of purely in-memory storage may make sense as a design decision.

    • A webinar, a whitepaper, and other high-level information on choosing Couchbase as a caching layer is on the main Couchbase website.