Getting started with FastAPI
"We adopted the FastAPI library to create a REST server that can be queried to obtain predictions."
- Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber
Why should you adopt FastAPI? FastAPI combines high performance, modern features, and ease of use, making it excellent for building scalable APIs with Python. FastAPI's high performance is on par with Node.js and Go, which is the major reason I got into the framework. Over time, it has become my favorite backend framework to work with. Here are a few features of FastAPI that should make you consider adopting it.
- Developer-friendly: FastAPI allows you to create a clean and intuitive API declaration using Python type annotations. Annotations provide hints in your code without affecting how the code runs. For example, by using annotations, you can specify that the data passed through a function should be a string. This improves code readability and helps developers understand the expected data types in different parts of the code.
This is what Python type annotations look like:
This is what it looks like without Python annotations. You don't get any options that tell you this is a string. It's quite intuitive and can help you and other developers understand what types of data should be used in different parts of the code
Documentation: FastAPI automatically generates documentation based on your code. This saves you from writing separate documentation and keeps it in sync with your API implementation.
Scalability: FastAPI is designed for high scalability. Its asynchronous nature and efficient handling of concurrent requests allow it to handle high traffic and scale horizontally. To put it simply, scalability means being able to handle increasing demand and growing your operations to cater to a larger customer base while maintaining customer satisfaction. Imagine a small poultry business in a small city that experiences an increase in demand over time. To meet the demand and improve production, the business needs to scale its operations.
Modern and Standard Features: FastAPI incorporates modern features and supports WebSocket communication, GraphQL integration, dependency injection, middleware, and more. It provides a rich set of tools and features to build robust and advanced APIs.
Now, let's dive into building our first CRUD (Create, Read, Update, Delete) application with FastAPI. We'll be using Tortoise Object-Relational Mapping (ORM) and Pydantic models.
First, create a virtual environment:
pipenv shell
Next, initialize the app, create a model, and a Pydantic model:
from fastapi import FastAPI
from tortoise.models import Model
from tortoise import fields
from tortoise.contrib.fastapi import register_tortoise
from pydantic import BaseModel
app = FastAPI()
class Task(Model):
id = fields.IntField(pk=True)
newtasks = fields.CharField(max_length=200)
post_date = fields.DatetimeField(auto_now_add=True)
class TaskCreate(BaseModel):
newtasks: str
register_tortoise(
app,
db_url='sqlite:///Users/neo/Documents/Codez/projects/myapp/database.db',
modules={'models': ['main']},
generate_schemas=True,
add_exception_handlers=True,
)
Remember to add the following import.
Now, let's create a POST request to create our first task:
@app.post('/newtask/')
async def new_task(new_task: TaskCreate):
try:
task = await Task.create(newtasks=new_task.newtasks)
return {'message': 'Task created'}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
This is a create method that creates the task. Notice that we have to pass the function with the TaskCreate
Pydantic model.
Next is the Read method:
@app.get('/tasks/')
async def tasks():
tasks = await Task.all().order_by("-post_date")
return tasks
Let's get a specific task:
@app.get('/task/{task_id}/')
async def task(task_id: int):
try:
task = await Task.get(id=task_id)
return task
except DoesNotExist:
raise HTTPException(status_code=404, detail='Task not found')
Next, we can update a task:
@app.put('/updatetask/{task_id}')
async def update_task(task_id: int, updated_task: TaskCreate):
try:
await Task.filter(id=task_id).update(newtasks=updated_task.newtasks)
task = await Task.get(id=task_id)
return {'message': 'Task updated', 'task': task}
except DoesNotExist:
raise HTTPException(status_code=404, detail='Task not found')
Lastly, we can delete a task:
@app.delete('/deletetask/{task_id}')
async def delete_task(task_id: int):
try:
await Task.filter(id=task_id).delete()
return {'message': 'Task deleted'}
except DoesNotExist:
raise HTTPException(status_code=404, detail='Task not found')
Notice that in this code, we use Pydantic models, which provide a powerful and intuitive way to define and validate request and response data
Thank you for reading. Please let me know in the comments what you think about this content and give your suggestions for what's next.
You can also follow me on my social media channels to get the latest insights on Twitter.
Stay tuned for more updates on my blog, and don't forget to subscribe to my YouTube channel. I'm also open to gigs and collaborations, so make sure to check out my GitHub for some of my repositories. For any inquiries or questions, please contact me at ikponmwosaenabs@gmail.com.