FastAPI by A Python Beginner (1)
Intro & Environment Setup
This is a multiparter journey. I don’t know where it’s going to ends. You’re always welcome if you’re interested in this topic or want to join this step-by-step journey.
Intro
I’ve been thinking about pick up another programming language for very long time. I’m a frontend developer, JavaScript has been a blessing and a curse for me, because it’s such a versatile language. With the power of Node, I can do almost everything with it. I’m fully aware of the situation that every time when I start doing something, I begin with…
“Ok, so I can solve it with this JavaScript library.” or “I can just write a simple script and execute it with node.”
And this is like a perfect definition of “If all you have is a hammer, everything looks like a nail.”
There are quite a few languages I’d like to learn. And Python is one of them. I know Machine Learning, AI, web scraping etc… is where Python really shines. But I don’t want to be overwhelmed by all the technologies that I have no prior knowledge of while learning a new language. So, I decide to build a web service with it. And after some research online, I choose to go with FastAPI because of its simplicity. It’s a high-level framework that provides a good amount of tools to build a fully functional web API service. Then I don’t have to scratch my head the whole night try to figure out how to make things work. Because my priority is Python itself, not the other way around.
Disclaimers
- This is not a tutorial from someone who already knew everything about Python & FastAPI. It’s a learning process.
- FastAPI is still a new kid on the block in Python web development. I’m not here to promote this framework. Do properly evaluate its performance and capabilities before you start using it.
Environment Setup
Although I do use runtime version manger (currently asdf is my go-to choice). But this time I decide to give container a go. I think it’s a good fit for someone who just want to start coding without mess up their computer just because they’re not sure what they really need.
I’m using Docker to run and setup my container. If anyone want to follow alone and doesn’t have Docker installed on your computer, you can go https://www.docker.com/ and install it. The installation steps should be fairly straightforward and user-friendly enough.
Since I’m going to use container as my development environment. The first thing is going to create a Python image with all the dependencies installed.
The conventional way to manage Python dependencies is by using a requirements.txt
file.
fastapi==0.103.1
pydantic==2.3.0
uvicorn==0.23.2
These are the minimum dependency requirements for now.
Now what I need to do is copy this file into my image, and run pip install
to install those dependencies.
FROM python:3.11
WORKDIR /usr/src/app
COPY ./requirements.txt /usr/src/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /usr/src/requirements.txt
Here I created a dockerfile
in the project root. Inside the dockerfile
I use the official Python 3.11 image as my base image. I didn’t try to use the latest Python 3.12 one. Because it just released days ago, I’m not sure about its compatibility.
And next is tell docker to copy the requirements.txt
file from my local project root into /usr/src
inside the container. And then run pip install --no-cache-dir --upgrade -r /usr/src/requirements.txt
to install the dependencies.
Now I need a compose file to specify other aspects of this container. It’s time to create a docker-compose.yml
file in the project root directory.
version: "3.9"
services:
fastapi:
container_name: fastapi
build: .
working_dir: /usr/src/app
command: uvicorn main:app --host=0.0.0.0 --port=8000 --reload
environment:
DEBUG: 1
volumes:
- ./app:/usr/src/app
ports:
- "80:8000"
restart: on-failure
At this point, I still not sure what I’m going to make. So, I just keep the service name and container name as FastAPI
.
All the other settings are pretty straightforward. I tell docker to build the image from the dockerfile
in the current directory. And run the command uvicorn main:app --host=0.0.0.0 --port=8000 --reload
that make uvicorn
execute app
inside the main
module and start hosting this app on 0.0.0.0:8000
. The --reload
flag makes uvicorn
reload itself every time there’s any code changes. And mount the app
directory into container’s /usr/src/app
. Lastly, map my local 80
port to container’s 8000
port. So once the app start working, I can just go to http://localhost
from my browser and see the response from the server.
And now just create an app
folder which is going to be my working directory basically. Then create a __init__.py
and main.py
file under the app
directory.
In Python, each folder is a package, and each .py
file is a module. The __init__.py
is not mandatory, but it allows us to use a simpler way to import modules. It also serves other purposes, but for now I’ll just leave it empty.
After all the steps, now the project folder should look like something below.
app
├── __init__.py
├── main.py
dockerfile
docker-compose.yml
requirements.txt
The Very Original “Hello World”
If all the setup is correct, we can start our app with docker-compose up
command. It could take a while for Docker to pull the base Python image and run all the install commands.
Once the installation is completed. You should be able to see the log complaining about app
not found in module main
.
fastapi | ERROR: Error loading ASGI app. Attribute "app" not found in module "main".
Which is perfectly normal. Because we don’t have app
in our main.py
yet.
To initialize a FastAPI server, we first need to import the module in our main.py.
from fastapi import FastAPI
As a frontend developer, this syntax kind of bugs me a little bit. It's totally opposite of JavaScript’s import x from y
.
Then we have to use it by declare the app
and call the FastAPI
we just imported
app = FastAPI()
After this step, our app should be able to take requests and send the response to the client. If you open the browser and go to http://localhost
, you should be able to see {"detail": "Not Found"}
with 404 status code.
Now, it’s time to say the infamous “Hello World”.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"Hello": "World!"}
We tell FastAPI to take all the get
request of /
to the following async function root
, which returns a JSON {"Hello": "World!"}
.
It’s just that simple. And this is basically why I choose FastAPI framework as my first taste of Python. Everything feels nature if you already have prior experiences of making an API service in other languages.
Time to call it a day.
All the source code can be found on my github: https://github.com/LeoCantThinkOfAName/newbie-fastapi