text: REST APIs have become a common way to establish an interface between web back-ends and front-ends, and between different web services. The simplicity of this kind of interface, and the ubiquitous support of the HTTP and HTTPS protocols across different networks and frameworks, makes it an easy choice when considering interoperability issues.
  Bottle is a minimalist Python web framework. It is lightweight, fast, and easy to use, and is well-suited to building RESTful services. A bare-bones comparison made by Andriy Kornatskyy put it among the top three frameworks in terms of response time and throughput (requests per second). In my own tests on the virtual servers available from DigitalOcean, I found that the combination of the uWSGI server stack and Bottle could achieve as low as a 140μs overhead per request.
  In this article, I’ll provide a walkthrough of how to build a RESTful API service using Bottle.
  Installation and Configuration
  The Bottle framework achieves its impressive performance in part thanks to its light weight. In fact the entire library is distributed as a one-file module. This means that it does not hold your hand as much as other frameworks, but it is also more flexible and can be adapted to fit into many different tech stacks. Bottle is therefore best suited for projects where performance and customizability are at a premium, and where the time-saving advantages of more heavy-duty frameworks are less of a consideration.
  The flexibility of Bottle makes an in-depth description of setting up the platform a bit futile, since it may not reflect your own stack. However, a quick overview of the options, and where to learn more about how to set them up, is appropriate here:
  Installation
  Installing Bottle is as easy as installing any other Python package. Your options are:
  - Install on your system using the system’s package manager. Debian Jessie (current stable) packages the version 0.12 as python-bottle.
  - Install on your system using the Python Package Index with
  pip install bottle.
  - Install on a virtual environment (recommended).
  To install Bottle on a virtual environment, you’ll need the virtualenv and pip tools. To install them, please refer to the virtualenv and pip documentation, though you probably have them on your system already.
  In Bash, create an environment with Python 3:
  $ virtualenv -p `which python3` env
  Suppressing the
  -p `which python3` parameter will lead to the installation of the default Python interpreter present on the system – usually Python 2.7. Python 2.7 is supported, but this tutorial assumes Python 3.4.
  Now activate the environment and install Bottle:
  $ . env/bin/activate $ pip install bottle
  That’s it. Bottle is installed and ready to use. If you’re not familiar with virtualenv or pip, their documentation is top notch. Take a look! They are well worth it.
  Server
  Bottle complies with Python’s standard Web Server Gateway Interface (WSGI), meaning it can be used with any WSGI-compliant server. This includes uWSGI, Tornado, Gunicorn, Apache, Amazon Beanstalk, Google App Engine, and others.
  The correct way to set it up varies slightly with each environment. Bottle exposes an object that conforms to the WSGI interface, and the server must be configured to interact with this object.
  To learn more about how to set up your server, refer to the server’s docs, and to Bottle’s docs, here.
  Database
  Bottle is database-agnostic and doesn’t care where the data is coming from. If you’d like to use a database in your app, the Python Package Index has several interesting options, like SQLAlchemy, PyMongo, MongoEngine, CouchDB and Boto for DynamoDB. You only need the appropriate adapter to get it working with the database of your choice.
  Bottle Framework Basics
  Now, let’s see how to make a basic app in Bottle. For code examples, I will assume Python >= 3.4. However, most of what I’ll write here will work on Python 2.7 as well.
  A basic app in Bottle looks like this:
  import bottle app = application = bottle.default_app() if __name__ == '__main__': bottle.run(host = '127.0.0.1', port = 8000)
  When I say basic, I mean this program doesn’t even “Hello World” you. (When was the last time you accessed a REST interface that answered “Hello World?”) All HTTP requests to
  127.0.0.1:8000 will recieve a 404 Not Found response status.
  Apps in Bottle
  Bottle may have several instances of apps created, but for the sake of convenience the first instance is created for you; that’s the default app. Bottle keeps these instances in a stack internal to the module. Whenever you do something with Bottle (such as running the app or attaching a route) and don’t specify which app you’re talking about, it refers to the default app. In fact, the
  app = application = bottle.default_app() line doesn’t even need to exist in this basic app, but it is there so that we can easily invoke the default app with Gunicorn, uWSGI or some generic WSGI server.
  The possibility of multiple apps may seem confusing at first, but they add flexibility to Bottle. For different modules of your application, you might create specialized Bottle apps by instantiating other Bottle classes and setting them up with different configurations as needed. These different apps could be accessed by different URLs, through Bottle’s URL router. We won’t delve into that in this tutorial, but you are encouraged to take a took at Bottle’s documentation here and here.
  Server Invocation
  The last line of the script runs Bottle using the indicated server. If no server is indicated, as is the case here, the default server is Python’s built-in WSGI reference server, which is only suitable for development purposes. A different server can be used like this:
  bottle.run(server='gunicorn', host = '127.0.0.1', port = 8000)
  This is syntactic sugar that let’s you start the app by running this script. For example, if this file is named
  main.py, you can simply run
  python main.py to start the app. Bottle carries quite an extensive list of server adapters that can be used this way.
  Some WSGI servers don’t have Bottle adapters. These can be started with the server’s own run commands. On uWSGI, for instance, all you’d have to do would be to call
  uwsgi like this:
  $ uwsgi --http :8000 --wsgi-file main.py
  A Note on File Structure
  Bottle leaves your app’s file structure entirely up to you. I’ve found my file structure policies evolve from project to project, but tend to be based on an MVC philosophy.
  Building Your REST API
  Of course, nobody needs a server that only returns 404 for every requested URI. I’ve promised you we’d build a REST API, so let’s do it.
  Suppose you’d like to build an interface that manipulates a set of names. In a real app you would probably use a database for this, but for this example we will just use the in-memory
  set data structure.
  The skeleton of our API might look like this. You can place this code anywhere in the project, but my recommendation would be a separate API file, such as
  api/names.py.
  from bottle import request, response from bottle import post, get, put, delete _names = set() # the set of names @post('/names') def creation_handler(): '''Handles name creation''' pass @get('/names') def listing_handler(): '''Handles name listing''' pass @put('/names/<name>') def update_handler(name): '''Handles name updates''' pass @delete('/names/<name>') def delete_handler(name): '''Handles name deletions''' pass
  Routing
  As we can see, routing in Bottle is done using decorators. The imported decorators
  get,
  put, and
  delete register handlers for these four actions. Understanding how these work can be broken down as follows:
  - All of the above decorators are a shortcut to the
  default_approuting decorators. For example, the
  @get()decorator applies
  bottle.default_app().get()to the handler.
  - The routing methods on
  default_appare all shortcuts for
  route(). So
  default_app().get('/')is equivalent to
  default_app().route(method='GET', '/').
  So
  @get('/') is the same as
  @route(method='GET', '/'), which is the same as
  @bottle.default_app().route(method='GET', '/'), and these can be used interchangeably.
  One helpful thing about the
  @route decorator is that if you’d like, for example, to use the same handler to deal with both object updates and deletes, you could just pass a list of methods it handles like this:
  @route('/names/<name>', method=['PUT', 'DELETE']) def update_delete_handler(name): '''Handles name updates and deletions''' pass
  Alright then, let’s implement some of these handlers.
  POST: Resource Creation
  Our POST handler might look like this:
  import re, json namepattern = re.compile(r'^[a-zA-Z\d]{1,64}$') @post('/names') def creation_handler(): '''Handles name creation''' try: # parse input data try: data = request.json() except: raise ValueError if data is None: raise ValueError # extract and validate name try: if namepattern.match(data['name']) is None: raise ValueError name = data['name'] except (TypeError, KeyError): raise ValueError # check for existence if name in _names: raise KeyError except ValueError: # if bad request data, return 400 Bad Request response.status = 400 return except KeyError: # if name already exists, return 409 Conflict response.status = 409 return # add name _names.add(name) # return 200 Success response.headers['Content-Type'] = 'application/json' return json.dumps({'name': name})
  Well, that’s quite a lot. Let’s review these steps part by part.
  Body Parsing
  This API requires the user to POST a JSON string at the body with an attribute named “name”.
  The
  request object imported earlier from
  bottle always points to the current request and holds all of the request’s data. Its
  body attribute contains a byte stream of the request body, which can be accessed by any function that is able to read a stream object (like reading a file).
  The
  request.json() method checks the headers of the request for the “application/json” content type and parses the body if it’s correct. If Bottle detects a malformed body (e.g.: empty or with wrong content type), this method returns
  None and thus we raise a
  ValueError. If malformed JSON content is detected by the JSON parser; it raises an exception that we catch and reraise, again as a
  ValueError.
  Object Parsing and Validation
  If there are no errors, we have converted the request’s body into a Python object referenced by the
  data variable. If we’ve received a dictionary with a “name” key, we’ll be able to access it via
  data['name']. If we received a dictionary without this key, trying to access it will lead us to a
  KeyError exception. If we’ve received anything other than a dictionary, we’ll get a
  TypeError exception. If any of these errors occur, once again, we reraise it as a
  ValueError, indicating a bad input.
  To check if the name key has the right format, we should test it against a regex mask, such as the
  namepattern mask we created here. If the key
  name isn’t a string,
  namepattern.match() will raise a
  TypeError, and if it doesn’t match it will return
  None.
  With the mask in this example, a name must be an ASCII alphanumeric with no blanks from 1 to 64 characters. This is a simple validation and it doesn’t test for an object with garbage data, for example. More complex and complete validation may be achieved through the use of tools such as FormEncode.
  Testing for Existence
  The last test before fulfilling the request is whether the given name already exists in the set. In a more structured app, that test should probably be done by a dedicated module and signaled to our API through a specialized exception, but since we’re manipulating a set directly, we have to do it here.
  We signal the existence of the name by raising a
  KeyError.
  Error Responses
  Just as the request object holds all the request data, the response object does the same for the response data. There are two ways of setting the response status:
  response.status = 400
  and:
  response.status = '400 Bad Request'
  For our example, we opted for the simpler form, but the second form may be used to specify the error’s text description. Internally, Bottle will split the second string and set the numeric code appropriately.
  Success Response
  If all the steps are successful, we fulfill the request by adding the name to the set
  _names, setting the
  Content-Type response header, and returning the response. Any string returned by the function will be treated as the response body of a
  200 Success response, so we simply generate one with
  json.dumps.
  GET: Resource Listing
  Moving on from name creation, we’ll implement the name listing handler:
  @get('/names') def listing_handler(): '''Handles name listing''' response.headers['Content-Type'] = 'application/json' response.headers['Cache-Control'] = 'no-cache' return json.dumps({'names': list(_names)})
  Listing the names was much easier, wasn’t it? Compared to name creation there isn’t much to do here. Simply set some response headers and return a JSON representation of all names, and we’re done.
  PUT: Resource Update
  Now, lets see how to implement the update method. It’s not very different from the create method, but we use this example to introduce URI parameters.
  @put('/names/<oldname>') def update_handler(name): '''Handles name updates''' try: # parse input data try: data = json.load(utf8reader(request.body)) except: raise ValueError # extract and validate new name try: if namepattern.match(data['name']) is None: raise ValueError newname = data['name'] except (TypeError, KeyError): raise ValueError # check if updated name exists if oldname not in _names: raise KeyError(404) # check if new name exists if name in _names: raise KeyError(409) except ValueError: response.status = 400 return except KeyError as e: response.status = e.args[0] return # add new name and remove old name _names.remove(oldname) _names.add(newname) # return 200 Success response.headers['Content-Type'] = 'application/json' return json.dumps({'name': newname})
  The body schema for the update action is the same as for the creation action, but now we also have a new
  oldname parameter in the URI, as defined by the route
  @put('/names/<oldname>').
  URI Parameters
  As you can see, Bottle’s notation for URI parameters is very straightforward. You can build URIs with as many parameters as you’d like. Bottle automatically extracts them from the URI and passes them to the request handler:
  @get('/<param1>/<param2>') def handler(param1, param2): pass
  Using cascading route decorators, you may build URIs with optional parameters:
  @get('/<param1>') @get('/<param1>/<param2>') def handler(param1, param2 = None) pass
  Also, Bottle allows for the following routing filters in URIs:
  int
  Matches only parameters that may be converted to
  int, and passes the converted value to the handler:
  @get('/<param:int>') def handler(param): pass
  float
  The same as
  int, but with floating point values:
  @get('/<param:float>') def handler(param): pass
  re(regular expressions)
  Matches only parameters which match the given regular expression:
  @get('/<param:re:^[a-z]+$>') def handler(param): pass
  path
  Matches subsegments of the URI path in a flexible way:
  @get('/<param:path>/id>') def handler(param): pass
  Matches:
  -
  /x/id, passing
  xas
  param.
  -
  /x/y/id, passing
  x/yas
  param.
  DELETE: Resource Deletion
  Like the GET method, the DELETE method brings us little news. Just note that returning
  None without setting a status returns a response with an empty body and a 200 status code.
  @delete('/names/<name>') def delete_handler(name): '''Handles name updates''' try: # Check if name exists if name not in _names: raise KeyError except KeyError: response.status = 404 return # Remove name _names.remove(name) return
  Final Step: Activating the API
  Supposing we’ve saved our names API as
  api/names.py , we can now enable these routes in the main application file
  main.py.
  import bottle from api import names app = application = bottle.default_app() if __name__ == '__main__': bottle.run(host = '127.0.0.1', port = 8000)
  Notice we’ve only imported the
  names module. Since we’ve decorated all the methods with their URIs attached to the default app, there is no need to do any further setup. Our methods are already in place, ready to be accessed.
  You can use tools like Curl or Postman to consume the API and test it manually. (If you are using Curl, you can use a JSON formatter to make the response look less cluttered.)
  Bonus: Cross Origin Resource Sharing (CORS)
  One common reason to build a REST API is to communicate with a JavaScript front-end through AJAX. For some applications, these requests should be allowed to come from any domain, not just your API’s home domain. By default, most browsers disallow this behavior, so let me show you how to set up cross-origin resource sharing (CORS) in Bottle to allow this:
  from bottle import hook, route, response _allow_origin = '*' _allow_methods = 'PUT, GET, POST, DELETE, OPTIONS' _allow_headers = 'Authorization, Origin, Accept, Content-Type, X-Requested-With' @hook('after_request') def enable_cors(): '''Add headers to enable CORS''' response.headers['Access-Control-Allow-Origin'] = _allow_origin response.headers['Access-Control-Allow-Methods'] = _allow_methods response.headers['Access-Control-Allow-Headers'] = _allow_headers @route('/', method = 'OPTIONS') @route('/<path:path>', method = 'OPTIONS') def options_handler(path = None): return
  The
  hook decorator allows us to call a function before or after each request. In our case, to enable CORS we must set the
  Access-Control-Allow-Origin,
  -Allow-Methods and
  -Allow-Headers headers for each of our responses. These indicate to the requester that we will serve the indicated requests.
  Also, the client may make an OPTIONS HTTP request to the server to see if it may really make requests with other methods. With this sample catch-all example, we respond to all OPTIONS requests with a 200 status code and empty body.
  To enable this, just save it and import it from the main module.
  Wrap Up
  That’s all there is to it!
  With this tutorial, I’ve tried to cover the basic steps to create a REST API for a Python app with the Bottle web framework.
  You can deepen your knowledge about this small but powerful framework by visiting its tutorial and API reference docs.