Skip to main content

FastAPI modules loading for better structure in your projects (like Django AppConfig)

Project description

fastapi-module-loader

This library allows you to load modules in a structured and well defined way. The overall idea is explained pretty quick, we have a list of modules and then ensure all those are basically imported. Also we allow the module classes to execute additional code on loading. This is useful to for example resolve forward references with pydantic, to register some hooks or just load all SQLAlchemy ORM models so relationships work.

The module loader idea is based on what Django does using AppConfig's - so if you know those you might find this pretty straight forward.

Note: fastapi-module-loader is compatible with Python 3.10, 3.11, 3.12, 3.13 and 3.14. This is also ensured running all tests on all those versions using tox.

Example FastAPI setup

Lets say we are using the following folder structure in your project, modules may contain a list of modules used by your application:

yourapp
|-- modules
|   `-- something
|       `-- __init__.py     <- SomethingModule is defined here, see technical details below
|-- config.py               <- Your config file
|-- loader.py               <- This is where you put the global loader instance
`-- main.py                 <- Your normal FastAPI application lives here

Now lets put the MODULES list into config.py:

MODULES = [
    "yourapp.modules.something.SomethingModules",
]

Then add the loader to your loader.py:

from fastapi_module_loader import ModuleLoader

from yourapp.config import MODULES


loader = ModuleLoader(MODULES)

Note: We are NOT calling loader.load() or loader.setup() here. This is because we want to do this in our main.py file so we have control when this actually happens.

Then change your main.py to look like this:

from fastapi import FastAPI

from yourapp.loader import loader


@asynccontextmanager
async def lifespan(app: FastAPI):
    loader.setup()  # Setup everything on FastAPI startup
    yield


loader.load()  # Ensure everything is loaded
app = FastAPI(lifespan=lifespan)

# ...put the actual FastAPI code here

Use cases

Those are some use cases we encountered while working on projects. Those use cases led us to create this library:

  • main.py: Load the modules to ensure everything is setup for the FastAPI app.
  • In alembic migrations env.py: Ensure all orm.py are loaded and the alembic migrations can "see" all the ORM models. Otherwise, detecting any changes would not be possible at all.
    (see https://alembic.sqlalchemy.org/en/latest/tutorial.html#the-migration-environment)
  • To register any hooks bound to async-signals Signal instances.
    (see https://github.com/team23/async-signals)
  • There will be many additional use cases for using the module loader. If you have any interesting use case please open an issue and tell us about it. 😉

Technical details

Basic structure

The class ModuleLoader is the main entry point for the module loader system. It needs to be instantiated with a list of modules as strings. You have to initialize the module loader in some global place for your application. This can be done directly in main.py, although it is recommended to use a separate file for this like loader.py.

Getting you own loader instance can look like this:

from fastapi_module_loader import ModuleLoader


# We recommend putting the module list into your normal config file - if you have one
MODULES = [
    "yourapp.modules.something.SomethingModules",
    "yourapp.modules.something_else.SomethingElseModules",
    "yourapp.modules.etc.EtcModules",
]


loader = ModuleLoader(MODULES)

The actual loading process then works like this:

  1. Using loader.load() will import all the modules passed to the constructor. It will use importlib to load the module and then instantiate the module class. After this is done it will call the load() method on the modules - you can use this hook to load additional modules or similar.
  2. The loader.setup() will execute the setup() method of all the modules. This is where the modules should do additional setup work like resolving ForwardRef's or registering hooks.
    Note that loader.setup() will actually call pre_setup(), then setup() and finally post_setup() on the modules. This allows you to have different stages of your setup process.

Module structure

A module is a class that inherits from BaseModule. It may implement the setup() and other methods as hooks. All methods will be called on module loading/setup.

Example for a module:

from fastapi_module_loader import BaseModule


class SomethingModule(BaseModule):
    def setup(self):  # providing a setup method is optional
        print("Hello world!")

Loading process

loader.load() will import all the modules and then call the load() method on them. This is the place where you should load further modules if you need to.

from yourapp.loader import loader  # Import has to match your own setup


loader.load()  # will import all modules and call load() on each of them

When all modules are loaded you may use loader.setup() to execute the setup() method on all modules. This is the place where you should do your actual setup work.

from yourapp.loader import loader  # Import has to match your own setup


loader.setup()  # will call pre_setup(), setup() and then post_setup() on all modules

Loading further modules in load()

It is a very common use case to load further modules in the setup() method. To support this use case the BaseModule class provides a method load_in_module() then will load given module names inside the current module scope.

Example:

from fastapi_module_loader import BaseModule


class SomethingModule(BaseModule):
    def setup(self):
        self.load_in_module("orm")

This will load yourapp.modules.something.orm if the module file itself is placed in yourapp/modules/something/__init__.py.

See this example structure:

yourapp
`-- modules
    `-- something
        |-- __init__.py     <- SomethingModule is defined here
        |-- etc...py
        |-- orm.py          <- This is loaded
        `-- etc...py

Note: For load_in_module to work you are required to put the modules class (SomethingModule in this example) into the __init__.py file of the module. Otherwise the module loader will not be able to find the module you want it to load.

Integrating the module loader

To ensure everything is setup properly you should just call the methods mentioned above. This may for example look like this:

from fastapi_module_loader import loader


loader.load()
loader.setup()


# Put the actual code here

Exceptions

The module loader will raise ImproperlyConfiguredModules if anything went wrong during handling the modules and loading those, this includes:

  • A module could not be imported
  • A module does not inherit from BaseModule
  • You are calling loader.setup() when loader.load() has not been called before

Note however that all exceptions raised by the setup() method of the modules will just be passed through. The module loader will not catch now handle them. This is also the case if a modules to be loaded by Self.load_in_module() does not exist. Generally you have to handle those errors in setup() yourself.

Contributing

If you want to contribute to this project, feel free to just fork the project, create a dev branch in your fork and then create a pull request (PR). If you are unsure about whether your changes really suit the project please create an issue first, to talk about this.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_module_loader-0.5.1.tar.gz (5.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_module_loader-0.5.1-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_module_loader-0.5.1.tar.gz.

File metadata

  • Download URL: fastapi_module_loader-0.5.1.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_module_loader-0.5.1.tar.gz
Algorithm Hash digest
SHA256 507c924dfa7ea7610471907d78266d10f5f948a7e43f42563df18f463d94a0a0
MD5 2050879d95fc01d38dabdef43af8272d
BLAKE2b-256 f311031aeddd75015d66804ac52f46e89ab6b6cd479b4492b8a08e0f5d1307b8

See more details on using hashes here.

File details

Details for the file fastapi_module_loader-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: fastapi_module_loader-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_module_loader-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0b41601068f710481eddeaa196c0f106c1b95028407566202c846da6ef5e1556
MD5 06f21d09289bc95b3e1ecdff0b31fe63
BLAKE2b-256 6a5c858273d385f03bb20cdc5c60c3f8148a120317c86ee5078819a07c5b0a3c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page