Tutorial

TODO

Installation

Bottle-REST is not dependant on external library other than Bottle. Download bottle_rest.py and bottle.py (if you don’t already have it) into your project directory.

$ wget https://bottlepy.org/bottle.py
$ wget https://raw.githubusercontent.com/thepure12/bottle-rest/main/bottle_rest.py

Quickstart

Once you have both Bottle and Bottle-REST installed, we can create a basic API.

from bottle import Bottle
from bottle_rest import API, Resource

app = Bottle()
api = API()
app.install(api)

class HelloWorld(Resource):
    def get(self):
        return {'msg': 'hello world'}

api.addResource(HelloWorld, '/')

if __name__ == '__main__':
    app.run(debug=True)

Save this to a file called app.py (or download it) and run it.

$ python app.py
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://127.0.0.1:8080/
Hit Ctrl-C to quit.

Using curl to test:

$ curl http://127.0.0.1:8080/
{"msg":"hello world"}

HTTP Methods

Bottle-REST is built on Bottle’s request routing. Each resources gives you access to the get(), post(), put(), delete() decorators by defining the methods in your resource.

from bottle import Bottle
from bottle_rest import API, Resource

app = Bottle()
api = API()
app.install(api)

food = []

class Food(Resource):
    def get(self):
        return {"food": food}

    def post(self, name):
        food.append(name)
        return {"name": food}

api.addResource(Food, '/food')

if __name__ == '__main__':
    app.run(debug=True)

Using curl to test:

$ curl http://127.0.0.1:8080/food \
> --header "Content-group: application/json" \
> --data '{"name":"carrot"}'
{"name": "carrot"]}
$ curl http://127.0.0.1:8080/food
{"food":["carrot"]}

Endpoints

Resources can also handle multiple URLs. Pass multiple URLs into the addResource() method and each will be routed to your resource.

api.addResource(Food, "/food", "/foods")
$ curl http://127.0.0.1:8080/food
{"food":[]}
$ curl http://127.0.0.1:8080/foods
{"food":[]}

Resources also handle Bottle’s dynamic routes. These are routes that contain wildcards, where a wildcard is: “name enclosed in angle brackets (e.g. <name>) and accepts one or more characters up to the next slash (/)”. Each wildcard is passed in as a keyword argument to the Resource method that the request routes to.

See Bottle’s Dynamic Routes
class Food(Resource):
    def get(self, name):
        return {"food": name}

api.addResource(Food, "/food", "/food/<name>")
$ curl http://127.0.0.1:8080/food/carrot
{"name": "carrot"}

Data Validation

TODO

food = {"carrot": {"group": "vegetable"}}

class Food(Resource):
    def get(self, name=None):
        if name and name in food:
            return {name: food[name]}
        return {"food": food}

    def post(self, name, group="junk"):
        food[name] = {"group": group}
        return {name: {"group": group}}

api.addResource(Food, '/food', '/food/<name>')

TODO

$ curl http://127.0.0.1:8080/food
{"food":{"carrot":{"group":"vegetable"}}}

# Get food named carrot
$ curl http://127.0.0.1:8080/food/carrot
{"carrot":{"group":"vegetable"}}

# Add food named candy with default group
$ curl http://127.0.0.1:8080/food \
> --header "Content-group: application/json" \
> --data '{"name":"candy"}'
{"candy":{"group":"junk"}}

# Add food named apple with group fruit
$ curl http://127.0.0.1:8080/food \
> --header "Content-group: application/json" \
> --data '{"name":"apple", "group": "fruit"}'
{"apple":{"group":"fruit"}}

# Add food without a name
$ curl http://127.0.0.1:8080/food \
> --header "Content-Type: application/json" \
> --data '{"group":"fruit"}'
{"name":"This feild is is required"}

# Update carrot group
$ curl http://127.0.0.1:8080/food/carrot \
> --header "Content-group: application/json" \
> --data '{"group":"veggie"}'
{"carrot":{"group":"veggie"}}

$ curl http://127.0.0.1:8080/food
{
    "food": {
        "carrot":{"group":"veggie"},
        "candy":{"group":"junk"},
        "apple":{"group":"fruit"}
    }
}

URL Params

TODO

food = {
    "carrot": {"group": "vegetable"},
    "squash": {"group": "vegetable"},
    "apple": {"group": "fruit"},
    "orange": {"group": "fruit"}
}

class Food(Resource):
    def get(self):
        if "group" in self.params:
            group = self.params["group"]
            return {n: f for n, f in food.items() if f["group"] == group}
        return {"food": food}

api.addResource(Food, '/food')

TODO

$ curl http://127.0.0.1:8080/food
{"food":{"carrot":{"group":"vegetable"},"squash":{"group":"vegetable"},"apple":{"group":"fruit"},"orange":{"group":"fruit"}}}
$ curl http://127.0.0.1:8080/food?group=fruit
{"apple":{"group":"fruit"},"orange":{"group":"fruit"}}