Python API Development with FastAPI
FastAPI Basics
Create Posts
In previous examples, we discussed best practices and naming conventions for building APIs. In this updated guide, our FastAPI code is refactored to align with RESTful principles and industry standards.
Initial Implementation
Initially, our application defined GET and POST endpoints as follows:
@app.get("/")
def root():
return {"message": "Hello World"}
@app.get("/posts")
def get_posts():
return {"data": "This is your posts"}
@app.post("/createposts")
def create_posts(post: Post):
print(post)
print(post.dict())
return {"data": post}
Notice that the POST endpoint is defined with the path /createposts
, which is inconsistent with RESTful naming standards. Our goal is to standardize all endpoints, so we update the POST endpoint to use /posts
.
Improved Endpoint Definition
By changing the POST endpoint to /posts
, our API maintains a consistent naming scheme. The updated endpoint is shown below:
@app.post("/posts")
def create_posts(post: Post):
print(post)
print(post.dict())
return {"data": post}
After updating the endpoint, be sure to adjust your testing tools—such as Postman—with the new endpoint URL. For example, sending the following JSON in a POST request:
{
"title": "top beaches in florida",
"content": "something something beaches",
"rating": 4
}
will now target the /posts
endpoint. When tested, this endpoint responds as expected.
Note
Keep in mind that this example only prints and returns the submitted post; it does not persist data in a database.
Simulating Data Persistence
Since this simple example does not use a database, we simulate data persistence by storing posts in an in-memory list. In a production setting, these posts would be saved to a database where ID creation is handled automatically. Here, we assign a unique ID to each post using a random integer.
Below is the consolidated version of our FastAPI application:
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
from random import randrange
app = FastAPI()
class Post(BaseModel):
title: str
content: str
published: bool = True
rating: Optional[int] = None
# In-memory storage for posts
my_posts = [
{"title": "title of post 1", "content": "content of post 1", "id": 1},
{"title": "favorite foods", "content": "I like pizza", "id": 2}
]
@app.get("/")
def root():
return {"message": "Hello World"}
@app.get("/posts")
def get_posts():
return {"data": my_posts}
@app.post("/posts")
def create_posts(post: Post):
# Convert the Pydantic model to a dictionary
post_dict = post.dict()
# Assign a unique ID using a random number for demonstration purposes
post_dict['id'] = randrange(0, 100000)
my_posts.append(post_dict)
# Return the newly created post including its ID
return {"data": post_dict}
Code Breakdown
Data Model:
ThePost
class (a Pydantic model) defines the structure and validation rules for incoming post data.In-Memory Storage:
Themy_posts
list simulates a database and is pre-populated with two sample posts. Note that any data stored here will be lost when the server restarts.GET Endpoint:
The/posts
GET endpoint returns all stored posts in response to client requests.POST Endpoint:
The/posts
POST endpoint validates the input post using thePost
model, converts it to a dictionary, assigns a unique ID usingrandrange
, appends the post to the in-memory list, and returns the new post data.
When you test the GET endpoint (using Postman, for example), you should see a response similar to this:
{
"data": [
{
"title": "title of post 1",
"content": "content of post 1",
"id": 1
},
{
"title": "favorite foods",
"content": "I like pizza",
"id": 2
}
]
}
Next Steps
As you continue to enhance your API, consider incorporating input validation and error handling mechanisms to build a fully functional CRUD-based application.
By following these guidelines, you ensure your API is both maintainable and aligned with RESTful best practices.
Watch Video
Watch video content