Build a REST API in Pyramid with TDD - Part One

about 3 years ago.

I will be going through a series of posts on how to create a REST API with Pyramid. The code source will be available in github.

We are going to create a TODO app. In this post, I will be talking about the application requirement, the project structure and how to setup the development environment.

Application description

The application will have 2 endpoints

List

This resource contain all todo lists. It will support 3 action GET, POST, and DELETE.

GET /todo will return all lists
POST /todo will create an empty new todo list
GET /todo/:list_id will return the content of the list that have list_id as id.
DELETE /todo/:list_id will delete the list.

List items

This resource contain the items of a list. It will support 3 action GET, POST, and DELETE.

GET /todo/:list_id:/items will return the list items
POST /todo/:list_id/items will add a new item to the list
DELETE /todo/:list_id:/items/:item_id mark the item as completed.
GET /todo/:list_id:/items/:item_id return the item's text.

Project structure

There will be two folders todo and tests. For the todo it will contain three sub folders one for the database models one for views api and the last one will contain script that will seed data into the database.
We will go through every folder in details in the next posts.
For the tests I will be following the conventions of pytest by putting the test directory outside the package.

todo/
├── __init__.py <- main file that will configure and return WSGI application
├── models      <- model definitions aka data sources (often RDBMS or noSQL)
│   ├── __init__.py
│   ├── meta.py
│   ├── list.py
│   ├── meta.py
│   └── list.py
├── scripts/    <- util Python scripts
│   ├── __init__.py
│   └── initializedb.py
└── api       <- views aka business logic
    ├── __init__.py
    ├── default.py
    └── notfound.py
tests/  <- seperate folder that contains the tests for the application
└── tests.py

Development environment

Any way to get the dependencies will works. The official documentation suggests to work with virtualenv. I advice you to use that. My setup is different, I am using Nix package manager for downloading packages and setting up a shell session. You can check my previous post on how to package a web app using Nix.

First test

To test the application setup, let's write our first test. It will reside in tests.py file in tests directory.

import unittest
from pyramid.paster import get_appsettings
settings = get_appsettings('test.ini', name='main')

class FunctionalTests(unittest.TestCase):
    def setUp(self):
        from todo import main
        app = main({}, **settings)
        from webtest import TestApp
        self.testapp = TestApp(app)

    def test_root(self):
        res = self.testapp.get('/')
        self.assertTrue(b'Hello World' in res.body)

As you can see we are getting the settings from test.ini file were the definition of database uri (see below) and passing them to the main function from todo package, that will return a WSGI which we can use to create a testapp using TestApp class from the webtest package.
In the test_root function we are issuing a GET requests to the home route. and check if 'Hello World' exist in the response.

test.ini

[app:main]
use = egg:todo
sqlalchemy.url = sqlite://

When you run the tests they must fail. Since we didn't implement the home route yet.

Loading Image

First View.

Views in pyramid are callable (function or classes) that take a request object apply the logic and return a response object.
We use the view_config decorator to create our first view. And map it to the '/' route using config.add_route.

@view_config(route_name='home')
def home(request):
    return Response('Hello World')

def includeme(config):
    config.add_route('home', '/')

After doing this our test will pass.

Loading Image

And that will be all for the first post.