fastapi-spawn 0.4.1__tar.gz → 0.4.3__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.
Files changed (65) hide show
  1. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/.gitignore +2 -0
  2. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/PKG-INFO +8 -4
  3. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/README.md +7 -3
  4. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/__init__.py +1 -1
  5. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/generator.py +14 -6
  6. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/api/graphql.py.j2 +1 -1
  7. fastapi_spawn-0.4.3/fastapi_spawn/templates/app/api/v1/router.py.j2 +20 -0
  8. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/config.py.j2 +11 -3
  9. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/main.py.j2 +9 -8
  10. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/middleware/request_logger.py.j2 +1 -24
  11. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/pyproject.toml.j2 +3 -18
  12. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/pyproject.toml +1 -1
  13. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/CHANGELOG.md +0 -0
  14. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/LICENSE +0 -0
  15. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/cli.py +0 -0
  16. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/config.py +0 -0
  17. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/constants.py +0 -0
  18. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/interactive.py +0 -0
  19. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/alembic/alembic.ini.j2 +0 -0
  20. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/alembic/env.py.j2 +0 -0
  21. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/__init__.py.j2 +0 -0
  22. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/api/deps.py.j2 +0 -0
  23. /fastapi_spawn-0.4.1/fastapi_spawn/templates/app/api/v1/auth.py.j2 → /fastapi_spawn-0.4.3/fastapi_spawn/templates/app/api/v1/auth/router.py.j2 +0 -0
  24. /fastapi_spawn-0.4.1/fastapi_spawn/templates/app/api/v1/health.py.j2 → /fastapi_spawn-0.4.3/fastapi_spawn/templates/app/api/v1/health/router.py.j2 +0 -0
  25. /fastapi_spawn-0.4.1/fastapi_spawn/templates/app/api/v1/ws.py.j2 → /fastapi_spawn-0.4.3/fastapi_spawn/templates/app/api/v1/ws/router.py.j2 +0 -0
  26. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/ai.py.j2 +0 -0
  27. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/email.py.j2 +0 -0
  28. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/exceptions.py.j2 +0 -0
  29. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/logger.py.j2 +0 -0
  30. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/logging.py.j2 +0 -0
  31. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/monitoring.py.j2 +0 -0
  32. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/notifications.py.j2 +0 -0
  33. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/security.py.j2 +0 -0
  34. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/storage.py.j2 +0 -0
  35. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/vector_db.py.j2 +0 -0
  36. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/core/ws_manager.py.j2 +0 -0
  37. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/db/session.py.j2 +0 -0
  38. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/frontend/index.html.j2 +0 -0
  39. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/middleware/__init__.py.j2 +0 -0
  40. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/app/middleware/rate_limit.py.j2 +0 -0
  41. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/Makefile.j2 +0 -0
  42. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/README.md.j2 +0 -0
  43. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/env.j2 +0 -0
  44. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/env_example.j2 +0 -0
  45. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/gitignore.j2 +0 -0
  46. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/base/pre_commit.j2 +0 -0
  47. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/ci/github/publish.yml.j2 +0 -0
  48. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/ci/github/tests.yml.j2 +0 -0
  49. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2 +0 -0
  50. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/docker/Dockerfile.j2 +0 -0
  51. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/docker/docker-compose.yml.j2 +0 -0
  52. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/docker/dockerignore.j2 +0 -0
  53. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/infra/docker/docker-compose.prod.yml.j2 +0 -0
  54. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/infra/helm/Chart.yaml.j2 +0 -0
  55. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/infra/helm/values.yaml.j2 +0 -0
  56. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/infra/terraform/main.tf.j2 +0 -0
  57. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/infra/terraform/variables.tf.j2 +0 -0
  58. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/root/main.py.j2 +0 -0
  59. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/tasks/arq_worker.py.j2 +0 -0
  60. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/tasks/celery_app.py.j2 +0 -0
  61. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/tasks/sample_tasks.py.j2 +0 -0
  62. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/tests/conftest.py.j2 +0 -0
  63. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/templates/tests/test_health.py.j2 +0 -0
  64. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/utils.py +0 -0
  65. {fastapi_spawn-0.4.1 → fastapi_spawn-0.4.3}/fastapi_spawn/validators.py +0 -0
@@ -51,3 +51,5 @@ Thumbs.db
51
51
 
52
52
  # Hatch / build backends
53
53
  .hatch/
54
+
55
+ .test/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-spawn
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options.
5
5
  Project-URL: Homepage, https://github.com/Bishwajitgarai/fastapi-spawn
6
6
  Project-URL: Documentation, https://github.com/Bishwajitgarai/fastapi-spawn#readme
@@ -110,9 +110,13 @@ my-api/
110
110
  │ ├── api/
111
111
  │ │ ├── graphql.py # Strawberry GraphQL schema + IDE (if graphql)
112
112
  │ │ └── v1/
113
- │ │ ├── health.py # GET /health /readiness /liveness
114
- │ │ ├── auth.py # POST /auth/login /auth/refresh
115
- │ │ └── ws.py # WebSocket /ws/connect /ws/connect/{room_id}
113
+ │ │ ├── router.py # Central API router aggregating sub-routers
114
+ │ │ ├── health/
115
+ │ │ └── router.py # GET /health /readiness /liveness
116
+ │ │ ├── auth/
117
+ │ │ │ └── router.py # POST /auth/login /auth/refresh (if auth)
118
+ │ │ └── ws/
119
+ │ │ └── router.py # WebSocket /ws/connect (if websockets)
116
120
  │ ├── core/
117
121
  │ │ ├── config.py # Pydantic Settings v2 — individual env fields + @property URLs
118
122
  │ │ ├── logger.py # Context-var logger — request ID, client IP, file rotation
@@ -69,9 +69,13 @@ my-api/
69
69
  │ ├── api/
70
70
  │ │ ├── graphql.py # Strawberry GraphQL schema + IDE (if graphql)
71
71
  │ │ └── v1/
72
- │ │ ├── health.py # GET /health /readiness /liveness
73
- │ │ ├── auth.py # POST /auth/login /auth/refresh
74
- │ │ └── ws.py # WebSocket /ws/connect /ws/connect/{room_id}
72
+ │ │ ├── router.py # Central API router aggregating sub-routers
73
+ │ │ ├── health/
74
+ │ │ └── router.py # GET /health /readiness /liveness
75
+ │ │ ├── auth/
76
+ │ │ │ └── router.py # POST /auth/login /auth/refresh (if auth)
77
+ │ │ └── ws/
78
+ │ │ └── router.py # WebSocket /ws/connect (if websockets)
75
79
  │ ├── core/
76
80
  │ │ ├── config.py # Pydantic Settings v2 — individual env fields + @property URLs
77
81
  │ │ ├── logger.py # Context-var logger — request ID, client IP, file rotation
@@ -1,6 +1,6 @@
1
1
  """fastapi-spawn — Production-ready FastAPI project scaffolding CLI."""
2
2
 
3
- __version__ = "0.4.1"
3
+ __version__ = "0.4.3"
4
4
  __author__ = "Bishwajit Garai"
5
5
  __email__ = "bishwajitgarai@gmail.com"
6
6
  __license__ = "MIT"
@@ -173,11 +173,15 @@ class ProjectGenerator:
173
173
  v1 = api / "v1"
174
174
  v1.mkdir()
175
175
  self._render_to(v1 / "__init__.py", "app/__init__.py.j2")
176
- self._render_to(v1 / "health.py", "app/api/v1/health.py.j2")
176
+ self._render_to(v1 / "router.py", "app/api/v1/router.py.j2")
177
+ (v1 / "health").mkdir()
178
+ self._render_to(v1 / "health" / "router.py", "app/api/v1/health/router.py.j2")
177
179
  if self.config.has_auth:
178
- self._render_to(v1 / "auth.py", "app/api/v1/auth.py.j2")
180
+ (v1 / "auth").mkdir()
181
+ self._render_to(v1 / "auth" / "router.py", "app/api/v1/auth/router.py.j2")
179
182
  if self.config.has_websockets:
180
- self._render_to(v1 / "ws.py", "app/api/v1/ws.py.j2")
183
+ (v1 / "ws").mkdir()
184
+ self._render_to(v1 / "ws" / "router.py", "app/api/v1/ws/router.py.j2")
181
185
  self._render_to(core / "ws_manager.py", "app/core/ws_manager.py.j2")
182
186
  if self.config.has_graphql:
183
187
  self._render_to(api / "graphql.py", "app/api/graphql.py.j2")
@@ -287,9 +291,13 @@ class ProjectGenerator:
287
291
  if cfg.has_s3: core_items += " storage.py"
288
292
  if cfg.has_ai: core_items += " ai.py"
289
293
  app.add(f"[blue]core/[/blue] {core_items}")
290
- api = app.add("[blue]api/[/blue] deps.py")
291
- v1 = "health.py" + (" auth.py" if cfg.has_auth else "")
292
- api.add(f"[blue]v1/[/blue] {v1}")
294
+ api = app.add("[blue]api/[/blue] deps.py [dim]graphql.py[/dim]?")
295
+ v1 = api.add("[blue]v1/[/blue] router.py")
296
+ v1.add("[blue]health/[/blue] router.py")
297
+ if cfg.has_auth:
298
+ v1.add("[blue]auth/[/blue] router.py")
299
+ if cfg.has_websockets:
300
+ v1.add("[blue]ws/[/blue] router.py")
293
301
  if cfg.db.value != "none":
294
302
  app.add("[blue]db/[/blue] session.py")
295
303
  for sub in ("models/", "schemas/", "services/", "repositories/"):
@@ -93,6 +93,6 @@ schema = strawberry.Schema(
93
93
  # Mount with: app.include_router(graphql_router, prefix="/graphql")
94
94
  graphql_router = GraphQLRouter(
95
95
  schema,
96
- graphiql=True, # GraphiQL IDE at /graphql — disable in production
96
+ graphql_ide="graphiql",
97
97
  subscription_protocols=["graphql-transport-ws", "graphql-ws"],
98
98
  )
@@ -0,0 +1,20 @@
1
+ from fastapi import APIRouter
2
+
3
+ from app.api.v1.health.router import router as health_router
4
+ {% if has_auth %}
5
+ from app.api.v1.auth.router import router as auth_router
6
+ {% endif %}
7
+ {% if has_websockets %}
8
+ from app.api.v1.ws.router import router as ws_router
9
+ {% endif %}
10
+
11
+
12
+ router = APIRouter()
13
+
14
+ router.include_router(health_router)
15
+ {% if has_auth %}
16
+ router.include_router(auth_router)
17
+ {% endif %}
18
+ {% if has_websockets %}
19
+ router.include_router(ws_router)
20
+ {% endif %}
@@ -3,9 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from functools import lru_cache
6
- from typing import Any
6
+ from typing import Any, Optional
7
7
 
8
- from pydantic import field_validator
8
+ from pydantic import ConfigDict, field_validator
9
9
  from pydantic_settings import BaseSettings, SettingsConfigDict
10
10
 
11
11
 
@@ -24,7 +24,15 @@ class Settings(BaseSettings):
24
24
  ENVIRONMENT: str = "dev"
25
25
  API_V1_PREFIX: str = "/api/v1"
26
26
  SECRET_KEY: str = "change-me"
27
- CORS_ORIGINS: list[str] = ["http://localhost:3000", "http://localhost:8000"]
27
+ CORS_ORIGINS: str | list[str] = ["http://localhost:3000", "http://localhost:8000"]
28
+
29
+ @field_validator("CORS_ORIGINS", mode="before")
30
+ def parse_cors(cls, v: Any) -> list[str]:
31
+ if isinstance(v, str) and not v.startswith("["):
32
+ return [i.strip() for i in v.split(",") if i.strip()]
33
+ if isinstance(v, list):
34
+ return v
35
+ return []
28
36
 
29
37
  @property
30
38
  def DEBUG(self) -> bool:
@@ -7,19 +7,17 @@ from fastapi import FastAPI
7
7
  from fastapi.middleware.cors import CORSMiddleware
8
8
  from fastapi.responses import ORJSONResponse
9
9
 
10
- from app.api.v1 import health
11
- {% if has_auth %}
12
- from app.api.v1 import auth
10
+ from app.api.v1.router import router as api_v1_router
11
+ {% if has_graphql %}
12
+ from app.api.graphql import graphql_router
13
13
  {% endif %}
14
14
  from app.core.config import settings
15
15
  from app.core.exceptions import register_exception_handlers
16
- from app.core.logging import configure_logging
17
16
 
18
17
 
19
18
  @asynccontextmanager
20
19
  async def lifespan(application: FastAPI) -> AsyncGenerator[None, None]:
21
20
  """Manage application startup and shutdown lifecycle."""
22
- configure_logging()
23
21
  {% if has_relational_db and orm == "sqlalchemy" %}
24
22
  from app.db.session import engine, Base
25
23
  async with engine.begin() as conn:
@@ -60,9 +58,12 @@ def create_application() -> FastAPI:
60
58
  register_exception_handlers(application)
61
59
 
62
60
  # Routers
63
- application.include_router(health.router, prefix=settings.API_V1_PREFIX, tags=["Health"])
64
- {% if has_auth %}
65
- application.include_router(auth.router, prefix=settings.API_V1_PREFIX, tags=["Auth"])
61
+ # Mount REST API v1 router
62
+ application.include_router(api_v1_router, prefix=settings.API_V1_PREFIX)
63
+
64
+ {% if has_graphql %}
65
+ # Mount GraphQL endpoint
66
+ application.include_router(graphql_router, prefix="/graphql")
66
67
  {% endif %}
67
68
 
68
69
  return application
@@ -7,33 +7,11 @@ from __future__ import annotations
7
7
 
8
8
  import time
9
9
  import uuid
10
- from contextvars import ContextVar
11
- from typing import Optional
12
-
13
10
  from fastapi import Request
14
11
  from starlette.middleware.base import BaseHTTPMiddleware
15
12
  from starlette.responses import Response
16
13
 
17
- # ── Context variables (per-request, async-safe) ────────────────────────────
18
- _request_id_ctx: ContextVar[Optional[str]] = ContextVar("request_id", default=None)
19
- _client_ip_ctx: ContextVar[Optional[str]] = ContextVar("client_ip", default=None)
20
-
21
-
22
- def set_request_id(value: Optional[str]) -> None:
23
- _request_id_ctx.set(value)
24
-
25
-
26
- def get_request_id() -> Optional[str]:
27
- return _request_id_ctx.get()
28
-
29
-
30
- def set_client_ip(value: Optional[str]) -> None:
31
- _client_ip_ctx.set(value)
32
-
33
-
34
- def get_client_ip() -> Optional[str]:
35
- return _client_ip_ctx.get()
36
-
14
+ from app.core.logger import logger, set_client_ip, set_request_id
37
15
 
38
16
  def _extract_client_ip(request: Request) -> str:
39
17
  """Respect X-Forwarded-For / X-Real-IP from reverse proxies."""
@@ -70,7 +48,6 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware):
70
48
 
71
49
  start = time.perf_counter()
72
50
 
73
- from app.core.logger import logger
74
51
  logger.info("→ %s %s rid=%s ip=%s", request.method, request.url.path, request_id, client_ip)
75
52
 
76
53
  try:
@@ -15,6 +15,7 @@ dependencies = [
15
15
  "pydantic-settings>=2.2.0",
16
16
  "httpx>=0.27.0",
17
17
  "slowapi>=0.1.9", # rate limiting middleware
18
+ "orjson>=3.9.0", # fast JSON responses
18
19
 
19
20
  {% if log_lib == "loguru" %}
20
21
  "loguru>=0.7.2",
@@ -175,24 +176,8 @@ dev-dependencies = [
175
176
  "pre-commit>=3.7.0",
176
177
  ]
177
178
 
178
- # ── uv run scripts ────────────────────────────────────────────────────────────
179
- # Usage: uv run <script-name>
180
- [tool.uv.scripts]
181
- dev = "uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
182
- start = "python main.py"
183
- test = "pytest --cov=app --cov-report=term-missing"
184
- lint = "ruff check ."
185
- format = "ruff format ."
186
- typecheck = "mypy app/"
187
- {% if migration == "alembic" %}
188
- migrate = "alembic upgrade head"
189
- rollback = "alembic downgrade -1"
190
- makemig = "alembic revision --autogenerate -m"
191
- {% endif %}
192
- {% if has_broker %}
193
- worker = "celery -A tasks.celery_app worker --loglevel=info"
194
- beat = "celery -A tasks.celery_app beat --loglevel=info"
195
- {% endif %}
179
+ [tool.hatch.build.targets.wheel]
180
+ packages = ["app"]
196
181
 
197
182
  [tool.pytest.ini_options]
198
183
  asyncio_mode = "auto"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-spawn"
7
- version = "0.4.1"
7
+ version = "0.4.3"
8
8
  description = "A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
File without changes