The basics of Creating a Full CRUD API in Python (Flask and FastAPI)

Summary of RESTful Convention

API IN FLASK

Setup

  • create a new project from THIS TEMPLATE and clone it to your computer.
  • navigate your terminal into the project folder and create a python virtual environment python -m venv venv
  • activate the virtual environment source ./venv/bin/activate
  • install all the dependencies from the requirements.txt pip install -r requirements.txt
    to update the requirements.txt if you install more libraries use the following command pip freeze > requirements.txt
  • test that the server runs by running python server.py and going to localhost:7000 to see if you can see the default route. (more details about the template in readme.md)

Setting Up Our Models

  • create a BlogPost class with a title and body property, constructor function
  • we’ll create an empty array to hold our blog posts.
  • we’ll create a sample BlogPost and add it to the array
# Our Blog Post Class
class BlogPost:
# Define our Properties
title = ""
body = ""
#define our constructor
def __init__(self, title, body):
self.title = title
self.body = body
# An Empty Array to hold our BlogPosts
posts = []
# A Sample BlogPost
first_post = BlogPost("My First Post", "The Body of My Post")
# Add the Post to our array
posts.append(first_post)

The Index Route

  • GET REQUEST => “/modelName” => Returns JSON of all items of the model
  • GET REQUEST => “/blogpost” => Returns JSON of all BlogPosts
  • import our class and array
  • create a get route return a dictionary with a property that is an tuple of dictionary versions of the BlogPost objects, we’ll use map to loop the BlogPost objects and convert them to dictionaries.
from flask import Blueprint
from models import BlogPost, posts
#############################################
## USE THIS FILE TO DEFINE ANY Routes
## Create Sub Blueprints as needed
#############################################
home = Blueprint("home", __name__)@home.get("/")
def home_home():
return {"home": "this is the home route"}
## BlogPost Index Route
@home.get("/blogpost")
def home_blogpost():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
  • run your server python server.py
  • go to localhost:7000/blogpost and see if you get the JSON response

The Show Route

  • Get request => /blogpost/<id> => return single object
from flask import Blueprint
from models import BlogPost, posts
#############################################
## USE THIS FILE TO DEFINE ANY Routes
## Create Sub Blueprints as needed
#############################################
home = Blueprint("home", __name__)@home.get("/")
def home_home():
return {"home": "this is the home route"}
## BlogPost Index Route
@home.get("/blogpost")
def home_blogpost():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
## BlogPost Show Route
@home.get("/blogpost/<id>")
def home_blogpost_id(id):
id = int(id)
return posts[id].__dict__
  • run your server python server.py
  • go to localhost:7000/blogpost/0 and see if you get the JSON response

The Create Route

  • Post Request => “/blogpost” => creates and returns new blogpost object
from flask import Blueprint, request
from models import BlogPost, posts
#############################################
## USE THIS FILE TO DEFINE ANY Routes
## Create Sub Blueprints as needed
#############################################
home = Blueprint("home", __name__)@home.get("/")
def home_home():
return {"home": "this is the home route"}
## BlogPost Index Route
@home.get("/blogpost")
def home_blogpost():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
## BlogPost Show Route
@home.get("/blogpost/<id>")
def home_blogpost_id(id):
id = int(id)
return posts[id].__dict__
@home.post("/blogpost")
def home_blopost_create():
#get dictionary of request body
body = request.json
# Create new BlogPost
post = BlogPost(body["title"], body["body"])
# append new BlogPost object to posts array
posts.append(post)
# return the new post as JSON
return post.__dict__
  • run your server python server.py
  • using a tool like postman make a post request to localhost:7000/blogpost and send a json body like this:
{
"title":"Another Post",
"body": "bloggity bloggity"
}
  • request the index route again to confirm the object was added

The Update Route

  • Put/Patch request to /blogpost/<id> => return updated object
from flask import Blueprint, request
from models import BlogPost, posts
#############################################
## USE THIS FILE TO DEFINE ANY Routes
## Create Sub Blueprints as needed
#############################################
home = Blueprint("home", __name__)@home.get("/")
def home_home():
return {"home": "this is the home route"}
## BlogPost Index Route
@home.get("/blogpost")
def home_blogpost():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
## BlogPost Show Route
@home.get("/blogpost/<id>")
def home_blogpost_id(id):
id = int(id)
return posts[id].__dict__
@home.post("/blogpost")
def home_blopost_create():
#get dictionary of request body
body = request.json
# Create new BlogPost
post = BlogPost(body["title"], body["body"])
# append new BlogPost object to posts array
posts.append(post)
# return the new post as JSON
return post.__dict__
  • run your server python server.py
  • using a tool like postman make a put or patch request to localhost:7000/blogpost/0 and send a json body like this:
{
"title":"Updated Post",
"body": "bloggity bloggity"
}
  • request the index route again to confirm the object was updated

Delete Route

  • Delete Request to /blogpost/<id> returns deleted object
from flask import Blueprint, request
from models import BlogPost, posts
#############################################
## USE THIS FILE TO DEFINE ANY Routes
## Create Sub Blueprints as needed
#############################################
home = Blueprint("home", __name__)@home.get("/")
def home_home():
return {"home": "this is the home route"}
## BlogPost Index Route
@home.get("/blogpost")
def home_blogpost():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
## BlogPost Show Route
@home.get("/blogpost/<id>")
def home_blogpost_id(id):
id = int(id)
return posts[id].__dict__
## The BlogPost Create Route
@home.post("/blogpost")
def home_blopost_create():
#get dictionary of request body
body = request.json
# Create new BlogPost
post = BlogPost(body["title"], body["body"])
# append new BlogPost object to posts array
posts.append(post)
# return the new post as JSON
return post.__dict__
#The BlogPost Update Route
@home.route("/blogpost/<id>", methods=["Put", "Patch"])
def home_update_blogpost(id):
# get id
id = int(id)
# get request body
body = request.json
# get post to be updated
post = posts[id]
# update post
post.title = body["title"]
post.body = body["body"]
# return updated object
return post.__dict__
@home.delete("/blogpost/<id>")
def home_blogpost_delete(id):
# get id
id = int(id)
# remove the item from the array
post = posts.pop(id)
# return removed item
return post.__dict__
  • run your server python server.py
  • using a tool like postman make a delete request to localhost:7000/blogpost/0
  • request the index route again to confirm the object was deleted

API IN FASTApi

Setup

  • create a new project from THIS TEMPLATE and clone it to your computer.
  • navigate your terminal into the project folder and create a python virtual environment python -m venv venv
  • activate the virtual environment source ./venv/bin/activate
  • install all the dependencies from the requirements.txt pip install -r requirements.txt
    to update the requirements.txt if you install more libraries use the following command pip freeze > requirements.txt
  • test that the server runs by running python server.py and going to localhost:7000 to see if you can see the default route. (more details about the template in readme.md)

Setting Up Our Models

  • create a BlogPost class with a title and body property, constructor function
  • we’ll create an empty array to hold our blog posts.
  • we’ll create a sample BlogPost and add it to the array
# Our Blog Post Class
class BlogPost:
# Define our Properties
title = ""
body = ""
#define our constructor
def __init__(self, title, body):
self.title = title
self.body = body
# An Empty Array to hold our BlogPosts
posts = []
# A Sample BlogPost
first_post = BlogPost("My First Post", "The Body of My Post")
# Add the Post to our array
posts.append(first_post)

The Index Route

  • GET REQUEST => “/modelName” => Returns JSON of all items of the model
  • GET REQUEST => “/blogpost” => Returns JSON of all BlogPosts
  • import our class and array
  • create a get route return a dictionary with a property that is an tuple of dictionary versions of the BlogPost objects, we’ll use map to loop the BlogPost objects and convert them to dictionaries.
from fastapi import APIRouter
from models import BlogPost, posts
##########################################
## Setup Your Routes in This File
##########################################
home = APIRouter(prefix="")@home.get("/")
async def home_home():
return {"home": "The Homepage"}
# Index Route
@home.get("/blogpost")
async def index():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
  • run your server python server.py
  • go to localhost:7000/blogpost and see if you get the JSON response

The Show Route

  • Get request => /blogpost/{id} => return single object
from fastapi import APIRouter
from models import BlogPost, posts
##########################################
## Setup Your Routes in This File
##########################################
home = APIRouter(prefix="")@home.get("/")
async def home_home():
return {"home": "The Homepage"}
# Index Route
@home.get("/blogpost")
async def index():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
# Show Route
@home.get("/blogpost/{id}")
async def show(id:int):
## Return the post with the right index
return posts[id].__dict__
  • run your server python server.py
  • go to localhost:7000/blogpost/0 and see if you get the JSON response

The Create Route

  • Post Request => “/blogpost” => creates and returns new blogpost object
# Import BaseModel from Pydantic
from pydantic import BaseModel
# Our Blog Post Class
class BlogPost:
# Define our Properties and types, which allows FastApi to validate
title = ""
body = ""
#define our constructor
def __init__(self, title, body):
self.title = title
self.body = body
# An Empty Array to hold our BlogPosts
posts = []
# A Sample BlogPost
first_post = BlogPost("My First Post", "The Body of My Post")
# BlogPostBody Class for Receiving Our Request Body
class BlogPostBody(BaseModel):
title:str
body:str
# Add the Post to our array
posts.append(first_post)
from fastapi import APIRouter
from models import BlogPost, posts, BlogPostBody
##########################################
## Setup Your Routes in This File
##########################################
home = APIRouter(prefix="")@home.get("/")
async def home_home():
return {"home": "The Homepage"}
# Index Route
@home.get("/blogpost")
async def index():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
# Show Route
@home.get("/blogpost/{id}")
async def show(id:int):
## Return the post with the right index
return posts[id].__dict__
#Create Route
@home.post("/blogpost")
async def create(post: BlogPostBody):
## Create a New BlogPost
posts.append(BlogPost(post.title, post.body))
## Return the Post
return post.__dict__
  • Make a post request to /blogpost creating a new blogpost, you can actually use FastApi built in documentation to test it out by going to /docs
  • request the index route again to confirm the object was added

The Update Route

  • Put/Patch request to /blogpost/{id} => return updated object
from fastapi import APIRouter
from models import BlogPost, posts, BlogPostBody
##########################################
## Setup Your Routes in This File
##########################################
home = APIRouter(prefix="")@home.get("/")
async def home_home():
return {"home": "The Homepage"}
# Index Route
@home.get("/blogpost")
async def index():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
# Show Route
@home.get("/blogpost/{id}")
async def show(id:int):
## Return the post with the right index
return posts[id].__dict__
#Create Route
@home.post("/blogpost")
async def create(post: BlogPostBody):
## Create a New BlogPost
posts.append(BlogPost(post.title, post.body))
## Return the Post
return post.__dict__
#Update Route
@home.api_route("/blogpost/{id}", methods=["Put", "Patch"])
async def update(id: int, post: BlogPostBody):
# get post to be updated
target = posts[id]
# update the post
target.title = post.title
target.body = post.body
# return the updated post
return target.__dict__
  • run your server python server.py
  • using a tool like postman or using /docs make a put or patch request to localhost:7000/blogpost/0 and send a json body like this:
{
"title":"Updated Post",
"body": "bloggity bloggity"
}
  • request the index route again to confirm the object was updated

Delete Route

  • Delete Request to /blogpost/{id} returns deleted object
from fastapi import APIRouter
from models import BlogPost, posts, BlogPostBody
##########################################
## Setup Your Routes in This File
##########################################
home = APIRouter(prefix="")@home.get("/")
async def home_home():
return {"home": "The Homepage"}
# Index Route
@home.get("/blogpost")
async def index():
# map over array of posts converting to tuple of dictionaries
return {"posts": tuple(map(lambda bp : bp.__dict__, posts))}
# Show Route
@home.get("/blogpost/{id}")
async def show(id:int):
## Return the post with the right index
return posts[id].__dict__
#Create Route
@home.post("/blogpost")
async def create(post: BlogPostBody):
## Create a New BlogPost
posts.append(BlogPost(post.title, post.body))
## Return the Post
return post.__dict__
#Update Route
@home.api_route("/blogpost/{id}", methods=["Put", "Patch"])
async def update(id: int, post: BlogPostBody):
# get post to be updated
target = posts[id]
# update the post
target.title = post.title
target.body = post.body
# return the updated post
return target.__dict__
#Destroy Route
@home.delete("/blogpost/{id}")
async def destroy(id: int):
# remove post
post = posts.pop(id)
# return removed post
return post.__dict__
  • run your server python server.py
  • using a tool like postman or /docs make a delete request to localhost:7000/blogpost/0
  • request the index route again to confirm the object was deleted

Conclusion

--

--

--

Alex Merced is a Full Stack Developer, learn more about his work at AlexMercedCoder.com

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Set Up SMTP and IMAP Proxy with HAProxy (Debian, Ubuntu, CentOS)

How to Schedule a Python Script as a Cron Job

How to create Microservices in Java using Spring — Step 5

Engineering Challenges at Plum

Tech Blog — Part 2 — NFT Auction Platform Tech Stack Overview

Are MySQL BETWEEN Operator Queries Inclusive?

Unity Development — Importing Assets

Anorak’s Journey (The Making Of The OASIS)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alex Merced Coder

Alex Merced Coder

Alex Merced is a Full Stack Developer, learn more about his work at AlexMercedCoder.com

More from Medium

PAGINATION USING PYTHON, REST API, FLASK & MONGODB

Using Celery with Flask for asynchronous tasks

How to Containerize Python Flask Application

How to debug Flask (running in docker-compose) in VS Code