Building and documenting REST APIs with Django and Swagger (Part 1)

Deanna Thompson

How to build a coffee-themed API with Django and the Django REST Framework

I’ve been on the hunt for a new technical writing position for the past two months. It’s been a while since I was on the market!

A few weeks ago, I made a list of some new skills I wanted to gain, and one of them is building my own API. So I decided to learn a little bit of Django to build my own REST APi and write some documentation for it. I picked Django because I have experience working with Python.

It was a great learning experience, so I’d like to share how I built it.

Overview

In this tutorial, you’ll learn how to build a simple REST API using Django and the Django REST framework. Django is a free and open-source, Python-based web framework. Django is great because it works out of the box so you can start building websites quickly.

The Django REST Framework (DRF) is a tool for building web APIs. It’s extremely customizable and supports several authentication methods.

In the next part of this tutorial, you’ll learn how to document the API using Swagger and drf-spectacular.

Intended audience

This tutorial is intended for anyone who has basic knowledge of Python, Git, databases, APIs, and the command line. You should be comfortable running commands from the terminal and modifying Python code.

Prerequisites

Before you start this tutorial, complete the following prerequisites:

  1. Install Python 3.12+ for your operating system. Visit the official Python website for installation instructions.
  2. Install Pipenv. Pipenv is a Python virtualenv management tool. See the Pipenv documentation for installation instructions.

Step 1: Install dependencies

The first thing you need to do is install several dependencies. This part of the project relies on the django and djangorestframework packages.

First, activate the Pipenv shell. This command starts your virtual environment and generates a Pipfile:

pipenv shell

Then, install the following packages:

pipenv install django
pipenv install djangorestframework

Step 2: Create a new Django project

Now that you’ve installed your dependencies, create a Django project with the following command:

django-admin startproject app . 

Then run this command to create the app that will live within the Django project:

django-admin startapp brew 

After you run these commands, your directory should look like this:

├── Pipfile
├── Pipfile.lock
├── app
   ├── __init__.py
   ├── asgi.py
   ├── settings.py
   ├── urls.py
   └── wsgi.py
├── brew
   ├── __init__.py
   ├── admin.py
   ├── apps.py
   ├── migrations
   └── __init__.py
   ├── models.py
   ├── tests.py
   └── views.py
└── manage.py

Step 3: Update your settings.py file

The app/settings.py file contains all of the configurations for your Django project. In this file, you’ll include all of the Python packages needed to build your API.

Under the INSTALLED_APPS section, add the following lines:

INSTALLED_APPS = [
    'django.contrib.admin',
    ...
    'rest_framework', 
    'rest_framework.authtoken`,
    'brew' 

Then, add the bottom of the file, add this code to control how many objects are returned:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication'
    ),
    'PAGE_SIZE': 10 
}

Step 4: Add your models

Mdoels contain essential fields and behaviors about the data that you’re storing. Each model maps to a single database table.

In this tutorial, you’re building a coffee-themed API, so you’ll create two models: one for coffee and one for snacks.

Add the following code to brew/models.py:

# Create your models here.
class Coffee(models.Model):
    type = models.CharField(max_length=200)
    temperature = models.CharField(max_length=500)
    caffeine_amount = models.CharField(max_length=200)
    price = models.CharField(max_length=500)

class Snack(models.Model):
    type = models.CharField(max_length=200)
    product_name = models.CharField(max_length=200, null=True)
    price = models.CharField(max_length=500) 

The Coffee model has four fields: type, temperature, caffeine_amount, and price. The Snack model has three fields: type, product_name and price.

Step 5: Add your serializers

The DRF includes serializers that convert data from Python objects into JSON or XML. JSON is the most common format for sending and requesting data through a REST API, so it’s best to use the DRF serializers to convert the data to JSON.

Create a serializers.py file in the brew directory, and then add the following code:

from rest_framework import serializers
from .models import Coffee
from .models import Snack

class CoffeeSerializer(serializers.ModelSerializer):
    class Meta:
        model=Coffee
        fields=['id', 'type', 'temperature', 'caffeine_amount', 'price']
    
class SnackSerializer(serializers.ModelSerializer):
    class Meta:
        model=Snack
        fields=['id', 'type', 'product_name', 'price']

Step 6: Create your views

A view function takes a web request and returns a web response. The response can be the HTML contents of a web page, a redirect, or even a 404 error. All of your views are placed in the views.py file in your application directory.

In brew/views.py: add your import statements:

from django.shortcuts import render, get_object_or_404
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from .models import Coffee
from .models import Snack
from .serializers import CoffeeSerializer
from .serializers import SnackSerializer

Then create a view to create a new coffee:

@api_view(['POST']) 
def create_coffee(request): # View to create a new coffee
    serializer = CoffeeSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Then create a view to get a coffee by ID:

@api_view(['GET']) # View to get a coffee by ID
def get_coffee_by_id(request, pk):
    coffee = get_object_or_404(Coffee, pk=pk)
    serializer = CoffeeSerializer(coffee)
    return Response(serializer.data)

Next, create a view to create a new snack:

@api_view(['POST']) # View to create a new snack
def create_snack(request):
    serializer = SnackSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Lastly, create a view to get a snack by ID:

@api_view(['GET']) # View to get a snack by ID
def get_snack_by_id(request, pk):
    snack = get_object_or_404(Snack, pk=pk)
    serializer = SnackSerializer(snack)
    return Response(serializer.data)

Step 7: Configure URLs for your app

Almost done! Next, you need a way to route HTTP requests to the appropriate view.

In the brew directory, create a urls.py file to add routes to your views:

from django.urls import path
from .views import create_coffee
from .views import create_snack
from .views import get_coffee_by_id
from .views import get_snack_by_id

urlpatterns = [
    path('api/coffees/', create_coffee, name='create_coffee'),
    path('api/snacks/', create_snack, name='create_snack'),
    path('api/coffees/<int:pk>/', get_coffee_by_id, name='get_coffee_by_id'),
    path('api/snacks/<int:pk>/', get_snack_by_id, name='get_snack_by_id')
]

The URL patterns correspond to the different views that handle the logic for those URLs. For example, the path coffee/1/ calls the get_coffee_by_id function.

Step 8: Add URLs to your Django project

The next thing to do is add the brew app URLs to the project.

In app/urls.py, add the following code:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('brew.urls'))  
]

Step 9: Make your migrations

Now that you’ve configured the project and app, you need to sync your database. Run these commands from the command line:

python3 manage.py makemigrations
python3 manage.py migrate

These commands add any changes you make to your models into your database schema. You should see output on the terminal that indicates that the models were created and the migrations ran successfully.

$ python3 manage.py makemigrations
Migrations for 'brew':
  brew/migrations/0001_initial.py
    - Create model Coffee
    - Create model Snack

$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, authtoken, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  ...

Step 10: Run the API server

You’ve made it to the end! You’ve configured your project, the app, and synced the database. The very last step is to run the API server.

From the command line, run python3 manage.py runserver.

Next, navigate to 127.0.0.1:8000/api/coffees. You should see the “Create coffee” page.

You can post new data using the text box:

{
    "type": "mocha",
    "temperature": "hot",
    "caffeine_amount": "105",
    "price": "3.75"
}

After you post the data, you’ll see the new resource and its ID in the text box:

HTTP 201 Created
Allow: GET, POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "type": "mocha",
    "temperature": "hot",
    "caffeine_amount": "105",
    "price": "3.75"
}

What to do next

Congratulations on completing this part of the tutorial! You successfully built a working REST API and defined several endpoints for those resources.

I need to start working on the next part of the tutorial: documenting the API with Swagger and drf-spectacular. In the meantime, here are some things you can do with your project:

  1. Send some data to the API using the DRF user interface or cURL.
  2. Learn about DRF Authentication. I might cover this in a future tutorial.
  3. Consider adding some more views and URLs to the project.