fastapi-basekit 0.2.1__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_basekit-0.3.1/PKG-INFO +134 -0
- fastapi_basekit-0.3.1/README.md +88 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/repository/base.py +29 -5
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/__init__.py +2 -1
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/service/base.py +8 -1
- fastapi_basekit-0.3.1/fastapi_basekit/aio/sqlalchemy/session.py +67 -0
- fastapi_basekit-0.3.1/fastapi_basekit/cli/__init__.py +5 -0
- fastapi_basekit-0.3.1/fastapi_basekit/cli/main.py +112 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/cookiecutter.json +19 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/hooks/post_gen_project.py +51 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/hooks/pre_gen_project.py +32 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.env.example +40 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.gitignore +12 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Dockerfile +18 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/LICENSE +61 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Makefile +46 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/README.md +41 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/env.py +84 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/script.py.mako +26 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic.ini +45 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/__init__.py +3 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/auth.py +42 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/__init__.py +3 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/user.py +73 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/routers.py +11 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/__init__.py +3 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/database.py +107 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/settings.py +70 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/main.py +96 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/auth.py +78 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/permissions.py +12 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/__init__.py +10 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/auth.py +49 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/base.py +78 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/enums.py +8 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/types.py +56 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/user.py +26 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/repository.py +36 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/auth.py +20 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/base.py +13 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/user.py +42 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init.py +13 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init_admin.py +61 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/auth_service.py +102 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/dependency.py +70 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/user_service.py +46 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/__init__.py +0 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/exception_handlers.py +84 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/security.py +11 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/docker-compose.yml +88 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/pytest.ini +6 -0
- fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/requirements.txt +60 -0
- fastapi_basekit-0.3.1/fastapi_basekit.egg-info/PKG-INFO +134 -0
- fastapi_basekit-0.3.1/fastapi_basekit.egg-info/SOURCES.txt +112 -0
- fastapi_basekit-0.3.1/fastapi_basekit.egg-info/entry_points.txt +2 -0
- fastapi_basekit-0.3.1/fastapi_basekit.egg-info/requires.txt +34 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/pyproject.toml +32 -9
- fastapi_basekit-0.2.1/PKG-INFO +0 -775
- fastapi_basekit-0.2.1/README.md +0 -739
- fastapi_basekit-0.2.1/fastapi_basekit.egg-info/PKG-INFO +0 -775
- fastapi_basekit-0.2.1/fastapi_basekit.egg-info/SOURCES.txt +0 -53
- fastapi_basekit-0.2.1/fastapi_basekit.egg-info/requires.txt +0 -22
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/LICENSE +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/controller/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/controller/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/repository/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/service/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/service/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/controller/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/controller/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/permissions/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/permissions/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/controller/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/controller/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/repository/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/repository/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/service/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/controller/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/controller/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/repository/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/repository/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/service/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlmodel/service/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/exceptions/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/exceptions/api_exceptions.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/exceptions/handler.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/schema/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/schema/base.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/schema/jwt.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/schema/schema.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/servicios/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/servicios/thrid/__init__.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/servicios/thrid/jwt.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit.egg-info/dependency_links.txt +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit.egg-info/top_level.txt +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/setup.cfg +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_api_exceptions.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_base_response.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_base_service.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_controller_auto_permissions.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_crud_beanie_controller.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_crud_controller.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_jwt_service.py +0 -0
- {fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/tests/test_sqlalchemy_base_service_order.py +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: fastapi-basekit
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Utilities and base classes for FastAPI async projects (Beanie, SQLAlchemy or SQLModel)
|
|
5
|
+
Author-email: Jerson Moreno <jerson.ml820@hotmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mundobien2025/fastapi-basekit
|
|
8
|
+
Project-URL: Repository, https://github.com/mundobien2025/fastapi-basekit
|
|
9
|
+
Project-URL: Issues, https://github.com/mundobien2025/fastapi-basekit/issues
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Framework :: FastAPI
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: fastapi>=0.116.1
|
|
19
|
+
Requires-Dist: pydantic<3,>=2.13.0
|
|
20
|
+
Requires-Dist: fastapi-restful[all]>=0.6.0
|
|
21
|
+
Requires-Dist: pyjwt>=2.12.1
|
|
22
|
+
Provides-Extra: beanie
|
|
23
|
+
Requires-Dist: beanie<3,>=2.0; extra == "beanie"
|
|
24
|
+
Provides-Extra: sqlalchemy
|
|
25
|
+
Requires-Dist: SQLAlchemy[asyncio]<3,>=2.0.30; extra == "sqlalchemy"
|
|
26
|
+
Requires-Dist: psycopg2>=2.9.0; extra == "sqlalchemy"
|
|
27
|
+
Provides-Extra: sqlmodel
|
|
28
|
+
Requires-Dist: sqlmodel>=0.0.37; extra == "sqlmodel"
|
|
29
|
+
Provides-Extra: init
|
|
30
|
+
Requires-Dist: cookiecutter>=2.5.0; extra == "init"
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
|
|
33
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
34
|
+
Requires-Dist: mkdocstrings[python]>=0.27.0; extra == "docs"
|
|
35
|
+
Requires-Dist: pymdown-extensions>=10.10; extra == "docs"
|
|
36
|
+
Requires-Dist: mike>=2.1.0; extra == "docs"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: beanie<3,>=2.0; extra == "all"
|
|
39
|
+
Requires-Dist: SQLAlchemy[asyncio]<3,>=2.0.30; extra == "all"
|
|
40
|
+
Requires-Dist: psycopg2>=2.9.0; extra == "all"
|
|
41
|
+
Requires-Dist: sqlmodel>=0.0.37; extra == "all"
|
|
42
|
+
Requires-Dist: cookiecutter>=2.5.0; extra == "all"
|
|
43
|
+
Requires-Dist: mkdocs>=1.6.0; extra == "all"
|
|
44
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "all"
|
|
45
|
+
Requires-Dist: mkdocstrings[python]>=0.27.0; extra == "all"
|
|
46
|
+
|
|
47
|
+
<div align="center">
|
|
48
|
+
|
|
49
|
+
<img src="docs/assets/logo-mark.svg" width="120" alt="fastapi-basekit">
|
|
50
|
+
|
|
51
|
+
# fastapi-basekit
|
|
52
|
+
|
|
53
|
+
**FastAPI toolkit con repos, services y controllers listos para producción.**
|
|
54
|
+
**Multi-ORM · CLI scaffolder · plugin Claude Code.**
|
|
55
|
+
|
|
56
|
+
[](https://pypi.org/project/fastapi-basekit/)
|
|
57
|
+
[](https://pypi.org/project/fastapi-basekit/)
|
|
58
|
+
[](https://fastapi.tiangolo.com/)
|
|
59
|
+
[](https://www.sqlalchemy.org/)
|
|
60
|
+
[](https://beanie-odm.dev/)
|
|
61
|
+
[](./LICENSE)
|
|
62
|
+
|
|
63
|
+
[**Docs**](https://mundobien2025.github.io/fastapi-basekit) ·
|
|
64
|
+
[**PyPI**](https://pypi.org/project/fastapi-basekit/) ·
|
|
65
|
+
[**Issues**](https://github.com/mundobien2025/fastapi-basekit/issues) ·
|
|
66
|
+
[**Changelog**](./CHANGELOG.md)
|
|
67
|
+
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install fastapi-basekit[init]
|
|
76
|
+
basekit init # cookiecutter prompts
|
|
77
|
+
cd <project_slug>
|
|
78
|
+
cp .env.example .env
|
|
79
|
+
make up-d
|
|
80
|
+
make migrate-create && make migrate-up
|
|
81
|
+
make seed
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Abre http://localhost:8000/docs. Login con `admin@example.com` / `ChangeMe2026!`.
|
|
85
|
+
|
|
86
|
+
## ¿Qué incluye?
|
|
87
|
+
|
|
88
|
+
- **Repository / Service / Controller** base async para SQLAlchemy 2.0, SQLModel y Beanie
|
|
89
|
+
- **Paginación, filtrado, búsqueda, ordenamiento** vía query string out-of-the-box
|
|
90
|
+
- **JWT middleware** + `BasePermission` classes + soft-delete
|
|
91
|
+
- **`basekit init`** — scaffolder cookiecutter (multi-ORM, multi-DB, redis, s3, license)
|
|
92
|
+
- **Plugin Claude Code** — la skill `fastapi-basekit-crud` enseña el patrón a Claude
|
|
93
|
+
|
|
94
|
+
## Documentación
|
|
95
|
+
|
|
96
|
+
**[mundobien2025.github.io/fastapi-basekit](https://mundobien2025.github.io/fastapi-basekit)**
|
|
97
|
+
|
|
98
|
+
| Sección | Contenido |
|
|
99
|
+
|---|---|
|
|
100
|
+
| [Primeros pasos](https://mundobien2025.github.io/fastapi-basekit/getting-started/installation) | Instalación, `basekit init`, primer CRUD |
|
|
101
|
+
| [Guía de usuario](https://mundobien2025.github.io/fastapi-basekit/user-guide/controllers) | Controllers, services, repositories, paginación, filtros |
|
|
102
|
+
| [Avanzado](https://mundobien2025.github.io/fastapi-basekit/advanced/permissions) | Permisos, soft-delete, logging, performance |
|
|
103
|
+
| [Referencia API](https://mundobien2025.github.io/fastapi-basekit/api-reference/base-controller) | Docstrings de `BaseController`, `BaseService`, `BaseRepository`, `Schemas` |
|
|
104
|
+
| [Ejemplos](https://mundobien2025.github.io/fastapi-basekit/examples/basic-crud) | CRUD básico, filtros complejos, relaciones, auth |
|
|
105
|
+
|
|
106
|
+
## Como plugin de Claude Code
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
/plugin marketplace add https://github.com/mundobien2025/fastapi-basekit
|
|
110
|
+
/plugin install fastapi-basekit
|
|
111
|
+
/plugin list
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Luego pide: *"Crea el recurso `Invoice` con CRUD completo"* — Claude usa la skill automáticamente.
|
|
115
|
+
|
|
116
|
+
## Instalación por ORM
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install fastapi-basekit[sqlalchemy] # Postgres / MySQL / SQLite
|
|
120
|
+
pip install fastapi-basekit[beanie] # MongoDB
|
|
121
|
+
pip install fastapi-basekit[sqlmodel]
|
|
122
|
+
pip install fastapi-basekit[init] # solo scaffolder
|
|
123
|
+
pip install fastapi-basekit[all] # todo
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Contribuir
|
|
127
|
+
|
|
128
|
+
PRs bienvenidos. Setup local en [docs/contributing](https://mundobien2025.github.io/fastapi-basekit/contributing).
|
|
129
|
+
|
|
130
|
+
Mantenedores: [`RELEASING.md`](./RELEASING.md) tiene release flow, CI/CD, mike y troubleshooting.
|
|
131
|
+
|
|
132
|
+
## Licencia
|
|
133
|
+
|
|
134
|
+
[MIT](./LICENSE) — © Jerson Moreno
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="docs/assets/logo-mark.svg" width="120" alt="fastapi-basekit">
|
|
4
|
+
|
|
5
|
+
# fastapi-basekit
|
|
6
|
+
|
|
7
|
+
**FastAPI toolkit con repos, services y controllers listos para producción.**
|
|
8
|
+
**Multi-ORM · CLI scaffolder · plugin Claude Code.**
|
|
9
|
+
|
|
10
|
+
[](https://pypi.org/project/fastapi-basekit/)
|
|
11
|
+
[](https://pypi.org/project/fastapi-basekit/)
|
|
12
|
+
[](https://fastapi.tiangolo.com/)
|
|
13
|
+
[](https://www.sqlalchemy.org/)
|
|
14
|
+
[](https://beanie-odm.dev/)
|
|
15
|
+
[](./LICENSE)
|
|
16
|
+
|
|
17
|
+
[**Docs**](https://mundobien2025.github.io/fastapi-basekit) ·
|
|
18
|
+
[**PyPI**](https://pypi.org/project/fastapi-basekit/) ·
|
|
19
|
+
[**Issues**](https://github.com/mundobien2025/fastapi-basekit/issues) ·
|
|
20
|
+
[**Changelog**](./CHANGELOG.md)
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install fastapi-basekit[init]
|
|
30
|
+
basekit init # cookiecutter prompts
|
|
31
|
+
cd <project_slug>
|
|
32
|
+
cp .env.example .env
|
|
33
|
+
make up-d
|
|
34
|
+
make migrate-create && make migrate-up
|
|
35
|
+
make seed
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Abre http://localhost:8000/docs. Login con `admin@example.com` / `ChangeMe2026!`.
|
|
39
|
+
|
|
40
|
+
## ¿Qué incluye?
|
|
41
|
+
|
|
42
|
+
- **Repository / Service / Controller** base async para SQLAlchemy 2.0, SQLModel y Beanie
|
|
43
|
+
- **Paginación, filtrado, búsqueda, ordenamiento** vía query string out-of-the-box
|
|
44
|
+
- **JWT middleware** + `BasePermission` classes + soft-delete
|
|
45
|
+
- **`basekit init`** — scaffolder cookiecutter (multi-ORM, multi-DB, redis, s3, license)
|
|
46
|
+
- **Plugin Claude Code** — la skill `fastapi-basekit-crud` enseña el patrón a Claude
|
|
47
|
+
|
|
48
|
+
## Documentación
|
|
49
|
+
|
|
50
|
+
**[mundobien2025.github.io/fastapi-basekit](https://mundobien2025.github.io/fastapi-basekit)**
|
|
51
|
+
|
|
52
|
+
| Sección | Contenido |
|
|
53
|
+
|---|---|
|
|
54
|
+
| [Primeros pasos](https://mundobien2025.github.io/fastapi-basekit/getting-started/installation) | Instalación, `basekit init`, primer CRUD |
|
|
55
|
+
| [Guía de usuario](https://mundobien2025.github.io/fastapi-basekit/user-guide/controllers) | Controllers, services, repositories, paginación, filtros |
|
|
56
|
+
| [Avanzado](https://mundobien2025.github.io/fastapi-basekit/advanced/permissions) | Permisos, soft-delete, logging, performance |
|
|
57
|
+
| [Referencia API](https://mundobien2025.github.io/fastapi-basekit/api-reference/base-controller) | Docstrings de `BaseController`, `BaseService`, `BaseRepository`, `Schemas` |
|
|
58
|
+
| [Ejemplos](https://mundobien2025.github.io/fastapi-basekit/examples/basic-crud) | CRUD básico, filtros complejos, relaciones, auth |
|
|
59
|
+
|
|
60
|
+
## Como plugin de Claude Code
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
/plugin marketplace add https://github.com/mundobien2025/fastapi-basekit
|
|
64
|
+
/plugin install fastapi-basekit
|
|
65
|
+
/plugin list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Luego pide: *"Crea el recurso `Invoice` con CRUD completo"* — Claude usa la skill automáticamente.
|
|
69
|
+
|
|
70
|
+
## Instalación por ORM
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install fastapi-basekit[sqlalchemy] # Postgres / MySQL / SQLite
|
|
74
|
+
pip install fastapi-basekit[beanie] # MongoDB
|
|
75
|
+
pip install fastapi-basekit[sqlmodel]
|
|
76
|
+
pip install fastapi-basekit[init] # solo scaffolder
|
|
77
|
+
pip install fastapi-basekit[all] # todo
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Contribuir
|
|
81
|
+
|
|
82
|
+
PRs bienvenidos. Setup local en [docs/contributing](https://mundobien2025.github.io/fastapi-basekit/contributing).
|
|
83
|
+
|
|
84
|
+
Mantenedores: [`RELEASING.md`](./RELEASING.md) tiene release flow, CI/CD, mike y troubleshooting.
|
|
85
|
+
|
|
86
|
+
## Licencia
|
|
87
|
+
|
|
88
|
+
[MIT](./LICENSE) — © Jerson Moreno
|
{fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/beanie/repository/base.py
RENAMED
|
@@ -2,9 +2,9 @@ from typing import Any, Dict, List, Optional, Type, Union
|
|
|
2
2
|
from typing import get_args, get_origin
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from bson import ObjectId
|
|
5
|
+
from bson import ObjectId
|
|
6
6
|
from pydantic import BaseModel
|
|
7
|
-
from beanie import Document
|
|
7
|
+
from beanie import Document, Link
|
|
8
8
|
from beanie.odm.queries.find import FindMany
|
|
9
9
|
from beanie.operators import Or, RegEx
|
|
10
10
|
|
|
@@ -154,18 +154,42 @@ class BaseRepository:
|
|
|
154
154
|
|
|
155
155
|
raw_filters: Dict[str, Any] = {}
|
|
156
156
|
|
|
157
|
+
def _coerce_objectid(value: Any) -> Any:
|
|
158
|
+
"""str/UUID → ObjectId para queries Mongo sobre Link.id."""
|
|
159
|
+
if isinstance(value, ObjectId):
|
|
160
|
+
return value
|
|
161
|
+
if isinstance(value, str):
|
|
162
|
+
try:
|
|
163
|
+
return ObjectId(value)
|
|
164
|
+
except Exception:
|
|
165
|
+
return value
|
|
166
|
+
return value
|
|
167
|
+
|
|
157
168
|
for k, v in (filters or {}).items():
|
|
158
169
|
# MongoDB-style keys (dot-notation like "user.$id" or operators like "$or")
|
|
159
170
|
# cannot be resolved via hasattr — pass them as a raw dict to find()
|
|
160
171
|
if "." in k or k.startswith("$"):
|
|
161
172
|
raw_filters[k] = v
|
|
162
|
-
|
|
163
|
-
field_attr = getattr(self.model, k)
|
|
173
|
+
continue
|
|
164
174
|
|
|
175
|
+
if hasattr(self.model, k):
|
|
176
|
+
field_attr = getattr(self.model, k)
|
|
165
177
|
if _is_link_field(k):
|
|
166
|
-
exprs.append(field_attr.id == v)
|
|
178
|
+
exprs.append(field_attr.id == _coerce_objectid(v))
|
|
167
179
|
else:
|
|
168
180
|
exprs.append(field_attr == v)
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
# `<field>_id` alias para Link fields. Permite filtros con
|
|
184
|
+
# `customer_id`, `user_id`, etc. sin que el caller deba conocer
|
|
185
|
+
# la sintaxis Mongo nested. Sólo se activa si <field> existe en
|
|
186
|
+
# el modelo y es un Link[X].
|
|
187
|
+
if k.endswith("_id"):
|
|
188
|
+
base = k[:-3]
|
|
189
|
+
if hasattr(self.model, base) and _is_link_field(base):
|
|
190
|
+
field_attr = getattr(self.model, base)
|
|
191
|
+
exprs.append(field_attr.id == _coerce_objectid(v))
|
|
192
|
+
continue
|
|
169
193
|
|
|
170
194
|
# Raw MongoDB filters go first so Beanie processes them as a dict condition
|
|
171
195
|
query_args: list = ([raw_filters] if raw_filters else []) + exprs
|
{fastapi_basekit-0.2.1 → fastapi_basekit-0.3.1}/fastapi_basekit/aio/sqlalchemy/service/base.py
RENAMED
|
@@ -10,7 +10,14 @@ from ....exceptions.api_exceptions import (
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class BaseService:
|
|
13
|
-
"""Servicio base para SQLAlchemy AsyncSession.
|
|
13
|
+
"""Servicio base para SQLAlchemy AsyncSession.
|
|
14
|
+
|
|
15
|
+
Regla del proyecto: los servicios NO deben llamar `session.flush()`,
|
|
16
|
+
`session.commit()` ni `session.refresh()`. El flush vive en
|
|
17
|
+
`BaseRepository.create / update`; el commit/rollback único por
|
|
18
|
+
request lo gestiona el lifecycle creado con
|
|
19
|
+
`fastapi_basekit.aio.sqlalchemy.make_session_lifecycle`.
|
|
20
|
+
"""
|
|
14
21
|
|
|
15
22
|
repository: BaseRepository
|
|
16
23
|
search_fields: List[str] = []
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Reusable async SQLAlchemy session lifecycle.
|
|
2
|
+
|
|
3
|
+
Single commit / single rollback per request. Optional hooks fire after
|
|
4
|
+
commit (success) or rollback (error) so callers can attach logging,
|
|
5
|
+
metrics, sentry, retries, etc. without coupling the lifecycle to any
|
|
6
|
+
specific telemetry stack.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
|
|
10
|
+
from fastapi_basekit.aio.sqlalchemy.session import make_session_lifecycle
|
|
11
|
+
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
|
12
|
+
|
|
13
|
+
engine = create_async_engine(DATABASE_URL)
|
|
14
|
+
SessionFactory = async_sessionmaker(engine, autoflush=False, expire_on_commit=False)
|
|
15
|
+
|
|
16
|
+
async def on_error(exc, session):
|
|
17
|
+
logger.exception("rollback", exc_info=exc)
|
|
18
|
+
|
|
19
|
+
get_db = make_session_lifecycle(SessionFactory, on_error=on_error)
|
|
20
|
+
|
|
21
|
+
# then in routes: ... session: AsyncSession = Depends(get_db)
|
|
22
|
+
|
|
23
|
+
Rule of thumb for callers:
|
|
24
|
+
- Services SHOULD NOT call session.flush / session.commit / session.refresh.
|
|
25
|
+
- Repositories own flush via BaseRepository.create / update.
|
|
26
|
+
- get_db owns the single commit (success) and rollback (error).
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable
|
|
32
|
+
from typing import Optional
|
|
33
|
+
|
|
34
|
+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
35
|
+
|
|
36
|
+
ErrorHook = Callable[[Exception, AsyncSession], Awaitable[None]]
|
|
37
|
+
SuccessHook = Callable[[AsyncSession], Awaitable[None]]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def make_session_lifecycle(
|
|
41
|
+
session_factory: async_sessionmaker[AsyncSession],
|
|
42
|
+
*,
|
|
43
|
+
on_success: Optional[SuccessHook] = None,
|
|
44
|
+
on_error: Optional[ErrorHook] = None,
|
|
45
|
+
) -> Callable[[], AsyncGenerator[AsyncSession, None]]:
|
|
46
|
+
"""Return an async generator dependency that owns the request session.
|
|
47
|
+
|
|
48
|
+
The returned callable yields a session, commits on success, rolls back
|
|
49
|
+
on error, and invokes optional hooks. Hooks must not raise — wrap them
|
|
50
|
+
in try/except at the call site if their failure should not poison the
|
|
51
|
+
request.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
55
|
+
async with session_factory() as session:
|
|
56
|
+
try:
|
|
57
|
+
yield session
|
|
58
|
+
await session.commit()
|
|
59
|
+
if on_success is not None:
|
|
60
|
+
await on_success(session)
|
|
61
|
+
except Exception as exc:
|
|
62
|
+
await session.rollback()
|
|
63
|
+
if on_error is not None:
|
|
64
|
+
await on_error(exc, session)
|
|
65
|
+
raise
|
|
66
|
+
|
|
67
|
+
return get_db
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""basekit CLI entry point.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
basekit init # interactive cookiecutter prompts
|
|
5
|
+
basekit init --no-input # use defaults (good for CI / testing)
|
|
6
|
+
basekit init -o /path # output dir
|
|
7
|
+
basekit init --extra-context key=value
|
|
8
|
+
basekit version
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
from importlib import metadata
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Sequence
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _template_dir() -> Path:
|
|
21
|
+
return Path(__file__).resolve().parent.parent / "templates" / "project"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _cmd_init(args: argparse.Namespace) -> int:
|
|
25
|
+
try:
|
|
26
|
+
from cookiecutter.main import cookiecutter
|
|
27
|
+
except ImportError:
|
|
28
|
+
print(
|
|
29
|
+
"Error: cookiecutter not installed. Install with:\n"
|
|
30
|
+
" pip install fastapi-basekit[init]\n"
|
|
31
|
+
"or pip install cookiecutter",
|
|
32
|
+
file=sys.stderr,
|
|
33
|
+
)
|
|
34
|
+
return 1
|
|
35
|
+
|
|
36
|
+
extra_context: dict[str, str] = {}
|
|
37
|
+
for kv in args.extra_context or []:
|
|
38
|
+
if "=" not in kv:
|
|
39
|
+
print(f"Bad --extra-context value (expected key=value): {kv}", file=sys.stderr)
|
|
40
|
+
return 2
|
|
41
|
+
k, v = kv.split("=", 1)
|
|
42
|
+
extra_context[k.strip()] = v.strip()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
cookiecutter(
|
|
46
|
+
template=str(_template_dir()),
|
|
47
|
+
no_input=args.no_input,
|
|
48
|
+
output_dir=args.output_dir,
|
|
49
|
+
extra_context=extra_context,
|
|
50
|
+
overwrite_if_exists=args.overwrite,
|
|
51
|
+
)
|
|
52
|
+
except Exception as exc: # pragma: no cover - cookiecutter raises various
|
|
53
|
+
print(f"basekit init failed: {exc}", file=sys.stderr)
|
|
54
|
+
return 3
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _cmd_version(_: argparse.Namespace) -> int:
|
|
59
|
+
try:
|
|
60
|
+
v = metadata.version("fastapi-basekit")
|
|
61
|
+
except metadata.PackageNotFoundError: # pragma: no cover
|
|
62
|
+
v = "unknown"
|
|
63
|
+
print(f"fastapi-basekit {v}")
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
68
|
+
parser = argparse.ArgumentParser(
|
|
69
|
+
prog="basekit",
|
|
70
|
+
description="fastapi-basekit project scaffolder.",
|
|
71
|
+
)
|
|
72
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
73
|
+
|
|
74
|
+
init = sub.add_parser("init", help="Scaffold a new project from the basekit template.")
|
|
75
|
+
init.add_argument(
|
|
76
|
+
"-o",
|
|
77
|
+
"--output-dir",
|
|
78
|
+
default=".",
|
|
79
|
+
help="Where to write the new project (default: current directory).",
|
|
80
|
+
)
|
|
81
|
+
init.add_argument(
|
|
82
|
+
"--no-input",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Skip interactive prompts; use cookiecutter.json defaults.",
|
|
85
|
+
)
|
|
86
|
+
init.add_argument(
|
|
87
|
+
"--overwrite",
|
|
88
|
+
action="store_true",
|
|
89
|
+
help="Overwrite the output directory if it already exists.",
|
|
90
|
+
)
|
|
91
|
+
init.add_argument(
|
|
92
|
+
"--extra-context",
|
|
93
|
+
action="append",
|
|
94
|
+
metavar="KEY=VALUE",
|
|
95
|
+
help="Override one cookiecutter variable; repeatable.",
|
|
96
|
+
)
|
|
97
|
+
init.set_defaults(func=_cmd_init)
|
|
98
|
+
|
|
99
|
+
version = sub.add_parser("version", help="Print the installed version.")
|
|
100
|
+
version.set_defaults(func=_cmd_version)
|
|
101
|
+
|
|
102
|
+
return parser
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
106
|
+
parser = build_parser()
|
|
107
|
+
args = parser.parse_args(argv)
|
|
108
|
+
return args.func(args)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__": # pragma: no cover
|
|
112
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project_name": "My FastAPI Service",
|
|
3
|
+
"project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_') }}",
|
|
4
|
+
"package_name": "{{ cookiecutter.project_slug }}",
|
|
5
|
+
"description": "Backend API built with fastapi-basekit",
|
|
6
|
+
"author_name": "Your Name",
|
|
7
|
+
"author_email": "you@example.com",
|
|
8
|
+
"python_version": "3.12",
|
|
9
|
+
"orm": ["sqlalchemy", "beanie"],
|
|
10
|
+
"database": ["postgres", "mariadb", "sqlite", "mongodb"],
|
|
11
|
+
"server": ["uvicorn", "gunicorn"],
|
|
12
|
+
"cache": ["none", "redis"],
|
|
13
|
+
"background_tasks": ["none", "arq"],
|
|
14
|
+
"bucket": ["none", "s3"],
|
|
15
|
+
"include_alembic": ["yes", "no"],
|
|
16
|
+
"license": ["MIT", "Apache-2.0", "GPL-3.0", "Proprietary"],
|
|
17
|
+
"include_docker": ["yes", "no"],
|
|
18
|
+
"_extensions": ["jinja2.ext.do"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Cleanup files that aren't needed for the chosen options."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
ORM = "{{ cookiecutter.orm }}"
|
|
8
|
+
DB = "{{ cookiecutter.database }}"
|
|
9
|
+
INCLUDE_ALEMBIC = "{{ cookiecutter.include_alembic }}" == "yes"
|
|
10
|
+
INCLUDE_DOCKER = "{{ cookiecutter.include_docker }}" == "yes"
|
|
11
|
+
CACHE = "{{ cookiecutter.cache }}"
|
|
12
|
+
BUCKET = "{{ cookiecutter.bucket }}"
|
|
13
|
+
|
|
14
|
+
PROJECT_DIR = Path(os.getcwd())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def rm(path: str) -> None:
|
|
18
|
+
p = PROJECT_DIR / path
|
|
19
|
+
if p.is_dir():
|
|
20
|
+
shutil.rmtree(p, ignore_errors=True)
|
|
21
|
+
elif p.exists():
|
|
22
|
+
p.unlink()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# 1. Beanie projects don't use alembic
|
|
26
|
+
if ORM == "beanie" or not INCLUDE_ALEMBIC:
|
|
27
|
+
rm("alembic")
|
|
28
|
+
rm("alembic.ini")
|
|
29
|
+
|
|
30
|
+
# 2. Docker is opt-out
|
|
31
|
+
if not INCLUDE_DOCKER:
|
|
32
|
+
rm("docker-compose.yml")
|
|
33
|
+
rm("Dockerfile")
|
|
34
|
+
|
|
35
|
+
# 3. Print next steps
|
|
36
|
+
print(
|
|
37
|
+
f"\n✓ Project scaffolded at {PROJECT_DIR}\n"
|
|
38
|
+
f" ORM : {ORM}\n"
|
|
39
|
+
f" Database : {DB}\n"
|
|
40
|
+
f" Cache : {CACHE}\n"
|
|
41
|
+
f" Bucket : {BUCKET}\n"
|
|
42
|
+
f" Alembic : {'yes' if INCLUDE_ALEMBIC and ORM != 'beanie' else 'no'}\n"
|
|
43
|
+
f" Docker : {'yes' if INCLUDE_DOCKER else 'no'}\n"
|
|
44
|
+
f"\nNext steps:\n"
|
|
45
|
+
f" cd {PROJECT_DIR.name}\n"
|
|
46
|
+
f" cp .env.example .env # edit values\n"
|
|
47
|
+
+ (" make up # start containers\n" if INCLUDE_DOCKER else " pip install -r requirements.txt\n uvicorn app.main:app --reload\n")
|
|
48
|
+
+ (f" make migrate-create # generate baseline alembic\n make migrate-up\n" if ORM != 'beanie' and INCLUDE_ALEMBIC else "")
|
|
49
|
+
+ f" make seed # seed admin user\n"
|
|
50
|
+
f"\nVisit http://localhost:8000/docs\n"
|
|
51
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Validate cookiecutter inputs before scaffolding."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
slug = "{{ cookiecutter.project_slug }}"
|
|
7
|
+
orm = "{{ cookiecutter.orm }}"
|
|
8
|
+
db = "{{ cookiecutter.database }}"
|
|
9
|
+
|
|
10
|
+
# 1. Slug must be a valid Python identifier
|
|
11
|
+
if not re.match(r"^[a-z][a-z0-9_]*$", slug):
|
|
12
|
+
print(
|
|
13
|
+
f"ERROR: project_slug '{slug}' is invalid. "
|
|
14
|
+
"Must start with a lowercase letter and contain only [a-z0-9_].",
|
|
15
|
+
file=sys.stderr,
|
|
16
|
+
)
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
# 2. ORM ↔ database compatibility
|
|
20
|
+
if orm == "beanie" and db != "mongodb":
|
|
21
|
+
print(
|
|
22
|
+
f"ERROR: orm=beanie requires database=mongodb (got '{db}').",
|
|
23
|
+
file=sys.stderr,
|
|
24
|
+
)
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
|
|
27
|
+
if orm in ("sqlalchemy", "sqlmodel") and db == "mongodb":
|
|
28
|
+
print(
|
|
29
|
+
f"ERROR: orm={orm} requires a SQL database (postgres/mariadb/sqlite), got '{db}'.",
|
|
30
|
+
file=sys.stderr,
|
|
31
|
+
)
|
|
32
|
+
sys.exit(1)
|
fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.env.example
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
PROJECT_NAME={{ cookiecutter.project_name }}
|
|
2
|
+
VERSION=0.1.0
|
|
3
|
+
DEBUG=true
|
|
4
|
+
ENVIRONMENT=local
|
|
5
|
+
|
|
6
|
+
SECRET_KEY=change-me-in-production
|
|
7
|
+
JWT_SECRET=change-me-in-production
|
|
8
|
+
JWT_ALGORITHM=HS256
|
|
9
|
+
JWT_EXPIRE_SECONDS=3600
|
|
10
|
+
ACCESS_TOKEN_EXPIRE_MINUTES=60
|
|
11
|
+
REFRESH_TOKEN_EXPIRE_DAYS=7
|
|
12
|
+
|
|
13
|
+
{% if cookiecutter.orm == "beanie" -%}
|
|
14
|
+
DATABASE_URL=mongodb://root:secret@db:27017
|
|
15
|
+
DATABASE_NAME={{ cookiecutter.package_name }}
|
|
16
|
+
{%- elif cookiecutter.database == "postgres" -%}
|
|
17
|
+
DATABASE_URL=postgresql+asyncpg://{{ cookiecutter.package_name }}:secret@db:5432/{{ cookiecutter.package_name }}
|
|
18
|
+
{%- elif cookiecutter.database == "mariadb" -%}
|
|
19
|
+
DATABASE_URL=mysql+aiomysql://{{ cookiecutter.package_name }}:secret@db:3306/{{ cookiecutter.package_name }}
|
|
20
|
+
{%- elif cookiecutter.database == "sqlite" -%}
|
|
21
|
+
DATABASE_URL=sqlite+aiosqlite:///./{{ cookiecutter.package_name }}.db
|
|
22
|
+
{%- endif %}
|
|
23
|
+
|
|
24
|
+
{% if cookiecutter.cache == "redis" -%}
|
|
25
|
+
REDIS_HOST=redis
|
|
26
|
+
REDIS_PORT=6379
|
|
27
|
+
REDIS_DB=0
|
|
28
|
+
REDIS_PASSWORD=
|
|
29
|
+
{%- endif %}
|
|
30
|
+
|
|
31
|
+
{% if cookiecutter.bucket == "s3" -%}
|
|
32
|
+
AWS_ACCESS_KEY_ID=
|
|
33
|
+
AWS_SECRET_ACCESS_KEY=
|
|
34
|
+
AWS_REGION_NAME=us-east-1
|
|
35
|
+
AWS_S3_BUCKET_NAME=
|
|
36
|
+
{%- endif %}
|
|
37
|
+
|
|
38
|
+
ADMIN_EMAIL=admin@example.com
|
|
39
|
+
ADMIN_PASSWORD=ChangeMe2026!
|
|
40
|
+
ADMIN_NAME=Platform Admin
|
fastapi_basekit-0.3.1/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
FROM python:{{ cookiecutter.python_version }}-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
6
|
+
build-essential \
|
|
7
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
8
|
+
|
|
9
|
+
COPY requirements.txt .
|
|
10
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
11
|
+
|
|
12
|
+
COPY . .
|
|
13
|
+
|
|
14
|
+
{% if cookiecutter.server == "gunicorn" -%}
|
|
15
|
+
CMD ["gunicorn", "app.main:app", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "--workers", "4"]
|
|
16
|
+
{%- else -%}
|
|
17
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
18
|
+
{%- endif %}
|