Skip to content

Feature request: NestJS-style DatabaseModule for ORM integration #130

@ItayTheDar

Description

@ItayTheDar

Summary

Introduce a NestJS-style database integration module for PyNest, similar in spirit to NestJS dynamic modules like TypeOrmModule.forRoot(...) and TypeOrmModule.forFeature(...).

The current ORM integration encourages app code to create a global OrmProvider instance, usually in config.py, and import it directly from services. That works for small examples, but it does not feel very Nest-like and makes lifecycle, dependency injection, testing, and future repository patterns harder to model cleanly.

Current pattern

Typical app code today looks like this:

# src/config.py
config = OrmProvider(
    db_type="postgresql",
    config_params={...},
)

# service.py
from src.config import config

with config.get_session() as session:
    ...

Problems with this pattern:

  • Database configuration is a global object rather than an injected dependency.
  • Services import infrastructure directly instead of receiving dependencies through DI.
  • DB lifecycle has to be managed manually or by app-specific provider glue.
  • Sessions are manually opened in every service method.
  • It is hard to support multiple DB connections, test replacement providers, or a repository-oriented API later.
  • The app module does not clearly express that database infrastructure is part of the module graph.

Proposed direction

Add a framework-level DatabaseModule with a dynamic-module style API:

@Module(
    imports=[
        DatabaseModule.for_root(
            driver="postgresql",
            host="localhost",
            database="default_nest_db",
            user="postgres",
            password="postgres",
            port=5432,
        ),
        AuthorModule,
        BookModule,
    ],
)
class AppModule:
    pass

The module would register providers such as:

  • DATABASE_OPTIONS
  • DATABASE_ENGINE
  • DATABASE_SESSION_FACTORY
  • DatabaseService

DatabaseService would own lifecycle hooks:

@Injectable
class DatabaseService(OnModuleInit, OnModuleDestroy):
    def on_module_init(self):
        Base.metadata.create_all(bind=self.engine)

    def on_module_destroy(self):
        self.engine.dispose()

    def session(self):
        ...

App services would then receive the database dependency through DI:

@Injectable
class AuthorService:
    def __init__(self, db: DatabaseService):
        self.db = db

    def get_authors(self):
        with self.db.session() as session:
            return session.query(AuthorEntity).all()

Future extension

After DatabaseModule.for_root(...), add a repository-oriented API, similar to NestJS TypeOrmModule.forFeature(...):

@Module(
    imports=[DatabaseModule.for_feature([AuthorEntity])],
    providers=[AuthorService],
)
class AuthorModule:
    pass

Then services could receive repositories rather than raw sessions:

@Injectable
class AuthorService:
    def __init__(self, authors: Repository[AuthorEntity]):
        self.authors = authors

    def get_authors(self):
        return self.authors.find_all()

Suggested implementation phases

  1. Add DatabaseModule.for_root(...) and DatabaseService while preserving backward compatibility with OrmProvider.
  2. Update generated templates to prefer DatabaseModule.for_root(...) instead of global config = OrmProvider(...).
  3. Add tests for lifecycle behavior, DI replacement, and app startup/shutdown.
  4. Later, add DatabaseModule.for_feature(...) and repository injection.

Why this matters

This would make PyNest database integration feel much closer to NestJS:

  • database infrastructure becomes part of the module graph
  • configuration is explicit at the app-module boundary
  • lifecycle is owned by the database provider
  • services use dependency injection instead of global imports
  • testing becomes easier by overriding providers
  • the framework gets a clean path toward repository injection

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions