Backend Development 7 min read

How to Build a Scalable Python Monorepo with UV: From Setup to Dependency Management

This tutorial shows how to use the UV tool to create a Python monorepo with separate API, core, and common library projects, manage dependencies with individual pyproject.toml files, and share reusable modules across FastAPI and other services.

Code Mala Tang
Code Mala Tang
Code Mala Tang
How to Build a Scalable Python Monorepo with UV: From Setup to Dependency Management

Previously I introduced UV , a Python package manager that does more than just install packages. Curious about its capabilities, I used UV to design a monorepo.

Repository Structure

When creating a monorepo I usually include three key projects:

API project : handles various APIs such as FastAPI REST API, gRPC, GraphQL, etc.

Core project : provides the core functionality of the monorepo.

Common library project : a shared library containing utilities, constants, and other common code.

This layout ensures scalability, better code organization, and improved dependency management.

Three Nested Projects

py-api

py-basecamp

py-zcommonlib

py-api and py-basecamp are independent and cannot import each other. Shared functionality should be placed in py-zcommonlib to keep the projects isolated.

Creating the Three Projects with UV

Run the following commands:

<code>uv init py-zcommonlib --lib
uv init py-api
uv init py-basecamp</code>

The --lib flag creates a src directory for Python modules; without it, UV defaults to --app , producing a flat structure.

Note: the flat structure is not wrong, just a different approach.

Dependency Management

Each project has its own pyproject.toml , allowing independent and clear dependency control.

Adding a Common Module in py-zcommonlib

Create datetime_lib.py with the following code:

<code>from datetime import datetime, timezone

def get_utc_timestamp():
    """Return the current UTC timestamp in ISO 8601 format.
    Example: '2025-02-15T12:34:56Z'"""
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

if __name__ == "__main__":
    print(get_utc_timestamp())</code>

Run it with UV:

<code>uv run src/py_zcommonlib/datetime_lib.py</code>

Using the Module in py-basecamp

Update main.py to import the function:

<code>from py_zcommonlib.datetime_lib import get_utc_timestamp

def main():
    print(get_utc_timestamp())

if __name__ == "__main__":
    main()</code>

The script fails because py-zcommonlib is not yet listed as a dependency of py-basecamp .

Updating py-basecamp pyproject.toml

Add py-zcommonlib as an editable dependency using a relative path:

<code>[project]
name = "py-basecamp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.9"
dependencies = ["py-zcommonlib"]

[tool.uv.sources]
py_zcommonlib = { path="../py-zcommonlib", editable=true }</code>

Run uv sync in the py-basecamp directory to install the dependency, then uv run main.py to test.

Understanding [tool.uv.sources]

The tool.uv.sources table extends the standard dependency list, allowing alternative sources such as editable installs and relative paths, which are not supported by project.dependencies .

Using the Module in py-api

In the FastAPI project, import and expose the timestamp endpoint:

<code>from fastapi import FastAPI
from py_zcommonlib.datetime_lib import get_utc_timestamp

app = FastAPI()

@app.get("/")
async def utc_timestamp():
    return get_utc_timestamp()</code>

Corresponding pyproject.toml for py-api :

<code>[project]
name = "py-api"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
    "fastapi>=0.115.11",
    "uvicorn>=0.34.0",
    "py-zcommonlib",
]

[tool.uv.sources]
py_zcommonlib = { path="../py-zcommonlib", editable=true }</code>
PythonMonorepodependency-managementfastapiPackage Managementuv
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.