Building a Fast and Efficient Backend: Why We Chose FastAPI and SQLite
To power our new web application and manage real-time communications with multiple 3D printers, we needed a backend that was robust, lightweight, and capable of handling multiple tasks simultaneously. We ultimately chose Python with FastAPI and SQLite, heavily relying on concurrent programming to ensure the system remains incredibly fast and efficient without eating up server resources.
Quick comparison: FastAPI + SQLite vs The Alternatives
-
FastAPI + SQLite (Our Choice)
- Pros: Blazing fast performance (thanks to ASGI), built-in async support, automatic API documentation, minimal setup overhead, zero-config database, and highly efficient for I/O-bound tasks.
- Cons: SQLite has limitations with heavy concurrent writes (though easily managed via architecture), and Python isn’t inherently as fast as compiled languages like Go or Rust for pure CPU crunching.
-
Django + PostgreSQL
- Pros: “Batteries-included” framework, incredibly robust ORM, easily handles massive scale and complex queries out of the box.
- Cons: Overkill for our current requirements, heavier memory footprint, and requires spinning up and managing a separate database service just to get started.
-
Node.js / Express
- Pros: Great async ecosystem, unified language across the stack (TypeScript).
- Cons: We wanted to leverage Python’s rich ecosystem for data processing and scripts, and FastAPI offers cleaner type-hint-driven validation and routing.
Why we picked this stack (concrete reasons)
- Concurrency & Responsiveness: By utilizing Python’s
asyncio, we can handle multiple non-blocking I/O operations simultaneously. This is critical when polling multiple printers or sending large files—one slow printer won’t block the rest of the application. - Speed of Development: FastAPI integrates natively with Pydantic. This means data validation, serialization, and Swagger/OpenAPI documentation generation happen automatically based on Python type hints.
- Simplicity & Portability: SQLite is an embedded database. There is no need to configure database users, manage network ports, or run dedicated containers. The entire database is a single file, making backups and deployments trivial.
- Resource Efficiency: We wanted a backend that could comfortably run on low-power devices (like a Raspberry Pi running alongside the printers) or a tiny VPS. FastAPI and SQLite require very little RAM overhead compared to traditional enterprise stacks.
Technology stack (what we used and why)
Core Backend & Concurrency
- Python 3.11+ — Modern features, strong typing, and excellent asynchronous capabilities.
- FastAPI — The core web framework. Extremely fast, lightweight, and intuitive.
- Uvicorn — A lightning-fast ASGI web server implementation used to run our FastAPI application.
- asyncio & httpx — Python’s standard library for concurrency combined with an async HTTP client. This allows us to make concurrent network calls to external printer APIs (like Moonraker) efficiently.
Database Layer
- SQLite — Our lightweight, serverless relational database. We enabled WAL (Write-Ahead Logging) mode to significantly boost concurrent read/write performance.
- SQLAlchemy (Async) — The ORM (Object Relational Mapper) that allows us to interact with SQLite using Python objects safely, without blocking the async event loop.
- Alembic — For tracking and applying database schema migrations over time.
Architecture overview
- Routers: Endpoints are logically grouped (e.g.,
/api/printers,/api/jobs,/api/users). FastAPI handles routing and strict Pydantic payload validation before any logic is executed. - Service Layer: This is where the core business logic lives. It decouples the API routes from the database and external hardware. The service layer spawns concurrent background tasks to communicate with printers without making the user wait.
- Event Loop & Background Tasks: When a user uploads a large 3D model, the API immediately returns a “Success” response, while a background task takes over the heavy lifting of streaming the file to the actual hardware.
- Data Access: All database queries use asynchronous sessions. This ensures that while the database is fetching data, the server can continue serving other users’ HTTP requests.
Testing, deploy & maintenance
- Local dev: Zero setup required for the database. Developers just install the Python requirements and run Uvicorn with hot-reloading enabled.
- Testing: We use
pytestcombined withpytest-asyncioandhttpxto write fast, asynchronous API tests that validate our endpoints against a temporary, in-memory SQLite database. - Deployment: The app is containerized via Docker. The entire backend environment lives in a single, easily deployable image, using a simple Docker volume mapped to the host to persist the SQLite
.dbfile. - Monitoring: Built-in FastAPI middleware logs response times, catching potential bottlenecks in our external API calls to the printers.
Conclusion & next steps
Building our backend with FastAPI, SQLite, and async Python provided the perfect balance of developer velocity and high-end performance. It easily handles our current traffic and concurrent hardware communications. Next steps for the backend include:
- Implementing WebSockets for real-time, two-way streaming of printer telemetry directly to the React frontend.
- Adding Redis for caching if external API rate-limiting becomes a necessity.
- Designing a seamless migration path to PostgreSQL via Alembic, should our concurrent write volume eventually outgrow SQLite’s capabilities.