fanest 0.1.0__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.
- fanest-0.1.0/.gitignore +11 -0
- fanest-0.1.0/LICENSE +21 -0
- fanest-0.1.0/PKG-INFO +608 -0
- fanest-0.1.0/README.md +564 -0
- fanest-0.1.0/benchmarks/benchmark_test.py +61 -0
- fanest-0.1.0/examples/all_uses/__init__.py +1 -0
- fanest-0.1.0/examples/all_uses/main.py +249 -0
- fanest-0.1.0/examples/basic/__init__.py +1 -0
- fanest-0.1.0/examples/basic/main.py +25 -0
- fanest-0.1.0/pyproject.toml +87 -0
- fanest-0.1.0/src/fanest/__init__.py +205 -0
- fanest-0.1.0/src/fanest/auth/__init__.py +28 -0
- fanest-0.1.0/src/fanest/auth/jwt.py +194 -0
- fanest-0.1.0/src/fanest/auth/passport.py +55 -0
- fanest-0.1.0/src/fanest/cache/__init__.py +25 -0
- fanest-0.1.0/src/fanest/cache/module.py +140 -0
- fanest-0.1.0/src/fanest/cli/__init__.py +1 -0
- fanest-0.1.0/src/fanest/cli/main.py +666 -0
- fanest-0.1.0/src/fanest/common/__init__.py +146 -0
- fanest-0.1.0/src/fanest/common/decorators.py +284 -0
- fanest-0.1.0/src/fanest/common/exceptions.py +76 -0
- fanest-0.1.0/src/fanest/common/middleware.py +120 -0
- fanest-0.1.0/src/fanest/common/pipes.py +158 -0
- fanest-0.1.0/src/fanest/common/responses.py +57 -0
- fanest-0.1.0/src/fanest/common/serialization.py +67 -0
- fanest-0.1.0/src/fanest/config/__init__.py +3 -0
- fanest-0.1.0/src/fanest/config/module.py +115 -0
- fanest-0.1.0/src/fanest/core/__init__.py +37 -0
- fanest-0.1.0/src/fanest/core/application.py +99 -0
- fanest-0.1.0/src/fanest/core/container.py +529 -0
- fanest-0.1.0/src/fanest/core/discovery.py +32 -0
- fanest-0.1.0/src/fanest/core/enhancers.py +10 -0
- fanest-0.1.0/src/fanest/core/factory.py +262 -0
- fanest-0.1.0/src/fanest/core/metadata.py +114 -0
- fanest-0.1.0/src/fanest/core/module.py +68 -0
- fanest-0.1.0/src/fanest/core/module_ref.py +87 -0
- fanest-0.1.0/src/fanest/core/providers.py +43 -0
- fanest-0.1.0/src/fanest/core/reflector.py +34 -0
- fanest-0.1.0/src/fanest/core/scanner.py +236 -0
- fanest-0.1.0/src/fanest/cqrs/__init__.py +19 -0
- fanest-0.1.0/src/fanest/cqrs/module.py +91 -0
- fanest-0.1.0/src/fanest/events/__init__.py +3 -0
- fanest-0.1.0/src/fanest/events/module.py +54 -0
- fanest-0.1.0/src/fanest/graphql/__init__.py +3 -0
- fanest-0.1.0/src/fanest/graphql/module.py +110 -0
- fanest-0.1.0/src/fanest/health/__init__.py +17 -0
- fanest-0.1.0/src/fanest/health/module.py +105 -0
- fanest-0.1.0/src/fanest/http/__init__.py +3 -0
- fanest-0.1.0/src/fanest/http/module.py +65 -0
- fanest-0.1.0/src/fanest/i18n/__init__.py +3 -0
- fanest-0.1.0/src/fanest/i18n/module.py +67 -0
- fanest-0.1.0/src/fanest/logger/__init__.py +3 -0
- fanest-0.1.0/src/fanest/logger/module.py +38 -0
- fanest-0.1.0/src/fanest/mailer/__init__.py +3 -0
- fanest-0.1.0/src/fanest/mailer/module.py +90 -0
- fanest-0.1.0/src/fanest/mapped_types.py +56 -0
- fanest-0.1.0/src/fanest/metrics/__init__.py +3 -0
- fanest-0.1.0/src/fanest/metrics/module.py +63 -0
- fanest-0.1.0/src/fanest/microservices/__init__.py +39 -0
- fanest-0.1.0/src/fanest/microservices/module.py +203 -0
- fanest-0.1.0/src/fanest/mongodb/__init__.py +17 -0
- fanest-0.1.0/src/fanest/mongodb/module.py +104 -0
- fanest-0.1.0/src/fanest/platform_fastapi/__init__.py +3 -0
- fanest-0.1.0/src/fanest/platform_fastapi/adapter.py +741 -0
- fanest-0.1.0/src/fanest/platform_fastapi/modules.py +11 -0
- fanest-0.1.0/src/fanest/py.typed +1 -0
- fanest-0.1.0/src/fanest/queues/__init__.py +23 -0
- fanest-0.1.0/src/fanest/queues/module.py +181 -0
- fanest-0.1.0/src/fanest/schedule/__init__.py +14 -0
- fanest-0.1.0/src/fanest/schedule/decorators.py +67 -0
- fanest-0.1.0/src/fanest/schedule/registry.py +98 -0
- fanest-0.1.0/src/fanest/schedule/runner.py +88 -0
- fanest-0.1.0/src/fanest/security/__init__.py +3 -0
- fanest-0.1.0/src/fanest/security/module.py +55 -0
- fanest-0.1.0/src/fanest/serve_static/__init__.py +3 -0
- fanest-0.1.0/src/fanest/serve_static/module.py +20 -0
- fanest-0.1.0/src/fanest/session/__init__.py +3 -0
- fanest-0.1.0/src/fanest/session/module.py +109 -0
- fanest-0.1.0/src/fanest/sqlalchemy/__init__.py +23 -0
- fanest-0.1.0/src/fanest/sqlalchemy/module.py +285 -0
- fanest-0.1.0/src/fanest/swagger/__init__.py +64 -0
- fanest-0.1.0/src/fanest/swagger/decorators.py +330 -0
- fanest-0.1.0/src/fanest/swagger/module.py +188 -0
- fanest-0.1.0/src/fanest/testing/__init__.py +3 -0
- fanest-0.1.0/src/fanest/testing/module.py +97 -0
- fanest-0.1.0/src/fanest/throttler/__init__.py +3 -0
- fanest-0.1.0/src/fanest/throttler/module.py +100 -0
- fanest-0.1.0/src/fanest/websockets/__init__.py +3 -0
- fanest-0.1.0/src/fanest/websockets/manager.py +93 -0
- fanest-0.1.0/src/fanest/workers/__init__.py +3 -0
- fanest-0.1.0/src/fanest/workers/module.py +38 -0
- fanest-0.1.0/tests/test_advanced_di.py +85 -0
- fanest-0.1.0/tests/test_app_enhancers.py +83 -0
- fanest-0.1.0/tests/test_application.py +36 -0
- fanest-0.1.0/tests/test_auth_health.py +62 -0
- fanest-0.1.0/tests/test_auth_security_parity.py +76 -0
- fanest-0.1.0/tests/test_basic_app.py +34 -0
- fanest-0.1.0/tests/test_builtin_modules_batch.py +97 -0
- fanest-0.1.0/tests/test_cli.py +204 -0
- fanest-0.1.0/tests/test_common_completions.py +37 -0
- fanest-0.1.0/tests/test_config_testing_swagger.py +81 -0
- fanest-0.1.0/tests/test_cqrs_module.py +92 -0
- fanest-0.1.0/tests/test_deep_http.py +63 -0
- fanest-0.1.0/tests/test_discovery_service.py +34 -0
- fanest-0.1.0/tests/test_file_pipes.py +52 -0
- fanest-0.1.0/tests/test_framework_options.py +66 -0
- fanest-0.1.0/tests/test_gap_fixes.py +133 -0
- fanest-0.1.0/tests/test_global_modules_signature.py +105 -0
- fanest-0.1.0/tests/test_graphql_module.py +44 -0
- fanest-0.1.0/tests/test_http_parity_batch.py +92 -0
- fanest-0.1.0/tests/test_http_response_helpers.py +79 -0
- fanest-0.1.0/tests/test_i18n_bull.py +79 -0
- fanest-0.1.0/tests/test_lifecycle_hooks.py +72 -0
- fanest-0.1.0/tests/test_metrics_module.py +25 -0
- fanest-0.1.0/tests/test_middleware_mapped_microservices.py +196 -0
- fanest-0.1.0/tests/test_module_boundaries.py +220 -0
- fanest-0.1.0/tests/test_module_config_isolation.py +38 -0
- fanest-0.1.0/tests/test_module_global_async.py +242 -0
- fanest-0.1.0/tests/test_module_ref.py +183 -0
- fanest-0.1.0/tests/test_mongodb_module.py +63 -0
- fanest-0.1.0/tests/test_passport_auth.py +55 -0
- fanest-0.1.0/tests/test_platform_health_filters.py +93 -0
- fanest-0.1.0/tests/test_production_readiness.py +54 -0
- fanest-0.1.0/tests/test_provider_scopes.py +123 -0
- fanest-0.1.0/tests/test_queue_mailer.py +120 -0
- fanest-0.1.0/tests/test_reflector.py +42 -0
- fanest-0.1.0/tests/test_request_pipeline.py +132 -0
- fanest-0.1.0/tests/test_schedule_cache_throttler.py +225 -0
- fanest-0.1.0/tests/test_sqlalchemy_orm.py +105 -0
- fanest-0.1.0/tests/test_static_render.py +39 -0
- fanest-0.1.0/tests/test_swagger_module.py +116 -0
- fanest-0.1.0/tests/test_testing_overrides.py +173 -0
- fanest-0.1.0/tests/test_websockets.py +226 -0
- fanest-0.1.0/tests/test_workers_module.py +35 -0
- fanest-0.1.0/uv.lock +1653 -0
fanest-0.1.0/.gitignore
ADDED
fanest-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ahfoysal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fanest-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fanest
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NestJS-style backend architecture for Python, powered by FastAPI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ahfoysal/fanest
|
|
6
|
+
Project-URL: Repository, https://github.com/ahfoysal/fanest
|
|
7
|
+
Project-URL: Issues, https://github.com/ahfoysal/fanest/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: backend,dependency-injection,fastapi,framework,nestjs,python
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: FastAPI
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
23
|
+
Requires-Dist: croniter>=3.0.0
|
|
24
|
+
Requires-Dist: email-validator>=2.2.0
|
|
25
|
+
Requires-Dist: fastapi>=0.115.0
|
|
26
|
+
Requires-Dist: greenlet>=3.0.0
|
|
27
|
+
Requires-Dist: httpx>=0.27.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Requires-Dist: pyjwt>=2.8.0
|
|
30
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
31
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
32
|
+
Requires-Dist: typer>=0.12.0
|
|
33
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: build>=1.2.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pyright>=1.1.411; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: tomli>=2.0.0; (python_version < '3.11') and extra == 'dev'
|
|
40
|
+
Requires-Dist: twine>=6.0.0; extra == 'dev'
|
|
41
|
+
Provides-Extra: standard
|
|
42
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'standard'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# FaNest
|
|
46
|
+
|
|
47
|
+
A progressive Python framework for building structured, scalable, and maintainable backend applications.
|
|
48
|
+
|
|
49
|
+
FaNest brings the NestJS way of thinking to Python: modules, controllers, services, decorators, dependency injection, guards, pipes, interceptors, filters, gateways, scheduled jobs, and package-style integrations. FastAPI does the HTTP work underneath; FaNest gives the application an architecture.
|
|
50
|
+
|
|
51
|
+
It is built for developers who like the NestJS workflow but want to work in Python.
|
|
52
|
+
|
|
53
|
+
## Description
|
|
54
|
+
|
|
55
|
+
Python has excellent web libraries, but larger applications still need a repeatable structure. FaNest provides that structure without hiding the Python ecosystem.
|
|
56
|
+
|
|
57
|
+
The framework combines:
|
|
58
|
+
|
|
59
|
+
- class-based modules, controllers, services, and gateways
|
|
60
|
+
- constructor-based dependency injection
|
|
61
|
+
- decorator-driven routing and WebSocket messages
|
|
62
|
+
- request pipeline layers: guards, pipes, interceptors, and filters
|
|
63
|
+
- FastAPI, Pydantic, SQLAlchemy, and pytest-friendly defaults
|
|
64
|
+
- a CLI shaped around everyday backend work
|
|
65
|
+
|
|
66
|
+
FaNest is inspired by NestJS, but it is not a wrapper around NestJS and is not affiliated with the NestJS project.
|
|
67
|
+
|
|
68
|
+
## Philosophy
|
|
69
|
+
|
|
70
|
+
The goal is not to make Python pretend to be TypeScript. The goal is to keep the workflow familiar for NestJS developers while choosing Python-native tools where they fit better.
|
|
71
|
+
|
|
72
|
+
FaNest should feel predictable in a growing codebase:
|
|
73
|
+
|
|
74
|
+
- modules describe boundaries
|
|
75
|
+
- providers hold business logic
|
|
76
|
+
- controllers stay thin
|
|
77
|
+
- decorators make framework behavior visible
|
|
78
|
+
- tests can override dependencies without patching imports
|
|
79
|
+
- packages can plug into the same module system
|
|
80
|
+
|
|
81
|
+
Small APIs should stay small. Bigger APIs should not become a pile of unrelated route functions.
|
|
82
|
+
|
|
83
|
+
## Getting Started
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv sync --extra dev
|
|
87
|
+
uv run fanest dev examples/basic/main.py
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Open:
|
|
91
|
+
|
|
92
|
+
```txt
|
|
93
|
+
http://127.0.0.1:8000/docs
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## A Small Application
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from pydantic import BaseModel
|
|
100
|
+
|
|
101
|
+
from fanest import Body, Controller, FaNestFactory, Get, Injectable, Module, Post
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class CreateUserDto(BaseModel):
|
|
105
|
+
name: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@Injectable()
|
|
109
|
+
class UsersService:
|
|
110
|
+
def __init__(self):
|
|
111
|
+
self.users = []
|
|
112
|
+
|
|
113
|
+
def find_all(self):
|
|
114
|
+
return self.users
|
|
115
|
+
|
|
116
|
+
def create(self, dto: CreateUserDto):
|
|
117
|
+
user = {"id": len(self.users) + 1, "name": dto.name}
|
|
118
|
+
self.users.append(user)
|
|
119
|
+
return user
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@Controller("users")
|
|
123
|
+
class UsersController:
|
|
124
|
+
def __init__(self, users_service: UsersService):
|
|
125
|
+
self.users_service = users_service
|
|
126
|
+
|
|
127
|
+
@Get("/")
|
|
128
|
+
async def find_all(self):
|
|
129
|
+
return self.users_service.find_all()
|
|
130
|
+
|
|
131
|
+
@Post("/")
|
|
132
|
+
async def create(self, dto: CreateUserDto = Body()):
|
|
133
|
+
return self.users_service.create(dto)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@Module(controllers=[UsersController], providers=[UsersService])
|
|
137
|
+
class AppModule:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
app = FaNestFactory.create(AppModule)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## All Uses Example
|
|
145
|
+
|
|
146
|
+
The fuller example lives in [examples/all_uses/main.py](/Users/foysal/fanest/examples/all_uses/main.py).
|
|
147
|
+
|
|
148
|
+
It shows:
|
|
149
|
+
|
|
150
|
+
- REST controllers
|
|
151
|
+
- Pydantic DTOs
|
|
152
|
+
- mapped DTO helpers
|
|
153
|
+
- constructor injection
|
|
154
|
+
- custom provider tokens
|
|
155
|
+
- middleware
|
|
156
|
+
- file uploads
|
|
157
|
+
- file validation pipes
|
|
158
|
+
- response headers, SSE, and streaming files
|
|
159
|
+
- rendered templates and static assets
|
|
160
|
+
- session cookies and security headers
|
|
161
|
+
- custom parameter decorators
|
|
162
|
+
- config module
|
|
163
|
+
- config validation helpers
|
|
164
|
+
- async module configuration
|
|
165
|
+
- i18n translations and locale extraction
|
|
166
|
+
- JWT auth
|
|
167
|
+
- Passport-style auth strategies
|
|
168
|
+
- role guards
|
|
169
|
+
- cache interceptor
|
|
170
|
+
- cache stores
|
|
171
|
+
- throttling guard
|
|
172
|
+
- Swagger document setup, security schemes, and TypeScript client generation
|
|
173
|
+
- typed exception filters
|
|
174
|
+
- Reflector and discovery services
|
|
175
|
+
- health indicators
|
|
176
|
+
- disk and memory health checks
|
|
177
|
+
- metrics counters
|
|
178
|
+
- worker task handlers
|
|
179
|
+
- GraphQL resolvers and subscriptions
|
|
180
|
+
- health endpoint
|
|
181
|
+
- SQLAlchemy module wiring
|
|
182
|
+
- TypeORM-style repository injection over SQLAlchemy
|
|
183
|
+
- Mongo-style document collections
|
|
184
|
+
- Mongoose-style module aliases
|
|
185
|
+
- interval jobs
|
|
186
|
+
- cron jobs
|
|
187
|
+
- timeout jobs and scheduler registry
|
|
188
|
+
- queue processors
|
|
189
|
+
- Bull-style queue aliases
|
|
190
|
+
- mailer service
|
|
191
|
+
- CQRS command/query/event buses
|
|
192
|
+
- event emitter wildcard, once, and off helpers
|
|
193
|
+
- named microservice transports
|
|
194
|
+
- WebSocket gateway with rooms, broadcasting, guards, pipes, filters, message-body decorators, connected-socket decorators, and Socket.IO-style emitters
|
|
195
|
+
- global prefix, CORS, and global pipes
|
|
196
|
+
- `APP_GUARD`, `APP_PIPE`, `APP_INTERCEPTOR`, and `APP_FILTER` global providers
|
|
197
|
+
|
|
198
|
+
Run it:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
uv run uvicorn examples.all_uses.main:app --reload
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Useful paths:
|
|
205
|
+
|
|
206
|
+
```txt
|
|
207
|
+
GET /api/users
|
|
208
|
+
POST /api/users
|
|
209
|
+
POST /api/users/login
|
|
210
|
+
GET /api/admin/me
|
|
211
|
+
GET /api/health
|
|
212
|
+
GET /api/docs
|
|
213
|
+
WS /api/chat
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## CLI
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
fanest new blog-api
|
|
220
|
+
fanest workspace acme-platform
|
|
221
|
+
fanest start main:app --reload
|
|
222
|
+
fanest dev main.py
|
|
223
|
+
fanest run main.py
|
|
224
|
+
fanest run src/main.py --app application --workers 2
|
|
225
|
+
|
|
226
|
+
fanest generate resource users
|
|
227
|
+
fanest generate module users
|
|
228
|
+
fanest generate controller users
|
|
229
|
+
fanest generate service users
|
|
230
|
+
fanest generate guard auth
|
|
231
|
+
fanest generate pipe validation
|
|
232
|
+
fanest generate interceptor logging
|
|
233
|
+
fanest generate filter http_error
|
|
234
|
+
fanest generate gateway chat
|
|
235
|
+
fanest generate dto users
|
|
236
|
+
fanest generate middleware request_id
|
|
237
|
+
fanest generate decorator current_user
|
|
238
|
+
fanest generate library common
|
|
239
|
+
fanest generate resource users --dry-run
|
|
240
|
+
fanest generate module users --module app_module.py
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Core Features
|
|
244
|
+
|
|
245
|
+
FaNest currently includes:
|
|
246
|
+
|
|
247
|
+
- `@Module`
|
|
248
|
+
- `@Controller`
|
|
249
|
+
- `@Injectable`
|
|
250
|
+
- `@Get`, `@Post`, `@Put`, `@Patch`, `@Delete`, `@Options`, `@Head`, `@All`
|
|
251
|
+
- `Body`, `Param`, `Query`, `Header`, `Headers`, `Cookie`, `Req`, `Res`, `Ip`, `HostParam`, `Session`
|
|
252
|
+
- `UploadedFile`, `UploadedFiles`, `BackgroundTasks`, custom param decorators
|
|
253
|
+
- `HttpCode`, `Redirect`, `SetHeader`, `SetMetadata`, `Version`, `ResponseModel`
|
|
254
|
+
- `Sse` and `StreamableFile`
|
|
255
|
+
- `UseGuards`
|
|
256
|
+
- `UsePipes`
|
|
257
|
+
- `UseInterceptors`
|
|
258
|
+
- `UseFilters`
|
|
259
|
+
- `WebSocketGateway`
|
|
260
|
+
- `SubscribeMessage`
|
|
261
|
+
- `MessageBody`
|
|
262
|
+
- `ConnectedSocket`
|
|
263
|
+
- `Interval`
|
|
264
|
+
- `Cron`
|
|
265
|
+
- `Timeout`
|
|
266
|
+
- `Global`
|
|
267
|
+
- `MessagePattern`
|
|
268
|
+
- `EventPattern`
|
|
269
|
+
- lifecycle hooks: `on_module_init`, `on_application_shutdown`
|
|
270
|
+
|
|
271
|
+
## Dependency Injection
|
|
272
|
+
|
|
273
|
+
FaNest supports class providers and Nest-style provider definitions:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from fanest import Inject, Injectable, Module, token, use_factory, use_value
|
|
277
|
+
|
|
278
|
+
CONFIG = token("CONFIG")
|
|
279
|
+
MESSAGE = token("MESSAGE")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@Injectable()
|
|
283
|
+
class MessageService:
|
|
284
|
+
def __init__(self, message: str = Inject(MESSAGE)):
|
|
285
|
+
self.message = message
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@Module(
|
|
289
|
+
providers=[
|
|
290
|
+
MessageService,
|
|
291
|
+
use_value(CONFIG, {"message": "hello"}),
|
|
292
|
+
use_factory(MESSAGE, lambda config: config["message"], inject=[CONFIG]),
|
|
293
|
+
],
|
|
294
|
+
)
|
|
295
|
+
class MessageModule:
|
|
296
|
+
pass
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Supported provider types:
|
|
300
|
+
|
|
301
|
+
- class providers
|
|
302
|
+
- value providers
|
|
303
|
+
- factory providers
|
|
304
|
+
- async factory providers
|
|
305
|
+
- existing provider aliases
|
|
306
|
+
- injection tokens
|
|
307
|
+
- optional injection
|
|
308
|
+
- singleton, request, and transient scopes
|
|
309
|
+
- `forward_ref`
|
|
310
|
+
- global modules
|
|
311
|
+
- provider overrides in tests
|
|
312
|
+
|
|
313
|
+
## Packages
|
|
314
|
+
|
|
315
|
+
The repository already contains first-party package starts:
|
|
316
|
+
|
|
317
|
+
```txt
|
|
318
|
+
fanest.core modules, DI, scanner, app factory
|
|
319
|
+
fanest.common decorators, exceptions, pipes
|
|
320
|
+
fanest.platform_fastapi FastAPI adapter
|
|
321
|
+
fanest.session signed cookie sessions
|
|
322
|
+
fanest.security helmet-style security headers
|
|
323
|
+
fanest.i18n translations and I18nLang helper
|
|
324
|
+
fanest.config ConfigModule and ConfigService
|
|
325
|
+
fanest.swagger decorators, DocumentBuilder, SwaggerModule
|
|
326
|
+
fanest.auth JWT service, passport strategies, auth guard, roles guard
|
|
327
|
+
fanest.sqlalchemy async SQLAlchemy module, repositories, TypeOrmModule alias
|
|
328
|
+
fanest.mongodb Mongo/Mongoose-style document service and collections
|
|
329
|
+
fanest.cache cache service, interceptor, and store adapters
|
|
330
|
+
fanest.throttler throttling module and guard
|
|
331
|
+
fanest.schedule interval, cron, timeout jobs, scheduler registry
|
|
332
|
+
fanest.websockets connection manager, rooms, broadcasting, Socket.IO-style server
|
|
333
|
+
fanest.serve_static static asset module
|
|
334
|
+
fanest.queues QueueModule/BullModule, processors, jobs
|
|
335
|
+
fanest.mailer mail service with outbox and SMTP handoff
|
|
336
|
+
fanest.cqrs command, query, and event buses
|
|
337
|
+
fanest.events event emitter and OnEvent decorators
|
|
338
|
+
fanest.graphql resolvers, queries, mutations, subscriptions, GraphQL endpoint
|
|
339
|
+
fanest.microservices message/event patterns and named transports
|
|
340
|
+
fanest.mapped_types PartialType, PickType, OmitType, IntersectionType
|
|
341
|
+
fanest.health health endpoint module with reusable indicators
|
|
342
|
+
fanest.metrics counters and metrics endpoint
|
|
343
|
+
fanest.workers task handler registry
|
|
344
|
+
fanest.discovery/core Reflector and DiscoveryService
|
|
345
|
+
fanest.testing TestingModule and provider overrides
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Built-In Pipes And Exceptions
|
|
349
|
+
|
|
350
|
+
Pipes:
|
|
351
|
+
|
|
352
|
+
- `ValidationPipe`
|
|
353
|
+
- `ParseIntPipe`
|
|
354
|
+
- `ParseBoolPipe`
|
|
355
|
+
- `ParseFloatPipe`
|
|
356
|
+
- `ParseUUIDPipe`
|
|
357
|
+
- `ParseEnumPipe`
|
|
358
|
+
- `ParseArrayPipe`
|
|
359
|
+
- `DefaultValuePipe`
|
|
360
|
+
- `ParseFilePipe`
|
|
361
|
+
- `MaxFileSizeValidator`
|
|
362
|
+
- `FileTypeValidator`
|
|
363
|
+
|
|
364
|
+
Exceptions:
|
|
365
|
+
|
|
366
|
+
- `BadRequestException`
|
|
367
|
+
- `UnauthorizedException`
|
|
368
|
+
- `ForbiddenException`
|
|
369
|
+
- `NotFoundException`
|
|
370
|
+
- `ConflictException`
|
|
371
|
+
- `InternalServerErrorException`
|
|
372
|
+
- `UnprocessableEntityException`
|
|
373
|
+
- `TooManyRequestsException`
|
|
374
|
+
- `ServiceUnavailableException`
|
|
375
|
+
|
|
376
|
+
## Swagger
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
from fanest.swagger import DocumentBuilder, SwaggerModule
|
|
380
|
+
|
|
381
|
+
config = (
|
|
382
|
+
DocumentBuilder()
|
|
383
|
+
.set_title("Blog API")
|
|
384
|
+
.set_description("A FaNest application")
|
|
385
|
+
.set_version("1.0.0")
|
|
386
|
+
.add_bearer_auth()
|
|
387
|
+
.build()
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
document = SwaggerModule.create_document(app, config)
|
|
391
|
+
SwaggerModule.setup("/docs", app, document)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Swagger decorators include `ApiTags`, `ApiOperation`, `ApiParam`, `ApiQuery`, `ApiHeader`,
|
|
395
|
+
`ApiBody`, `ApiResponse`, `ApiConsumes`, `ApiProduces`, `ApiBearerAuth`, `ApiBasicAuth`,
|
|
396
|
+
`ApiCookieAuth`, `ApiSecurity`, `ApiExtraModels`, `ApiExtension`, response shortcuts such as
|
|
397
|
+
`ApiOkResponse`, `ApiCreatedResponse`, `ApiNotFoundResponse`, `ApiExcludeEndpoint`,
|
|
398
|
+
`ApiProperty`, `ApiPropertyOptional`, and `ApiHideProperty`.
|
|
399
|
+
`SwaggerModule.generate_typescript_client(document)` can emit a small fetch client from the
|
|
400
|
+
generated OpenAPI document.
|
|
401
|
+
|
|
402
|
+
## WebSockets
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
from fanest import ConnectedSocket, MessageBody, SubscribeMessage, WebSocketGateway
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@WebSocketGateway("/chat")
|
|
409
|
+
class ChatGateway:
|
|
410
|
+
@SubscribeMessage("rename")
|
|
411
|
+
async def rename(self, name: str = MessageBody("name"), socket=ConnectedSocket()):
|
|
412
|
+
return {"name": name}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Gateways can use guards, pipes, filters, rooms, broadcast helpers, and `SocketIoServer` for
|
|
416
|
+
the familiar `server.to("room").emit(...)` shape.
|
|
417
|
+
|
|
418
|
+
## Health Checks
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
from fanest.health import DiskHealthIndicator, HealthIndicator, HealthModule, MemoryHealthIndicator
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
HealthModule.register(
|
|
425
|
+
[
|
|
426
|
+
HealthIndicator("database", lambda: {"status": "ok"}),
|
|
427
|
+
DiskHealthIndicator(path="/"),
|
|
428
|
+
MemoryHealthIndicator(rss_threshold_mb=512),
|
|
429
|
+
]
|
|
430
|
+
)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Mapped Types
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
from fanest import PartialType, PickType
|
|
437
|
+
|
|
438
|
+
UpdateUserDto = PartialType(CreateUserDto)
|
|
439
|
+
PublicUserDto = PickType(UserDto, ["id", "name"])
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## ORM
|
|
443
|
+
|
|
444
|
+
FaNest ships with an async SQLAlchemy package and Nest-style repository injection:
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
from fanest import Injectable, Module
|
|
448
|
+
from fanest.sqlalchemy import InjectRepository, SqlAlchemyRepository, TypeOrmModule
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@Injectable()
|
|
452
|
+
class UsersService:
|
|
453
|
+
def __init__(self, users: SqlAlchemyRepository = InjectRepository(User)):
|
|
454
|
+
self.users = users
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@Module(
|
|
458
|
+
imports=[
|
|
459
|
+
TypeOrmModule.for_root(database_url="sqlite+aiosqlite:///app.db"),
|
|
460
|
+
TypeOrmModule.for_feature([User]),
|
|
461
|
+
],
|
|
462
|
+
providers=[UsersService],
|
|
463
|
+
)
|
|
464
|
+
class UsersModule:
|
|
465
|
+
pass
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Repositories include `find_all`, `find_by`, `find_one`, `find_one_by`, `count`, `save`,
|
|
469
|
+
`update`, `delete`, and `delete_by`. `SqlAlchemyModule` and `TypeOrmModule` point to the same
|
|
470
|
+
Python-native SQLAlchemy integration, so Nest users can keep the familiar module shape while
|
|
471
|
+
still using SQLAlchemy models.
|
|
472
|
+
|
|
473
|
+
## Middleware
|
|
474
|
+
|
|
475
|
+
```python
|
|
476
|
+
class RequestIdMiddleware:
|
|
477
|
+
async def use(self, request, call_next):
|
|
478
|
+
response = await call_next(request)
|
|
479
|
+
response.headers["x-request-id"] = "local"
|
|
480
|
+
return response
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
@Module(controllers=[UsersController], middlewares=[RequestIdMiddleware])
|
|
484
|
+
class AppModule:
|
|
485
|
+
pass
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Modules can also expose a Nest-style `configure(consumer)` method for route-scoped
|
|
489
|
+
middleware with exclusions:
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
class AppModule:
|
|
493
|
+
def configure(self, consumer):
|
|
494
|
+
consumer.apply(RequestIdMiddleware).exclude("/health").for_routes("/users*")
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Microservices
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
from fanest.microservices import MessagePattern, MicroserviceServer
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
class MathService:
|
|
504
|
+
@MessagePattern("math.double")
|
|
505
|
+
async def double(self, data, context):
|
|
506
|
+
return data * 2
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
server = MicroserviceServer(AppModule).compile()
|
|
510
|
+
client = server.client()
|
|
511
|
+
result = await client.send("math.double", 21)
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## Testing
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
from fanest.testing import TestingModule
|
|
518
|
+
|
|
519
|
+
app = (
|
|
520
|
+
TestingModule.create(AppModule)
|
|
521
|
+
.override_provider(UsersService, MockUsersService())
|
|
522
|
+
.compile()
|
|
523
|
+
)
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Run the test suite:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
uv run pytest
|
|
530
|
+
uv run ruff check .
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## NestJS Parity
|
|
534
|
+
|
|
535
|
+
FaNest is aiming at the full NestJS surface area. The current implementation covers the core application model and many common packages, but some large systems still need deeper work.
|
|
536
|
+
|
|
537
|
+
Current:
|
|
538
|
+
|
|
539
|
+
- modules, controllers, providers
|
|
540
|
+
- DI with custom, async, scoped, optional, and aliased providers
|
|
541
|
+
- `APP_GUARD`, `APP_PIPE`, `APP_INTERCEPTOR`, and `APP_FILTER` global enhancer provider tokens
|
|
542
|
+
- global modules and exported module boundaries
|
|
543
|
+
- REST decorators
|
|
544
|
+
- request binding
|
|
545
|
+
- versioned routes, status codes, redirects, response headers, SSE, streaming files
|
|
546
|
+
- rendered templates and static asset module
|
|
547
|
+
- signed sessions and security headers
|
|
548
|
+
- middleware
|
|
549
|
+
- route-scoped middleware with exclusions
|
|
550
|
+
- file upload binding
|
|
551
|
+
- file validation
|
|
552
|
+
- custom param decorators
|
|
553
|
+
- mapped DTO helpers
|
|
554
|
+
- response serialization
|
|
555
|
+
- guards, pipes, interceptors, filters
|
|
556
|
+
- `@Catch` typed exception filters
|
|
557
|
+
- Reflector and DiscoveryService
|
|
558
|
+
- Swagger helpers and security schemes
|
|
559
|
+
- JWT auth and roles
|
|
560
|
+
- Passport-style strategy guards
|
|
561
|
+
- cache and throttling
|
|
562
|
+
- WebSocket gateways, Socket.IO-style room emitters, guards, pipes, filters, `MessageBody`, and `ConnectedSocket`
|
|
563
|
+
- cron, interval, timeout jobs, and scheduler registry
|
|
564
|
+
- in-memory queue processors
|
|
565
|
+
- queue retries and delayed jobs
|
|
566
|
+
- BullModule and InjectQueue aliases
|
|
567
|
+
- mailer package with templates
|
|
568
|
+
- CQRS package
|
|
569
|
+
- event emitter wildcard/once/off helpers
|
|
570
|
+
- microservice message/event patterns and named transports
|
|
571
|
+
- lightweight GraphQL module with queries, mutations, and subscriptions
|
|
572
|
+
- SQLAlchemy package start
|
|
573
|
+
- TypeOrmModule and InjectRepository aliases over SQLAlchemy
|
|
574
|
+
- migration template helper
|
|
575
|
+
- Mongo-style package start
|
|
576
|
+
- MongooseModule and InjectModel aliases
|
|
577
|
+
- i18n package
|
|
578
|
+
- cache store adapters
|
|
579
|
+
- health checks
|
|
580
|
+
- health indicators with disk and memory helpers
|
|
581
|
+
- metrics module
|
|
582
|
+
- worker task handlers
|
|
583
|
+
- testing utilities
|
|
584
|
+
- CLI generators
|
|
585
|
+
- workspace and library CLI commands
|
|
586
|
+
|
|
587
|
+
Still to deepen:
|
|
588
|
+
|
|
589
|
+
- Redis-backed queue transport
|
|
590
|
+
- advanced SMTP provider adapters
|
|
591
|
+
- full migration runner
|
|
592
|
+
- deeper GraphQL schema generation
|
|
593
|
+
- networked microservice drivers beyond the in-process transport contract
|
|
594
|
+
|
|
595
|
+
The plan is to keep closing that gap package by package, without losing the Python feel.
|
|
596
|
+
|
|
597
|
+
## Repository Status
|
|
598
|
+
|
|
599
|
+
This is an early framework build, but it is runnable and tested.
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
uv run pytest
|
|
603
|
+
# 90 passed
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## License
|
|
607
|
+
|
|
608
|
+
MIT
|