How to Use uvicorn run fastapi from Another Python Module
You can launch a FastAPI application programmatically from a separate Python file by calling uvicorn.run() with an import string in the format "module:variable", where the module contains your FastAPI instance.
FastAPI applications are built as ASGI-compatible callables that web servers can execute. When working with the fastapi/fastapi repository, separating your application definition from server bootstrapping is a common pattern for production deployments and testing workflows. This technique uses uvicorn’s module import string syntax to reference your app indirectly, avoiding import side effects until the server actually starts.
The ASGI Architecture in fastapi/fastapi
Under the hood, FastAPI implements the ASGI interface that uvicorn expects. The core FastAPI class is defined in fastapi/applications.py, where it constructs the callable object that handles HTTP requests. This class builds the routing tree defined in fastapi/routing.py, which implements the path operations and dependency injection machinery. When you use uvicorn run fastapi, uvicorn creates an event loop, wraps your callable in an ASGI server, and begins serving traffic on the specified host and port.
Step 1: Create the Application Module
First, define your FastAPI instance in a dedicated module. This file should only contain route definitions and application configuration, with no server startup logic.
# myapi.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def hello(name: str = "World"):
return {"message": f"Hello, {name}!"}
In fastapi/applications.py, the FastAPI class initializes the routing structure that makes this object callable by any ASGI server.
Step 2: Build the Launcher Script
Create a separate Python file that imports uvicorn and invokes uvicorn.run() with an import string. This pattern delegates server management to a launcher while keeping your application module importable by test suites or other scripts.
# run_server.py
import uvicorn
# Import string format: "module:variable"
APP_IMPORT = "myapi:app"
def main() -> None:
"""Start the FastAPI server."""
uvicorn.run(
APP_IMPORT, # String reference to the app
host="0.0.0.0", # Bind to all interfaces
port=8000, # Standard FastAPI port
reload=True, # Auto-reload on code changes
)
if __name__ == "__main__":
main()
According to the fastapi/fastapi source code, placing the uvicorn.run() call inside if __name__ == "__main__": is critical. This guard ensures the server does not start when the module is merely imported by other Python files or test runners.
Understanding the Import String Syntax
The first argument to uvicorn.run() accepts either an application instance directly or a module import string. The string format uses a colon separator:
"myapi:app"tells uvicorn to import themyapimodule and use theappvariable- This lazy-loading approach prevents circular imports and keeps startup control in the launcher
- Uvicorn (implemented in
encode/uvicorn) handles the actual import inspection and ASGI wrapper creation
Alternative: Command-Line Execution
The programmatic approach above is equivalent to invoking uvicorn directly from the terminal:
uvicorn myapi:app --host 0.0.0.0 --port 8000 --reload
However, embedding uvicorn.run() in a Python file provides flexibility for packaging, debugging, and integrating with custom logging or configuration loading.
Summary
- Expose the app in a dedicated module (e.g.,
myapi.py) containing only theFastAPIinstance and routes. - Reference with a string using the
"module:variable"syntax inuvicorn.run()to enable lazy loading. - Guard the entry point with
if __name__ == "__main__":to prevent automatic server startup during imports. - ASGI compatibility is handled by the
FastAPIclass infastapi/applications.pyand the routing logic infastapi/routing.py.
Frequently Asked Questions
What is the exact format for the uvicorn import string?
The import string uses the format "module:variable", where module is the Python file name without the .py extension, and variable is the name of your FastAPI instance. For example, if your app is defined in myapi.py as app = FastAPI(), the string is "myapi:app". Uvicorn splits this string, imports the module, and retrieves the callable.
Can I pass the FastAPI instance directly to uvicorn.run instead of a string?
Yes, uvicorn.run() accepts either a string import path or the actual ASGI application object. However, using a string is recommended when calling from a launcher file to avoid importing the application module at the top level, which ensures cleaner separation of concerns and prevents side effects during import-time.
Why does my server start when I import the launcher module in tests?
This occurs when the uvicorn.run() call is not protected by if __name__ == "__main__":. According to the test suite patterns in fastapi/fastapi, the server should only start when the script is executed directly. Always wrap server startup calls in this guard to prevent activation during test imports or module discovery.
How do I enable hot reload programmatically with uvicorn run fastapi?
Pass reload=True to the uvicorn.run() function as shown in the launcher example. This parameter tells uvicorn to monitor the file system for changes and restart the server automatically. You can also specify reload_dirs to limit which directories trigger a reload.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →