fastapi-spawn 0.1.0__py3-none-any.whl

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 (50) hide show
  1. fastapi_spawn/__init__.py +6 -0
  2. fastapi_spawn/cli.py +387 -0
  3. fastapi_spawn/config.py +162 -0
  4. fastapi_spawn/constants.py +133 -0
  5. fastapi_spawn/generator.py +294 -0
  6. fastapi_spawn/interactive.py +192 -0
  7. fastapi_spawn/templates/alembic/alembic.ini.j2 +39 -0
  8. fastapi_spawn/templates/alembic/env.py.j2 +64 -0
  9. fastapi_spawn/templates/app/__init__.py.j2 +1 -0
  10. fastapi_spawn/templates/app/api/deps.py.j2 +39 -0
  11. fastapi_spawn/templates/app/api/v1/auth.py.j2 +59 -0
  12. fastapi_spawn/templates/app/api/v1/health.py.j2 +40 -0
  13. fastapi_spawn/templates/app/core/ai.py.j2 +76 -0
  14. fastapi_spawn/templates/app/core/config.py.j2 +177 -0
  15. fastapi_spawn/templates/app/core/exceptions.py.j2 +43 -0
  16. fastapi_spawn/templates/app/core/logging.py.j2 +70 -0
  17. fastapi_spawn/templates/app/core/security.py.j2 +42 -0
  18. fastapi_spawn/templates/app/core/storage.py.j2 +73 -0
  19. fastapi_spawn/templates/app/db/session.py.j2 +84 -0
  20. fastapi_spawn/templates/app/main.py.j2 +71 -0
  21. fastapi_spawn/templates/base/Makefile.j2 +45 -0
  22. fastapi_spawn/templates/base/README.md.j2 +74 -0
  23. fastapi_spawn/templates/base/env.j2 +82 -0
  24. fastapi_spawn/templates/base/env_example.j2 +85 -0
  25. fastapi_spawn/templates/base/gitignore.j2 +38 -0
  26. fastapi_spawn/templates/base/pre_commit.j2 +17 -0
  27. fastapi_spawn/templates/base/pyproject.toml.j2 +129 -0
  28. fastapi_spawn/templates/ci/github/publish.yml.j2 +32 -0
  29. fastapi_spawn/templates/ci/github/tests.yml.j2 +39 -0
  30. fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2 +29 -0
  31. fastapi_spawn/templates/docker/Dockerfile.j2 +17 -0
  32. fastapi_spawn/templates/docker/docker-compose.yml.j2 +97 -0
  33. fastapi_spawn/templates/docker/dockerignore.j2 +13 -0
  34. fastapi_spawn/templates/infra/docker/docker-compose.prod.yml.j2 +43 -0
  35. fastapi_spawn/templates/infra/helm/Chart.yaml.j2 +6 -0
  36. fastapi_spawn/templates/infra/helm/values.yaml.j2 +26 -0
  37. fastapi_spawn/templates/infra/terraform/main.tf.j2 +26 -0
  38. fastapi_spawn/templates/infra/terraform/variables.tf.j2 +17 -0
  39. fastapi_spawn/templates/root/main.py.j2 +16 -0
  40. fastapi_spawn/templates/tasks/celery_app.py.j2 +37 -0
  41. fastapi_spawn/templates/tasks/sample_tasks.py.j2 +27 -0
  42. fastapi_spawn/templates/tests/conftest.py.j2 +22 -0
  43. fastapi_spawn/templates/tests/test_health.py.j2 +30 -0
  44. fastapi_spawn/utils.py +58 -0
  45. fastapi_spawn/validators.py +67 -0
  46. fastapi_spawn-0.1.0.dist-info/METADATA +262 -0
  47. fastapi_spawn-0.1.0.dist-info/RECORD +50 -0
  48. fastapi_spawn-0.1.0.dist-info/WHEEL +4 -0
  49. fastapi_spawn-0.1.0.dist-info/entry_points.txt +2 -0
  50. fastapi_spawn-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,43 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ slug }}
5
+ labels:
6
+ app: {{ slug }}
7
+ spec:
8
+ replicas: 2
9
+ selector:
10
+ matchLabels:
11
+ app: {{ slug }}
12
+ template:
13
+ metadata:
14
+ labels:
15
+ app: {{ slug }}
16
+ spec:
17
+ containers:
18
+ - name: {{ slug }}
19
+ image: ghcr.io/your-org/{{ slug }}:latest
20
+ ports:
21
+ - containerPort: 8000
22
+ envFrom:
23
+ - secretRef:
24
+ name: {{ slug }}-secrets
25
+ livenessProbe:
26
+ httpGet:
27
+ path: /api/v1/health/liveness
28
+ port: 8000
29
+ initialDelaySeconds: 15
30
+ periodSeconds: 20
31
+ readinessProbe:
32
+ httpGet:
33
+ path: /api/v1/health/readiness
34
+ port: 8000
35
+ initialDelaySeconds: 5
36
+ periodSeconds: 10
37
+ resources:
38
+ requests:
39
+ memory: "128Mi"
40
+ cpu: "100m"
41
+ limits:
42
+ memory: "512Mi"
43
+ cpu: "500m"
@@ -0,0 +1,6 @@
1
+ apiVersion: v2
2
+ name: {{ slug }}
3
+ description: Helm chart for {{ project_name }}
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "0.1.0"
@@ -0,0 +1,26 @@
1
+ replicaCount: 2
2
+
3
+ image:
4
+ repository: ghcr.io/your-org/{{ slug }}
5
+ tag: latest
6
+ pullPolicy: IfNotPresent
7
+
8
+ service:
9
+ type: ClusterIP
10
+ port: 8000
11
+
12
+ ingress:
13
+ enabled: false
14
+ host: {{ slug }}.example.com
15
+
16
+ env:
17
+ APP_ENV: production
18
+ DEBUG: "false"
19
+
20
+ resources:
21
+ requests:
22
+ memory: "128Mi"
23
+ cpu: "100m"
24
+ limits:
25
+ memory: "512Mi"
26
+ cpu: "500m"
@@ -0,0 +1,26 @@
1
+ terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ version = "~> 5.0"
6
+ }
7
+ }
8
+ }
9
+
10
+ provider "aws" {
11
+ region = var.aws_region
12
+ }
13
+
14
+ # ECR repository
15
+ resource "aws_ecr_repository" "app" {
16
+ name = "{{ slug }}"
17
+ image_tag_mutability = "MUTABLE"
18
+ image_scanning_configuration {
19
+ scan_on_push = true
20
+ }
21
+ }
22
+
23
+ # ECS Cluster
24
+ resource "aws_ecs_cluster" "main" {
25
+ name = "{{ slug }}-cluster"
26
+ }
@@ -0,0 +1,17 @@
1
+ variable "aws_region" {
2
+ description = "AWS region to deploy to"
3
+ type = string
4
+ default = "us-east-1"
5
+ }
6
+
7
+ variable "app_name" {
8
+ description = "Application name"
9
+ type = string
10
+ default = "{{ slug }}"
11
+ }
12
+
13
+ variable "container_port" {
14
+ description = "Port the container listens on"
15
+ type = number
16
+ default = 8000
17
+ }
@@ -0,0 +1,16 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Run with: uv run main.py
4
+ Or: uvicorn app.main:app --reload
5
+ """
6
+
7
+ import uvicorn
8
+
9
+ if __name__ == "__main__":
10
+ uvicorn.run(
11
+ "app.main:app",
12
+ host="0.0.0.0",
13
+ port=8000,
14
+ reload=True,
15
+ log_level="info",
16
+ )
@@ -0,0 +1,37 @@
1
+ """Celery application factory for {{ project_name }}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from celery import Celery
6
+
7
+ from app.core.config import settings
8
+
9
+ {% if broker == "redis" %}
10
+ celery_app = Celery(
11
+ "{{ slug }}",
12
+ broker=settings.REDIS_URL,
13
+ backend=settings.REDIS_URL,
14
+ )
15
+ {% elif broker == "rabbitmq" %}
16
+ celery_app = Celery(
17
+ "{{ slug }}",
18
+ broker=settings.RABBITMQ_URL,
19
+ backend="rpc://",
20
+ )
21
+ {% else %}
22
+ celery_app = Celery("{{ slug }}")
23
+ {% endif %}
24
+
25
+ celery_app.conf.update(
26
+ task_serializer="json",
27
+ result_serializer="json",
28
+ accept_content=["json"],
29
+ timezone="UTC",
30
+ enable_utc=True,
31
+ task_track_started=True,
32
+ worker_prefetch_multiplier=1,
33
+ task_acks_late=True,
34
+ )
35
+
36
+ # Auto-discover tasks in the tasks/ package
37
+ celery_app.autodiscover_tasks(["tasks"])
@@ -0,0 +1,27 @@
1
+ """Sample Celery tasks for {{ project_name }}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+
7
+ from tasks.celery_app import celery_app
8
+
9
+
10
+ @celery_app.task(bind=True, name="tasks.sample.add", max_retries=3)
11
+ def add(self, x: int, y: int) -> int:
12
+ """Simple addition task — use as a template."""
13
+ return x + y
14
+
15
+
16
+ @celery_app.task(bind=True, name="tasks.sample.slow_task", max_retries=3)
17
+ def slow_task(self, seconds: int = 5) -> str:
18
+ """Simulate a long-running task."""
19
+ time.sleep(seconds)
20
+ return f"Completed after {seconds}s"
21
+
22
+
23
+ @celery_app.task(bind=True, name="tasks.sample.send_email", max_retries=3)
24
+ def send_email(self, to: str, subject: str, body: str) -> dict:
25
+ """Stub email sending task. Replace with real email logic."""
26
+ # TODO: integrate with SendGrid / SES / SMTP
27
+ return {"status": "sent", "to": to, "subject": subject}
@@ -0,0 +1,22 @@
1
+ """pytest configuration and fixtures for {{ project_name }}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from httpx import ASGITransport, AsyncClient
7
+
8
+ from app.main import app
9
+
10
+
11
+ @pytest.fixture(scope="session")
12
+ def anyio_backend() -> str:
13
+ return "asyncio"
14
+
15
+
16
+ @pytest.fixture
17
+ async def client() -> AsyncClient: # type: ignore[return]
18
+ async with AsyncClient(
19
+ transport=ASGITransport(app=app),
20
+ base_url="http://testserver",
21
+ ) as ac:
22
+ yield ac
@@ -0,0 +1,30 @@
1
+ """Health endpoint tests for {{ project_name }}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from httpx import AsyncClient
7
+
8
+
9
+ @pytest.mark.anyio
10
+ async def test_health_ok(client: AsyncClient) -> None:
11
+ response = await client.get("/api/v1/health")
12
+ assert response.status_code == 200
13
+ data = response.json()
14
+ assert data["status"] == "ok"
15
+ assert "uptime_seconds" in data
16
+ assert data["service"] == "{{ project_name }}"
17
+
18
+
19
+ @pytest.mark.anyio
20
+ async def test_readiness(client: AsyncClient) -> None:
21
+ response = await client.get("/api/v1/health/readiness")
22
+ assert response.status_code == 200
23
+ assert response.json() == {"status": "ready"}
24
+
25
+
26
+ @pytest.mark.anyio
27
+ async def test_liveness(client: AsyncClient) -> None:
28
+ response = await client.get("/api/v1/health/liveness")
29
+ assert response.status_code == 200
30
+ assert response.json() == {"status": "alive"}
fastapi_spawn/utils.py ADDED
@@ -0,0 +1,58 @@
1
+ """Utility helpers for fastapi-spawn."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import Path
7
+
8
+
9
+ def to_snake_case(name: str) -> str:
10
+ """Convert a string to snake_case."""
11
+ s = re.sub(r"[-\s]+", "_", name)
12
+ s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", s)
13
+ s = re.sub(r"([a-z\d])([A-Z])", r"\1_\2", s)
14
+ return s.lower()
15
+
16
+
17
+ def to_pascal_case(name: str) -> str:
18
+ """Convert a snake_case or kebab-case string to PascalCase."""
19
+ return "".join(word.capitalize() for word in re.split(r"[-_\s]+", name))
20
+
21
+
22
+ def to_kebab_case(name: str) -> str:
23
+ """Convert a string to kebab-case."""
24
+ s = re.sub(r"[_\s]+", "-", name)
25
+ s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1-\2", s)
26
+ s = re.sub(r"([a-z\d])([A-Z])", r"\1-\2", s)
27
+ return s.lower()
28
+
29
+
30
+ def render_tree(root: Path, prefix: str = "") -> str:
31
+ """
32
+ Recursively render a directory tree as a Rich-compatible string.
33
+ Used in --dry-run mode.
34
+ """
35
+ lines: list[str] = []
36
+ try:
37
+ items = sorted(root.iterdir(), key=lambda p: (p.is_file(), p.name))
38
+ except PermissionError:
39
+ return ""
40
+ for i, item in enumerate(items):
41
+ connector = "└── " if i == len(items) - 1 else "├── "
42
+ lines.append(prefix + connector + item.name)
43
+ if item.is_dir():
44
+ extension = " " if i == len(items) - 1 else "│ "
45
+ lines.append(render_tree(item, prefix + extension))
46
+ return "\n".join(filter(None, lines))
47
+
48
+
49
+ def collect_dry_run_paths(root: Path) -> list[str]:
50
+ """
51
+ Walk a directory and return all relative file paths as strings.
52
+ Used by the generator in dry-run mode.
53
+ """
54
+ paths = []
55
+ for path in sorted(root.rglob("*")):
56
+ if path.is_file():
57
+ paths.append(str(path.relative_to(root)))
58
+ return paths
@@ -0,0 +1,67 @@
1
+ """Input validators for fastapi-spawn."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import Path
7
+
8
+ from fastapi_spawn.constants import ORM, ORM_DB_COMPAT, Database
9
+
10
+
11
+ def validate_project_name(name: str) -> str:
12
+ """
13
+ Validate that a project name is a valid Python package identifier
14
+ (allows hyphens which are converted to underscores).
15
+ Returns the cleaned name or raises ValueError.
16
+ """
17
+ cleaned = name.strip()
18
+ if not cleaned:
19
+ raise ValueError("Project name cannot be empty.")
20
+ if not re.match(r"^[a-zA-Z][a-zA-Z0-9_-]*$", cleaned):
21
+ raise ValueError(
22
+ f"Invalid project name '{cleaned}'. "
23
+ "Use letters, numbers, hyphens, or underscores. Must start with a letter."
24
+ )
25
+ if len(cleaned) > 64:
26
+ raise ValueError("Project name must be 64 characters or fewer.")
27
+ return cleaned
28
+
29
+
30
+ def validate_orm_db_compat(orm: ORM, db: Database) -> None:
31
+ """
32
+ Raise ValueError if the selected ORM is not compatible with the selected DB.
33
+ """
34
+ compatible_dbs = ORM_DB_COMPAT.get(orm, [])
35
+ if db not in compatible_dbs:
36
+ compat_str = ", ".join(d.value for d in compatible_dbs)
37
+ raise ValueError(
38
+ f"ORM '{orm.value}' is not compatible with database '{db.value}'. "
39
+ f"Compatible databases: {compat_str}"
40
+ )
41
+
42
+
43
+ def validate_output_dir(path: Path, force: bool) -> None:
44
+ """
45
+ Validate that the output directory can be safely created.
46
+ Raises ValueError if the directory exists and --force was not passed.
47
+ """
48
+ if path.exists() and not force:
49
+ raise ValueError(
50
+ f"Directory '{path}' already exists. Use --force to overwrite."
51
+ )
52
+
53
+
54
+ def questionary_validator(validate_fn): # type: ignore[no-untyped-def]
55
+ """
56
+ Wrap a validation function for use as a questionary validator.
57
+ Returns a callable that returns True on success or an error string on failure.
58
+ """
59
+
60
+ def _inner(val: str): # type: ignore[no-untyped-def]
61
+ try:
62
+ validate_fn(val)
63
+ return True
64
+ except ValueError as exc:
65
+ return str(exc)
66
+
67
+ return _inner
@@ -0,0 +1,262 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-spawn
3
+ Version: 0.1.0
4
+ Summary: A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options.
5
+ Project-URL: Homepage, https://github.com/Bishwajitgarai/fastapi-spawn
6
+ Project-URL: Documentation, https://github.com/Bishwajitgarai/fastapi-spawn#readme
7
+ Project-URL: Repository, https://github.com/Bishwajitgarai/fastapi-spawn
8
+ Project-URL: Bug Tracker, https://github.com/Bishwajitgarai/fastapi-spawn/issues
9
+ Project-URL: Changelog, https://github.com/Bishwajitgarai/fastapi-spawn/blob/main/CHANGELOG.md
10
+ Author-email: Bishwajit Garai <bishwajitgarai@gmail.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: boilerplate,cli,devtools,docker,fastapi,generator,jwt,project-generator,python,scaffold,sqlalchemy,template
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Code Generators
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: click>=8.1.0
29
+ Requires-Dist: jinja2>=3.1.4
30
+ Requires-Dist: questionary>=2.0.1
31
+ Requires-Dist: rich>=13.7.0
32
+ Requires-Dist: tomli>=2.0.1; python_version < '3.11'
33
+ Requires-Dist: typer>=0.12.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
36
+ Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
37
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
38
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ <div align="center">
43
+
44
+ # ⚡ fastapi-spawn
45
+
46
+ **The most complete FastAPI project scaffolding CLI — built for modern Python development.**
47
+
48
+ [![PyPI version](https://img.shields.io/pypi/v/fastapi-spawn.svg?color=cyan&style=flat-square)](https://pypi.org/project/fastapi-spawn/)
49
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi-spawn.svg?style=flat-square)](https://pypi.org/project/fastapi-spawn/)
50
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](LICENSE)
51
+ [![CI](https://img.shields.io/github/actions/workflow/status/Bishwajitgarai/fastapi-spawn/tests.yml?label=tests&style=flat-square)](https://github.com/Bishwajitgarai/fastapi-spawn/actions)
52
+
53
+ </div>
54
+
55
+ ---
56
+
57
+ ## Why fastapi-spawn?
58
+
59
+ `fastapi-spawn` generates **production-ready** FastAPI projects in seconds. No boilerplate, no guesswork — just run one command and get a fully structured, configured project with the exact stack you need.
60
+
61
+ | Feature | scaffold-fastapi | **fastapi-spawn** |
62
+ |---|---|---|
63
+ | Interactive TUI | Basic prompts | ✅ Rich questionary TUI |
64
+ | Databases | PostgreSQL, MongoDB, SQLite | ✅ + MySQL |
65
+ | ORM | None | ✅ SQLAlchemy 2.x, Tortoise, Beanie |
66
+ | Migrations | ❌ | ✅ Alembic (async), Aerich |
67
+ | Auth | ❌ | ✅ JWT, OAuth2, API Key |
68
+ | Message brokers | Redis, RabbitMQ | ✅ + Kafka |
69
+ | File storage | ❌ | ✅ AWS S3 (boto3) |
70
+ | AI integration | ❌ | ✅ OpenAI, Anthropic |
71
+ | Config style | Single URL | ✅ Individual fields + `@property` URL |
72
+ | Entry point | app/main.py | ✅ Root `main.py` (`uv run main.py`) |
73
+ | CI/CD | GitHub Actions | ✅ GitHub Actions + GitLab CI |
74
+ | Observability | ❌ | ✅ /health /readiness /liveness |
75
+ | Logging | None | ✅ loguru / structlog / standard |
76
+ | Package manager | uv | ✅ uv (with `[tool.uv]` config) |
77
+ | Dry-run mode | ❌ | ✅ Preview tree before generating |
78
+
79
+ ---
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ pip install fastapi-spawn
85
+ # or
86
+ uv pip install fastapi-spawn
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Quick Start
92
+
93
+ ```bash
94
+ # Fully interactive — guided TUI
95
+ fastapi-spawn new my-api
96
+
97
+ # One-liner (all flags)
98
+ fastapi-spawn new my-api \
99
+ --db postgresql \
100
+ --orm sqlalchemy \
101
+ --migration alembic \
102
+ --auth jwt \
103
+ --broker redis \
104
+ --storage s3 \
105
+ --ai openai \
106
+ --stack full \
107
+ --ci github \
108
+ --log-lib loguru
109
+
110
+ # Preview without writing files
111
+ fastapi-spawn new my-api --dry-run
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Generated Project Structure
117
+
118
+ ```
119
+ my-api/
120
+ ├── app/
121
+ │ ├── api/
122
+ │ │ └── v1/
123
+ │ │ ├── health.py # /health /readiness /liveness
124
+ │ │ └── auth.py # JWT login + refresh
125
+ │ ├── core/
126
+ │ │ ├── config.py # Pydantic Settings v2 (individual env fields)
127
+ │ │ ├── logging.py # loguru / structlog / standard
128
+ │ │ ├── exceptions.py # Custom exception hierarchy
129
+ │ │ ├── security.py # JWT + bcrypt (when auth enabled)
130
+ │ │ ├── storage.py # AWS S3 utils (when s3 chosen)
131
+ │ │ └── ai.py # OpenAI / Anthropic client (when AI chosen)
132
+ │ ├── db/
133
+ │ │ └── session.py # Async SQLAlchemy / Tortoise / Beanie
134
+ │ ├── models/ # ORM models
135
+ │ ├── schemas/ # Pydantic schemas
136
+ │ ├── services/ # Business logic layer
137
+ │ └── repositories/ # Data access layer
138
+ ├── tasks/ # Celery workers (root-level)
139
+ │ ├── celery_app.py
140
+ │ └── sample_tasks.py
141
+ ├── migrations/ # Alembic migrations
142
+ │ ├── env.py # Async-compatible env
143
+ │ └── versions/
144
+ ├── infra/
145
+ │ ├── docker/
146
+ │ ├── helm/
147
+ │ └── terraform/
148
+ ├── tests/
149
+ │ ├── conftest.py
150
+ │ └── test_health.py
151
+ ├── main.py # uv run main.py entry point
152
+ ├── alembic.ini
153
+ ├── Dockerfile # Uses uv for fast builds
154
+ ├── docker-compose.yml # All services pre-configured
155
+ ├── .env # gitignored
156
+ ├── .env.example
157
+ ├── .gitignore
158
+ ├── .pre-commit-config.yaml
159
+ ├── Makefile
160
+ └── pyproject.toml # uv-compatible
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Environment Variables
166
+
167
+ `fastapi-spawn` generates **individual env fields** (not URL strings) for every service, assembled into URLs via `@property`:
168
+
169
+ ```env
170
+ # PostgreSQL — assembled into DATABASE_URL by settings
171
+ POSTGRES_USER=postgres
172
+ POSTGRES_PASSWORD=postgres
173
+ POSTGRES_HOST=localhost
174
+ POSTGRES_PORT=5432
175
+ POSTGRES_DB=my_api_db
176
+
177
+ # OpenAI — supports custom base URL for Azure / LM Studio
178
+ OPENAI_API_KEY=sk-placeholder
179
+ OPENAI_MODEL=gpt-4o
180
+ OPENAI_BASE_URL= # blank = api.openai.com
181
+
182
+ # Redis
183
+ REDIS_HOST=localhost
184
+ REDIS_PORT=6379
185
+ REDIS_PASSWORD=
186
+ REDIS_DB=0
187
+ ```
188
+
189
+ ---
190
+
191
+ ## All Options
192
+
193
+ ```
194
+ fastapi-spawn new [OPTIONS] PROJECT_NAME
195
+
196
+ --db postgresql | mysql | mongodb | sqlite | none
197
+ --orm sqlalchemy | tortoise | beanie | none
198
+ --migration alembic | aerich | none
199
+ --auth jwt | oauth2 | api-key | none
200
+ --broker redis | rabbitmq | kafka | none
201
+ --cache redis | memcached | none
202
+ --storage s3 | local | none
203
+ --ai openai | anthropic | none
204
+ --stack minimal | standard | full
205
+ --ci github | gitlab | both | none
206
+ --log-lib loguru | structlog | standard
207
+ --no-docker Skip Docker files
208
+ --no-tests Skip test suite
209
+ --dry-run Preview file tree only
210
+ --force / -f Overwrite existing directory
211
+ --output / -o Output directory (default: .)
212
+ ```
213
+
214
+ ### Subcommands
215
+
216
+ ```bash
217
+ fastapi-spawn list-templates # Show all options + ORM/DB compatibility
218
+ fastapi-spawn validate FILE # Validate a .fastapi-spawn.toml
219
+ ```
220
+
221
+ ---
222
+
223
+ ## After Scaffolding
224
+
225
+ ```bash
226
+ cd my-api
227
+ uv sync # Install all dependencies
228
+ uv run alembic upgrade head # Run DB migrations (if alembic)
229
+ docker compose up --build # Start all services
230
+ # or
231
+ uv run main.py # Run locally
232
+ ```
233
+
234
+ ---
235
+
236
+ ## ORM ↔ Database Compatibility
237
+
238
+ | ORM | Compatible Databases |
239
+ |---|---|
240
+ | `sqlalchemy` | postgresql, mysql, sqlite |
241
+ | `tortoise` | postgresql, mysql, sqlite |
242
+ | `beanie` | mongodb |
243
+ | `none` | any |
244
+
245
+ ---
246
+
247
+ ## Contributing
248
+
249
+ ```bash
250
+ git clone https://github.com/Bishwajitgarai/fastapi-spawn
251
+ cd fastapi-spawn
252
+ uv sync --all-extras
253
+ uv run pytest
254
+ ```
255
+
256
+ PRs are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
257
+
258
+ ---
259
+
260
+ ## License
261
+
262
+ MIT © [Bishwajit Garai](https://github.com/Bishwajitgarai)