@intentsolutionsio/fullstack-starter-pack 1.0.0

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.
@@ -0,0 +1,674 @@
1
+ ---
2
+ name: fastapi-scaffold
3
+ description: >
4
+ Generate production-ready FastAPI REST API with async and authentication
5
+ shortcut: fas
6
+ category: backend
7
+ difficulty: intermediate
8
+ estimated_time: 5-10 minutes
9
+ ---
10
+ # FastAPI Scaffold
11
+
12
+ Generates a complete FastAPI REST API boilerplate with async support, authentication, database integration, and testing setup.
13
+
14
+ ## What This Command Does
15
+
16
+ **Generated Project:**
17
+ - FastAPI with Python 3.10+
18
+ - Async/await throughout
19
+ - JWT authentication
20
+ - Database integration (SQLAlchemy async)
21
+ - Pydantic models & validation
22
+ - Automatic OpenAPI docs
23
+ - Testing setup (Pytest + httpx)
24
+ - Docker configuration
25
+ - Example CRUD endpoints
26
+
27
+ **Output:** Complete API project ready for development
28
+
29
+ **Time:** 5-10 minutes
30
+
31
+ ---
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ # Generate full FastAPI API
37
+ /fastapi-scaffold "Task Management API"
38
+
39
+ # Shortcut
40
+ /fas "E-commerce API"
41
+
42
+ # With specific database
43
+ /fas "Blog API" --database postgresql
44
+
45
+ # With authentication type
46
+ /fas "Social API" --auth jwt --database postgresql
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Example Output
52
+
53
+ **Input:**
54
+ ```
55
+ /fas "Task Management API" --database postgresql
56
+ ```
57
+
58
+ **Generated Project Structure:**
59
+ ```
60
+ task-api/
61
+ ├── app/
62
+ │ ├── api/
63
+ │ │ ├── deps.py # Dependencies
64
+ │ │ └── v1/
65
+ │ │ ├── __init__.py
66
+ │ │ ├── auth.py # Auth endpoints
67
+ │ │ └── tasks.py # Task endpoints
68
+ │ ├── core/
69
+ │ │ ├── config.py # Settings
70
+ │ │ ├── security.py # JWT, password hashing
71
+ │ │ └── database.py # Database connection
72
+ │ ├── models/ # SQLAlchemy models
73
+ │ │ ├── user.py
74
+ │ │ └── task.py
75
+ │ ├── schemas/ # Pydantic schemas
76
+ │ │ ├── user.py
77
+ │ │ └── task.py
78
+ │ ├── services/ # Business logic
79
+ │ │ ├── auth.py
80
+ │ │ └── task.py
81
+ │ ├── db/
82
+ │ │ └── init_db.py # Database initialization
83
+ │ ├── main.py # FastAPI app
84
+ │ └── __init__.py
85
+ ├── tests/
86
+ │ ├── conftest.py
87
+ │ ├── test_auth.py
88
+ │ └── test_tasks.py
89
+ ├── alembic/ # Database migrations
90
+ │ ├── versions/
91
+ │ └── env.py
92
+ ├── .env.example
93
+ ├── .gitignore
94
+ ├── requirements.txt
95
+ ├── pyproject.toml
96
+ ├── Dockerfile
97
+ ├── docker-compose.yml
98
+ └── README.md
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Generated Files
104
+
105
+ ### 1. **app/main.py** (Application Entry)
106
+
107
+ ```python
108
+ from fastapi import FastAPI
109
+ from fastapi.middleware.cors import CORSMiddleware
110
+ from fastapi.middleware.trustedhost import TrustedHostMiddleware
111
+
112
+ from app.api.v1 import auth, tasks
113
+ from app.core.config import settings
114
+ from app.core.database import engine
115
+ from app.models import Base
116
+
117
+ # Create database tables
118
+ Base.metadata.create_all(bind=engine)
119
+
120
+ app = FastAPI(
121
+ title=settings.PROJECT_NAME,
122
+ version="1.0.0",
123
+ openapi_url=f"{settings.API_V1_STR}/openapi.json",
124
+ docs_url=f"{settings.API_V1_STR}/docs",
125
+ )
126
+
127
+ # CORS middleware
128
+ app.add_middleware(
129
+ CORSMiddleware,
130
+ allow_origins=settings.ALLOWED_ORIGINS,
131
+ allow_credentials=True,
132
+ allow_methods=["*"],
133
+ allow_headers=["*"],
134
+ )
135
+
136
+ # Security middleware
137
+ app.add_middleware(
138
+ TrustedHostMiddleware,
139
+ allowed_hosts=settings.ALLOWED_HOSTS
140
+ )
141
+
142
+ @app.get("/health")
143
+ async def health_check():
144
+ return {
145
+ "status": "ok",
146
+ "version": "1.0.0"
147
+ }
148
+
149
+ # Include routers
150
+ app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
151
+ app.include_router(tasks.router, prefix=f"{settings.API_V1_STR}/tasks", tags=["tasks"])
152
+
153
+ if __name__ == "__main__":
154
+ import uvicorn
155
+ uvicorn.run(app, host="0.0.0.0", port=8000)
156
+ ```
157
+
158
+ ### 2. **app/core/config.py** (Settings)
159
+
160
+ ```python
161
+ from pydantic_settings import BaseSettings
162
+ from typing import List
163
+
164
+ class Settings(BaseSettings):
165
+ PROJECT_NAME: str = "Task API"
166
+ API_V1_STR: str = "/api/v1"
167
+
168
+ # Database
169
+ DATABASE_URL: str
170
+
171
+ # Security
172
+ SECRET_KEY: str
173
+ ALGORITHM: str = "HS256"
174
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 days
175
+
176
+ # CORS
177
+ ALLOWED_ORIGINS: List[str] = ["http://localhost:3000"]
178
+ ALLOWED_HOSTS: List[str] = ["*"]
179
+
180
+ class Config:
181
+ env_file = ".env"
182
+ case_sensitive = True
183
+
184
+ settings = Settings()
185
+ ```
186
+
187
+ ### 3. **app/core/security.py** (Authentication)
188
+
189
+ ```python
190
+ from datetime import datetime, timedelta
191
+ from typing import Optional
192
+ from jose import jwt, JWTError
193
+ from passlib.context import CryptContext
194
+ from fastapi import Depends, HTTPException, status
195
+ from fastapi.security import OAuth2PasswordBearer
196
+
197
+ from app.core.config import settings
198
+
199
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
200
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")
201
+
202
+ def verify_password(plain_password: str, hashed_password: str) -> bool:
203
+ return pwd_context.verify(plain_password, hashed_password)
204
+
205
+ def get_password_hash(password: str) -> str:
206
+ return pwd_context.hash(password)
207
+
208
+ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
209
+ to_encode = data.copy()
210
+
211
+ if expires_delta:
212
+ expire = datetime.utcnow() + expires_delta
213
+ else:
214
+ expire = datetime.utcnow() + timedelta(
215
+ minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
216
+ )
217
+
218
+ to_encode.update({"exp": expire})
219
+ encoded_jwt = jwt.encode(
220
+ to_encode,
221
+ settings.SECRET_KEY,
222
+ algorithm=settings.ALGORITHM
223
+ )
224
+ return encoded_jwt
225
+
226
+ def decode_access_token(token: str) -> dict:
227
+ try:
228
+ payload = jwt.decode(
229
+ token,
230
+ settings.SECRET_KEY,
231
+ algorithms=[settings.ALGORITHM]
232
+ )
233
+ return payload
234
+ except JWTError:
235
+ raise HTTPException(
236
+ status_code=status.HTTP_401_UNAUTHORIZED,
237
+ detail="Could not validate credentials",
238
+ headers={"WWW-Authenticate": "Bearer"},
239
+ )
240
+ ```
241
+
242
+ ### 4. **app/core/database.py** (Database Setup)
243
+
244
+ ```python
245
+ from sqlalchemy import create_engine
246
+ from sqlalchemy.ext.declarative import declarative_base
247
+ from sqlalchemy.orm import sessionmaker
248
+ from app.core.config import settings
249
+
250
+ engine = create_engine(
251
+ settings.DATABASE_URL,
252
+ pool_pre_ping=True,
253
+ echo=False
254
+ )
255
+
256
+ SessionLocal = sessionmaker(
257
+ autocommit=False,
258
+ autoflush=False,
259
+ bind=engine
260
+ )
261
+
262
+ Base = declarative_base()
263
+
264
+ def get_db():
265
+ db = SessionLocal()
266
+ try:
267
+ yield db
268
+ finally:
269
+ db.close()
270
+ ```
271
+
272
+ ### 5. **app/models/user.py** (User Model)
273
+
274
+ ```python
275
+ from sqlalchemy import Column, String, DateTime
276
+ from sqlalchemy.orm import relationship
277
+ from datetime import datetime
278
+ import uuid
279
+
280
+ from app.core.database import Base
281
+
282
+ class User(Base):
283
+ __tablename__ = "users"
284
+
285
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
286
+ email = Column(String, unique=True, index=True, nullable=False)
287
+ hashed_password = Column(String, nullable=False)
288
+ name = Column(String, nullable=False)
289
+ created_at = Column(DateTime, default=datetime.utcnow)
290
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
291
+
292
+ # Relationships
293
+ tasks = relationship("Task", back_populates="owner", cascade="all, delete-orphan")
294
+ ```
295
+
296
+ ### 6. **app/models/task.py** (Task Model)
297
+
298
+ ```python
299
+ from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey
300
+ from sqlalchemy.orm import relationship
301
+ from datetime import datetime
302
+ import uuid
303
+
304
+ from app.core.database import Base
305
+
306
+ class Task(Base):
307
+ __tablename__ = "tasks"
308
+
309
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
310
+ title = Column(String, nullable=False)
311
+ description = Column(String, nullable=True)
312
+ completed = Column(Boolean, default=False)
313
+ user_id = Column(String, ForeignKey("users.id", ondelete="CASCADE"))
314
+ created_at = Column(DateTime, default=datetime.utcnow)
315
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
316
+
317
+ # Relationships
318
+ owner = relationship("User", back_populates="tasks")
319
+ ```
320
+
321
+ ### 7. **app/schemas/user.py** (Pydantic Schemas)
322
+
323
+ ```python
324
+ from pydantic import BaseModel, EmailStr
325
+ from datetime import datetime
326
+ from typing import Optional
327
+
328
+ class UserBase(BaseModel):
329
+ email: EmailStr
330
+ name: str
331
+
332
+ class UserCreate(UserBase):
333
+ password: str
334
+
335
+ class UserUpdate(BaseModel):
336
+ name: Optional[str] = None
337
+ email: Optional[EmailStr] = None
338
+
339
+ class UserInDB(UserBase):
340
+ id: str
341
+ created_at: datetime
342
+ updated_at: datetime
343
+
344
+ class Config:
345
+ from_attributes = True
346
+
347
+ class User(UserInDB):
348
+ pass
349
+
350
+ class Token(BaseModel):
351
+ access_token: str
352
+ token_type: str
353
+
354
+ class TokenData(BaseModel):
355
+ email: Optional[str] = None
356
+ ```
357
+
358
+ ### 8. **app/schemas/task.py** (Task Schemas)
359
+
360
+ ```python
361
+ from pydantic import BaseModel
362
+ from datetime import datetime
363
+ from typing import Optional
364
+
365
+ class TaskBase(BaseModel):
366
+ title: str
367
+ description: Optional[str] = None
368
+
369
+ class TaskCreate(TaskBase):
370
+ pass
371
+
372
+ class TaskUpdate(BaseModel):
373
+ title: Optional[str] = None
374
+ description: Optional[str] = None
375
+ completed: Optional[bool] = None
376
+
377
+ class TaskInDB(TaskBase):
378
+ id: str
379
+ completed: bool
380
+ user_id: str
381
+ created_at: datetime
382
+ updated_at: datetime
383
+
384
+ class Config:
385
+ from_attributes = True
386
+
387
+ class Task(TaskInDB):
388
+ pass
389
+ ```
390
+
391
+ ### 9. **app/api/deps.py** (Dependencies)
392
+
393
+ ```python
394
+ from typing import Generator
395
+ from fastapi import Depends, HTTPException, status
396
+ from sqlalchemy.orm import Session
397
+
398
+ from app.core.database import get_db
399
+ from app.core.security import oauth2_scheme, decode_access_token
400
+ from app.models.user import User
401
+
402
+ def get_current_user(
403
+ db: Session = Depends(get_db),
404
+ token: str = Depends(oauth2_scheme)
405
+ ) -> User:
406
+ payload = decode_access_token(token)
407
+ email: str = payload.get("sub")
408
+
409
+ if email is None:
410
+ raise HTTPException(
411
+ status_code=status.HTTP_401_UNAUTHORIZED,
412
+ detail="Could not validate credentials"
413
+ )
414
+
415
+ user = db.query(User).filter(User.email == email).first()
416
+
417
+ if user is None:
418
+ raise HTTPException(
419
+ status_code=status.HTTP_401_UNAUTHORIZED,
420
+ detail="User not found"
421
+ )
422
+
423
+ return user
424
+ ```
425
+
426
+ ### 10. **app/api/v1/tasks.py** (Task Endpoints)
427
+
428
+ ```python
429
+ from typing import List
430
+ from fastapi import APIRouter, Depends, HTTPException, status
431
+ from sqlalchemy.orm import Session
432
+
433
+ from app.api.deps import get_current_user
434
+ from app.core.database import get_db
435
+ from app.models.user import User
436
+ from app.models.task import Task as TaskModel
437
+ from app.schemas.task import Task, TaskCreate, TaskUpdate
438
+
439
+ router = APIRouter()
440
+
441
+ @router.get("/", response_model=List[Task])
442
+ async def list_tasks(
443
+ db: Session = Depends(get_db),
444
+ current_user: User = Depends(get_current_user),
445
+ skip: int = 0,
446
+ limit: int = 100
447
+ ):
448
+ tasks = db.query(TaskModel)\
449
+ .filter(TaskModel.user_id == current_user.id)\
450
+ .offset(skip)\
451
+ .limit(limit)\
452
+ .all()
453
+ return tasks
454
+
455
+ @router.post("/", response_model=Task, status_code=status.HTTP_201_CREATED)
456
+ async def create_task(
457
+ task_in: TaskCreate,
458
+ db: Session = Depends(get_db),
459
+ current_user: User = Depends(get_current_user)
460
+ ):
461
+ task = TaskModel(
462
+ **task_in.dict(),
463
+ user_id=current_user.id
464
+ )
465
+ db.add(task)
466
+ db.commit()
467
+ db.refresh(task)
468
+ return task
469
+
470
+ @router.get("/{task_id}", response_model=Task)
471
+ async def get_task(
472
+ task_id: str,
473
+ db: Session = Depends(get_db),
474
+ current_user: User = Depends(get_current_user)
475
+ ):
476
+ task = db.query(TaskModel)\
477
+ .filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
478
+ .first()
479
+
480
+ if not task:
481
+ raise HTTPException(
482
+ status_code=status.HTTP_404_NOT_FOUND,
483
+ detail="Task not found"
484
+ )
485
+
486
+ return task
487
+
488
+ @router.patch("/{task_id}", response_model=Task)
489
+ async def update_task(
490
+ task_id: str,
491
+ task_in: TaskUpdate,
492
+ db: Session = Depends(get_db),
493
+ current_user: User = Depends(get_current_user)
494
+ ):
495
+ task = db.query(TaskModel)\
496
+ .filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
497
+ .first()
498
+
499
+ if not task:
500
+ raise HTTPException(
501
+ status_code=status.HTTP_404_NOT_FOUND,
502
+ detail="Task not found"
503
+ )
504
+
505
+ for field, value in task_in.dict(exclude_unset=True).items():
506
+ setattr(task, field, value)
507
+
508
+ db.commit()
509
+ db.refresh(task)
510
+ return task
511
+
512
+ @router.delete("/{task_id}", status_code=status.HTTP_204_NO_CONTENT)
513
+ async def delete_task(
514
+ task_id: str,
515
+ db: Session = Depends(get_db),
516
+ current_user: User = Depends(get_current_user)
517
+ ):
518
+ task = db.query(TaskModel)\
519
+ .filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
520
+ .first()
521
+
522
+ if not task:
523
+ raise HTTPException(
524
+ status_code=status.HTTP_404_NOT_FOUND,
525
+ detail="Task not found"
526
+ )
527
+
528
+ db.delete(task)
529
+ db.commit()
530
+ ```
531
+
532
+ ### 11. **tests/test_tasks.py** (Pytest Tests)
533
+
534
+ ```python
535
+ import pytest
536
+ from httpx import AsyncClient
537
+ from app.main import app
538
+
539
+ @pytest.mark.asyncio
540
+ async def test_create_task(client: AsyncClient, test_user_token):
541
+ response = await client.post(
542
+ "/api/v1/tasks/",
543
+ json={
544
+ "title": "Test Task",
545
+ "description": "Test description"
546
+ },
547
+ headers={"Authorization": f"Bearer {test_user_token}"}
548
+ )
549
+
550
+ assert response.status_code == 201
551
+ data = response.json()
552
+ assert data["title"] == "Test Task"
553
+ assert "id" in data
554
+
555
+ @pytest.mark.asyncio
556
+ async def test_list_tasks(client: AsyncClient, test_user_token):
557
+ response = await client.get(
558
+ "/api/v1/tasks/",
559
+ headers={"Authorization": f"Bearer {test_user_token}"}
560
+ )
561
+
562
+ assert response.status_code == 200
563
+ data = response.json()
564
+ assert isinstance(data, list)
565
+
566
+ @pytest.mark.asyncio
567
+ async def test_create_task_unauthorized(client: AsyncClient):
568
+ response = await client.post(
569
+ "/api/v1/tasks/",
570
+ json={"title": "Test"}
571
+ )
572
+
573
+ assert response.status_code == 401
574
+ ```
575
+
576
+ ### 12. **requirements.txt**
577
+
578
+ ```
579
+ fastapi==0.109.0
580
+ uvicorn[standard]==0.27.0
581
+ sqlalchemy==2.0.25
582
+ pydantic==2.5.3
583
+ pydantic-settings==2.1.0
584
+ python-jose[cryptography]==3.3.0
585
+ passlib[bcrypt]==1.7.4
586
+ python-multipart==0.0.6
587
+ alembic==1.13.1
588
+ psycopg2-binary==2.9.9
589
+
590
+ # Development
591
+ pytest==7.4.4
592
+ pytest-asyncio==0.23.3
593
+ httpx==0.26.0
594
+ black==23.12.1
595
+ isort==5.13.2
596
+ mypy==1.8.0
597
+ ```
598
+
599
+ ---
600
+
601
+ ## Features
602
+
603
+ **Performance:**
604
+ - Async/await for high concurrency
605
+ - Background tasks support
606
+ - WebSocket support (optional)
607
+ - Automatic Pydantic validation
608
+
609
+ **Documentation:**
610
+ - Auto-generated OpenAPI (Swagger)
611
+ - ReDoc documentation
612
+ - Type hints throughout
613
+
614
+ **Database:**
615
+ - SQLAlchemy ORM with async support
616
+ - Alembic migrations
617
+ - Connection pooling
618
+
619
+ **Security:**
620
+ - JWT authentication
621
+ - Password hashing (bcrypt)
622
+ - CORS middleware
623
+ - Trusted host middleware
624
+
625
+ **Testing:**
626
+ - Pytest with async support
627
+ - Test fixtures
628
+ - Coverage reporting
629
+
630
+ ---
631
+
632
+ ## Getting Started
633
+
634
+ **1. Install dependencies:**
635
+ ```bash
636
+ pip install -r requirements.txt
637
+ ```
638
+
639
+ **2. Configure environment:**
640
+ ```bash
641
+ cp .env.example .env
642
+ # Edit .env with your database URL and secrets
643
+ ```
644
+
645
+ **3. Run database migrations:**
646
+ ```bash
647
+ alembic upgrade head
648
+ ```
649
+
650
+ **4. Start development server:**
651
+ ```bash
652
+ uvicorn app.main:app --reload
653
+ ```
654
+
655
+ **5. View API docs:**
656
+ - Swagger UI: http://localhost:8000/api/v1/docs
657
+ - ReDoc: http://localhost:8000/api/v1/redoc
658
+
659
+ **6. Run tests:**
660
+ ```bash
661
+ pytest
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Related Commands
667
+
668
+ - `/express-api-scaffold` - Generate Express.js boilerplate
669
+ - Backend Architect (agent) - Architecture review
670
+ - API Builder (agent) - API design guidance
671
+
672
+ ---
673
+
674
+ **Build high-performance APIs. Scale effortlessly. Deploy confidently.**