
Foundational Hello World with FastAPI
Setting up a new web service can feel overwhelming with all the tooling and best practices to consider. In this article, I’ll walk you through creating a simple FastAPI service from scratch, implementing essential development tools that ensure code quality, reliability, and maintainability from the start. By the end, you’ll have a production-ready setup that includes unit testing, code formatting, type checking, and automated pre-commit hooks.
Before diving into the implementation, let’s understand why each tool in our stack is crucial:
- FastAPI: Modern, fast web framework with automatic API documentation
- Unit Tests: Catch bugs early and ensure your code works as expected
- Code Formatting: Maintain consistent style across your codebase
- Type Checking: Prevent runtime errors and improve code documentation
- Pre-commit Hooks: Automate quality checks before code reaches your repository
Before we start
For this article, you should have basic knowledge of Python and pytest routines.
We’ll use uv
as our package manager for its speed and reliability.
Make sure you have it installed on your machine. You can refer to the installation guide on the official website
To verify the installation you can run basic help command from the terminal:
uv --help
Once it’s installed we can proceed.
Note that you don’t need any Python distribution installed on your machine.
Step 1: Build the application
First, create your project structure:
mkdir foundational-hello-world
cd foundational-hello-world
From the terminal, move into folder and run:
uv init
The command will automatically create and fill the pyproject.toml
file to define our project, with the following content
You can modify the description at your ease.
I will also create:
- an empty
README.md
file, - a
.gitignore
, - a
.python-version
file with your current version (mine is 3.13) - a
main.py
module that we will check later
We can now add the FastAPI dependency we need to make our service.
First, let’s install the fastapi package simply by running:
uv add fastapi[standard]
The [standard]
option specifies to add standard extra packages we are going to use.
Once the installation is completed, we can open the main.py
file and write our FastAPI application
Now we can run the application from the terminal by running:
uv run fastapi dev
With uv
you can run any command in the application environment by prepending uv run
.
It’s a very useful feature to integrate commands without activating the environment.
The command fastapi dev
comes from the FastAPI CLI, and runs the server in development mode.
Visit http://localhost:8000
to see your API in action, and check http://localhost:8000/docs
for the automatic interactive documentation that FastAPI generates as shown in the figure:
This is the application as it is.
Step 2: Enforce with early test
You have built the application, you are now halfway through the work. We can now setup the testing routine. Testing is not optional—it’s your safety net. Let’s create the test for our unique endpoint
Let’s add the pytest packages by running:
uv add pytest --dev
The option --dev
specifies that this dependency will be grouped into the dev dependencies group so that it will be isolated from production.
At the project root, create a file test_main.py
containing our test
This test verifies both the HTTP status code and the JSON response content. FastAPI’s TestClient
makes it easy to test your endpoints without running a server.
Run your tests:
uv run pytest
You should see output confirming your test passes. As your application grows, you can run specific tests:
uv run pytest test_main.py::test_main
You have placed a robust foundation for your application.
Step 3: Formatting, Linting and Type Checking Like a Pro
Consistent code style reduces cognitive load and prevents style-related discussions in code reviews. Ruff is incredibly fast and combines formatting, linting, and import sorting in one tool.
Like we have done for pytest, we do the same for ruff. We install it as a dev dependency:
uv add ruff --dev
In the pyproject.toml
, we define basic rules in a dedicated section:
This configuration enables import sorting (I), specific error categories (E4, E7), Pyflakes (F), and quote consistency (Q).
Format your code:
uv run ruff format
Check for linting issues:
uv run ruff check
Ruff can also automatically fix many issues:
uv run ruff check --fix
Finally, we can also setup static type checking.
Type checking catches bugs before runtime and serves as documentation for other developers. Python’s type hints make this possible.
First, install mypy as a dev dependency:
uv add mypy --dev
Run type checking:
uv run mypy .
Our current code should pass without issues since FastAPI has excellent type support. As you add more complex logic, mypy will help catch type-related errors early.
For example, if you accidentally return the wrong type: From my experience, mypy is not yet mature to be used extensively, however today there are other promising tools that will definitely deserve dedicated articles such as pyrefly or ty.
Extra: Pre-commit Hooks for Automated Quality
Pre-commit hooks run automatically before each commit, ensuring code quality without relying on developer memory.
Create .pre-commit-config.yaml
:
Install and activate pre-commit:
uv add pre-commit --dev
uv run pre-commit install
Now, every time you commit, these checks run automatically:
git add .
git commit -m "Add FastAPI hello world service"
If any check fails, the commit is blocked until you fix the issues.
Wrap up
With this setup, your development workflow becomes:
- Write code
- Run tests:
pytest
- Let pre-commit handle formatting and linting automatically
- Push with confidence
For quick iterations during development:
- Start the developer server with:
uv run fastapi dev
- In another terminal, run tests when needed
uv run pytest
Conclusion
Setting up a robust development environment might seem like overhead for a simple “Hello World” service, but it pays dividends as your project grows. This setup ensures:
- Code quality through automated formatting and linting
- Reliability through comprehensive testing
- Team consistency through shared tooling and pre-commit hooks
- Developer confidence through type checking and automated validation
The initial investment in tooling creates a foundation that supports rapid, reliable development. Your future self (and teammates) will thank you for establishing these practices early.
Remember: good tooling doesn’t slow you down—it makes you faster by catching issues early and maintaining consistency across your codebase.
The complete code for this tutorial can be found at: https://github.com/yourusername/foundational-hello-world-fastapi