Getting Started with Python FastAPI — Concepts, Install & Running on PyCharm (feat. uvicorn)
After working only with Java Spring Boot, I had to build something in Python, so I wrote this starter note while setting up FastAPI for the first time. The goals of this post are:
- Why use FastAPI (with uvicorn)
- Python's GIL and FastAPI — direction for development (a taste)
- Installing FastAPI — FastAPI and the ASGI server (uvicorn), plus a primer on web applications
- Running FastAPI on PyCharm — virtualenv vs system environment, two ways to run uvicorn, and the built-in Swagger UI
1. Why FastAPI?
FastAPI is praised for high performance through async processing and concise code. It is a good fit when fast response time is critical — for example, a "data analytics platform."
That said, you need to distinguish CPU-bound tasks from I/O-bound tasks. FastAPI shines on I/O-bound work (DB calls, external API calls, etc.), but when there are many CPU-bound tasks, async alone gives limited gains because of Python's GIL.
📘 FastAPI official docs — high performance, easy to learn, fast to code, ready for production.
2. Python's GIL & FastAPI
Because of the GIL (Global Interpreter Lock), Python cannot achieve true parallelism even with multiple threads. The GIL allows only one thread to execute Python bytecode at a time (to prevent race conditions), so multithreading does not help CPU-bound tasks.
Therefore, FastAPI's async (via uvicorn) is advantageous for I/O-bound work, but if you have many CPU-bound tasks you need multiprocessing or optimization in another language.
The GIL is worth understanding well, because it changes your development direction and code structure entirely. Especially if you come from Java Spring Boot (MVC), understand the difference between Python's and Java's async models before you start — it saves a lot of later refactoring. (I'll cover the GIL in depth in a separate post.)
3. Installing FastAPI
If pip or your Python environment variables are not set up yet, read the post below first.
3.1 Install FastAPI
Use pip, Python's package manager, to install the fastapi package.
pip install fastapi
To confirm the install, check that fastapi exists under Lib\site-packages and Scripts of your Python install path (e.g., D:\Python).
3.2 Install the ASGI (Asynchronous Server Gateway Interface) server
pip install "uvicorn[standard]"
ASGI is an interface for async web applications. If WSGI is the standard for synchronous web applications, ASGI is the standard extended to support async ones.
- ASGI — Uvicorn, Daphne / used with FastAPI
- WSGI — Gunicorn, uWSGI, Waitress / used with Django, Flask, etc.
Abstract, docs-style definitions may not click at first. I'll do a deep dive into the structure and mechanics of ASGI, WSGI, and frameworks like FastAPI in a separate post.
3.3 (Reference) Concept mapping for Java developers (Tomcat, Netty)
By role, Spring Boot (MVC)'s Tomcat and Spring WebFlux's Netty are analogous to WSGI and ASGI servers respectively. Because the roles are similar you might call these server gateways a "WAS," but strictly speaking that is not accurate.
Application servers (processes) like Tomcat, Netty, WSGI, and ASGI all run an internal loop to receive and process incoming HTTP requests (including request parsing, etc.).
Developers then pick a framework like Spring Boot, Flask, or FastAPI to build the service logic on top of such a server. For reference, Java's Tomcat is written in Java, while uvicorn is known to be written in Cython.
3.4 (Important) Web framework vs web application — relationship and roles
You might reasonably wonder the following.
Do web frameworks like FastAPI or Spring fail to act as a server without a web application such as Uvicorn, Gunicorn, Tomcat, Netty, or Jetty?
Figure 1. A simple diagram of the web framework / web application relationship
My answer is: unless you write your own while-loop server, a web framework alone cannot be a server. A web framework is less a web server itself and more a tool that makes it easy to develop an application that runs on top of a web server (application). Low-level work like network communication and thread management is handled by the web application.
In short, a web application server manages client communication, manages threads to efficiently handle many concurrent requests, and provides an environment to run the application logic. In Python's case, uvicorn even handles parent-child process management.
"But Spring Boot or fastapi[standard] starts a server just by running it?"
That's because Spring Boot ships with an embedded Tomcat, and installing fastapi[standard] and running fastapi dev main.py runs the embedded uvicorn.
📘 Uvicorn official docs — an ASGI web server, for Python.
Here is how to run uvicorn manually (from the uvicorn docs).
import asyncio
import uvicorn
async def app(scope, receive, send):
...
async def main():
config = uvicorn.Config("main:app", port=5000, log_level="info")
server = uvicorn.Server(config)
await server.serve()
if __name__ == "__main__":
asyncio.run(main())
You can see "main:app" being passed to uvicorn and run asynchronously via asyncio.run(main()).
4. Running FastAPI with uvicorn in PyCharm
Now that we've covered the concepts, let's actually set up and run it in PyCharm (Community 2024.2.3). We installed fastapi and uvicorn[standard] into the global site-packages via steps 3.1 and 3.2.
Term — global vs local site-packages When you install Python on your system, there is a library folder inside the install directory. Every project using that interpreter references it in common, so it's called global site-packages (e.g.,
D:\Python\Python.3.11.8\Lib\site-packages). Alternatively, you can give each project an isolated environment whose packages live only inside the project folder — a virtual environment (venv) = local site-packages. This is useful when different projects need different library versions.
4.1 Create a project (virtualenv / system environment)
Figure 2. Creating a new project
When you create a project in PyCharm, you choose an interpreter type (project venv / conda / custom environment). Here we'll bring in the Python interpreter we installed and reference the global site-packages.
- Interpreter type — choose Custom environment
- Environment — "Generate new (venv)" means use my interpreter but only the libraries inside the virtual environment. To reference global site-packages, choose "Select existing."
Figure 3. (Top) virtualenv / (Bottom) referencing global site-packages
With a virtual environment, Lib and Scripts folders are created inside the project, and external libraries reference that venv's Scripts. A project using global site-packages references the Python install path instead. If you want fastapi inside a venv, run pip install fastapi again from that environment's terminal.
4.2 Create main.py — the FastAPI instance
Figure 4. fastapi is now recognized
Create main.py under the project and write the official FastAPI example.
# Source: FastAPI official docs
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/sync/say-hello")
def read_root():
return {"Hello": "World"}
@app.get("/async/say-hello")
async def async_read_root():
return {"async Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
app = FastAPI() creates the FastAPI instance, and you pass this app to uvicorn to run the web application.
4.3 Two ways to run with uvicorn (ASGI)
① Run uvicorn directly from the terminal
uvicorn main:app --reload
This means "run uvicorn and pass it the app object inside main.py." --reload restarts the server automatically on code changes — a development convenience that is not recommended in production. (You can set the port with --port and the number of worker processes with --workers; I'll cover workers in the GIL/async post.)
Figure 5. uvicorn running
When uvicorn runs, the reloader and the uvicorn server run under separate process IDs. You can see that the reloader is its own process.
② Embed uvicorn in code (more intuitive)
from typing import Union
from fastapi import FastAPI
import asyncio
import uvicorn
app = FastAPI()
@app.get("/sync/say-hello")
def read_root():
return {"Hello": "World"}
@app.get("/async/say-hello")
async def async_read_root():
return {"async Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
async def main():
config = uvicorn.Config("main:app", port=8000, log_level="info")
server = uvicorn.Server(config)
await server.serve()
if __name__ == "__main__":
asyncio.run(main())
Here we pass app to uvicorn.Config to run the server directly, and run main asynchronously via asyncio. The structure is intuitive and easier to follow.
Figure 6. Running main in PyCharm
You can run it from the terminal with python main.py or via PyCharm's Run.
Figure 7. Run result
The result is the same as running uvicorn from the terminal. Since we didn't spawn a separate --reload process, only the single uvicorn application process ID appears.
4.4 Sending a request
Figure 8. Sending a request
To check that the server works, send a GET request to http://127.0.0.1:8000/sync/say-hello; you can confirm it returns a proper response.
4.5 FastAPI's built-in Swagger UI
Figure 9. FastAPI's built-in Swagger UI
Unlike Java Spring Boot, FastAPI bundles Swagger UI rather than pulling it in as a dependency. If your server domain is http://127.0.0.1:8000, append /docs to the path to see the Swagger UI for the provided APIs.
Wrap-up
In this post we looked at FastAPI's advantages and installation, the relationship between a web framework and a web application, and ran a FastAPI server step by step in PyCharm.
FastAPI is a powerful web framework that delivers high performance through async processing, well suited to applications that need fast responses. I also emphasized that understanding Python's GIL matters.
Next, I plan to cover configuring FastAPI as an API server (DB setup, routers, service layer) and how uvicorn interacts with the OS internally — including managing multiprocessing via parent-child processes to work around GIL constraints.
References
📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/23

Comments