In this course, we'll build a social media API. Users will be able to publish posts and write comments on them.
Let's start with publishing posts:
- Everyone will be able to post posts, without signing up first.
- Everyone will be able to see everyone's posts.
Doing this makes it much simpler. We don't have to worry about user authentication or the concept of "followers". We'll add those in later!
Creating an endpoint to receive a user's post
Imagine we are backend developers in a social media company, and we're tasked with making this API. There's another team that is tasked with making the website, and a third team that makes the mobile app. Both the website and mobile app will use your API, and that ensures that they both work in the same way.
APIs work by allowing clients (in our case, there's two) to send it data. The API then can do some processing, such as storing it in the database or sending an email, and then it responds to the client with some other data.
The first step in allowing users to publish posts is to add an API endpoint to which their client can send data. It doesn't matter to us if the client is the website or the mobile app, both will send us the same data. We'll then store that data in our database and respond with a success message.
First let's define what data clients will need to send us for their posts. Let's begin with something simple: the user's name and their post. Both of these are just text.
Where does the client get the user's name from?
As the API developer, we really don't care! The website or app could ask the user for their name when the user writes the post, or when they hit 'Publish', or at any other time. It's simply not something we need to worry about.
FastAPI relies strongly on another library called
pydantic to model the different bits of data a user might send. To create the model, create a file called
models/post.py with this content:
from pydantic import BaseModel class UserPost(BaseModel): name: str body: str
Now let's import that model in our
from fastapi import FastAPI from models.post import UserPost app = FastAPI()
And then let's create an endpoint to which users can send JSON. We'll then give the JSON to our model, and it will spit back out a Python object with two attributes:
The endpoint will use an HTTP POST method. The HTTP method is something that every request to our API will have. It's just a piece of data in the request that helps tell the API what the request is for.
What are HTTP methods?
The HTTP method is just some data in the request. For example, if the method is
GET, that usually means the user wants our API to give them some data. If the method is
POST, that usually means the user wants to send our API some data (to tell our API to create something, or store something in the database).
There are also other HTTP methods, such as
DELETE, and others.
Important: the HTTP methods themselves don't have much meaning. It's up to our API to decide what to do with each request, and the HTTP method gives us a bit more context about what the user wants.
Since the user wants us to create something in our API, let's use the
POST HTTP method for our endpoint:
from fastapi import FastAPI from models.post import UserPost app = FastAPI() @app.post("/post") async def create_post(post: UserPost): return post
Something very cool about FastAPI is how it uses Python type hinting to simplify the definition of our API. Here by telling it that the
post parameter is of type
UserPost, it will automatically pass the incoming request's JSON body through our
UserPost model. Then a few things happen:
UserPostmodel validates the incoming JSON. It makes sure the
bodyfields are in there, and they can be converted to strings if they aren't already.
- Converts the JSON into a
UserPostobject and passes that to our
create_postfunction, so that
postis a Python object with two attributes:
body. We don't have to parse the JSON ourselves.
- Helps populate the autogenerated documentation with information about what the client should send the API.
We've made the endpoint return the newly created post, but we could just return a message saying "Post created successfully" or something along those lines. In any case, the client should understand that receiving the newly created post JSON means "success".
Storing and returning user posts
Now let's store the post in a list so that future API requests can read them:
from fastapi import FastAPI from models.post import UserPost app = FastAPI() posts =  @app.post("/post") async def create_post(post: UserPost): posts.append(post) return post
All we've done here is added the
posts global variable, and then after we receive the client's data, we add the
post to the list.
Finally, let's make another endpoint that returns all the posts for everyone to see:
from fastapi import FastAPI from models.post import UserPost app = FastAPI() posts =  @app.post("/post", response_model=UserPost) async def create_post(post: UserPost): posts.append(post) return post @app.get("/posts", response_model=list[UserPost]) async def get_all_posts(): return posts
Here we've added
response_model=list[UserPost] to the decorators, beside the endpoint URL itself. This tells FastAPI how to convert our response to JSON.
create_post endpoint will pass the
post object to
UserPost, and be turned to a dictionary. FastAPI then returns the dictionary as JSON text.
get_all_posts endpoint will expect a list of
UserPost objects. Each object will be turned into a dictionary by the
pydantic library, and then the list will be returned as JSON text.
HTTP status codes
Just as the HTTP method is given to us by the client of our API and gives us a bit more information about what they want to achieve, we can return some data that tells the client the status of their request.
I'm sure you already know of some infamous status codes like
500. Each status code means something specific, such as "Not Found" or "Internal Server Error".
The default status code, which means
200. That's what FastAPI will use for all responses unless we tell it to do something else.
When we create something we should also return a specific status code:
201. Let's use that when we create posts:
from fastapi import FastAPI from models.post import UserPost app = FastAPI() posts =  @app.post("/post", status_code=201, response_model=UserPost) async def create_post(post: UserPost): posts.append(post) return post