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 the myapi module and use the app variable
  • 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 the FastAPI instance and routes.
  • Reference with a string using the "module:variable" syntax in uvicorn.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 FastAPI class in fastapi/applications.py and the routing logic in fastapi/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →