Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:20:34 +08:00
commit 10052112c1
29 changed files with 8734 additions and 0 deletions

421
commands/auth-setup.md Normal file
View File

@@ -0,0 +1,421 @@
---
description: Generate authentication boilerplate with JWT, OAuth, and session support
shortcut: as
category: backend
difficulty: intermediate
estimated_time: 5-10 minutes
---
# Auth Setup
Generates complete authentication boilerplate including JWT, OAuth (Google/GitHub), session management, and password reset flows.
## What This Command Does
**Generated Auth System:**
- JWT authentication with refresh tokens
- OAuth2 (Google, GitHub, Facebook)
- Password hashing (bcrypt)
- Email verification
- Password reset flow
- Session management
- Rate limiting on auth endpoints
- Authentication middleware
**Output:** Complete authentication system ready for production
**Time:** 5-10 minutes
---
## Usage
```bash
# Generate full auth system
/auth-setup jwt
# Shortcut
/as oauth --providers google,github
# With specific features
/as jwt --features email-verification,password-reset,2fa
```
---
## Example Output
### **JWT Authentication**
**auth.service.ts:**
```typescript
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
import { User } from './models/User'
export class AuthService {
async register(email: string, password: string, name: string) {
// Check if user exists
const existing = await User.findOne({ email })
if (existing) {
throw new Error('Email already registered')
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 12)
// Create user
const user = await User.create({
email,
password: hashedPassword,
name,
emailVerified: false
})
// Generate verification token
const verificationToken = this.generateToken({ userId: user.id, type: 'verify' }, '24h')
// Send verification email (implement sendEmail)
await this.sendVerificationEmail(email, verificationToken)
// Generate auth tokens
const accessToken = this.generateAccessToken(user)
const refreshToken = this.generateRefreshToken(user)
return {
user: { id: user.id, email: user.email, name: user.name },
accessToken,
refreshToken
}
}
async login(email: string, password: string) {
const user = await User.findOne({ email })
if (!user) {
throw new Error('Invalid credentials')
}
const validPassword = await bcrypt.compare(password, user.password)
if (!validPassword) {
throw new Error('Invalid credentials')
}
if (!user.emailVerified) {
throw new Error('Please verify your email')
}
const accessToken = this.generateAccessToken(user)
const refreshToken = this.generateRefreshToken(user)
return {
user: { id: user.id, email: user.email, name: user.name },
accessToken,
refreshToken
}
}
async refreshToken(refreshToken: string) {
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!) as any
const user = await User.findById(decoded.userId)
if (!user) {
throw new Error('User not found')
}
const accessToken = this.generateAccessToken(user)
return { accessToken }
} catch (error) {
throw new Error('Invalid refresh token')
}
}
async verifyEmail(token: string) {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
if (decoded.type !== 'verify') {
throw new Error('Invalid token type')
}
await User.findByIdAndUpdate(decoded.userId, { emailVerified: true })
return { message: 'Email verified successfully' }
}
async requestPasswordReset(email: string) {
const user = await User.findOne({ email })
if (!user) {
// Don't reveal if user exists
return { message: 'If email exists, reset link sent' }
}
const resetToken = this.generateToken({ userId: user.id, type: 'reset' }, '1h')
await this.sendPasswordResetEmail(email, resetToken)
return { message: 'If email exists, reset link sent' }
}
async resetPassword(token: string, newPassword: string) {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
if (decoded.type !== 'reset') {
throw new Error('Invalid token type')
}
const hashedPassword = await bcrypt.hash(newPassword, 12)
await User.findByIdAndUpdate(decoded.userId, { password: hashedPassword })
return { message: 'Password reset successfully' }
}
private generateAccessToken(user: any) {
return jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
)
}
private generateRefreshToken(user: any) {
return jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: '7d' }
)
}
private generateToken(payload: any, expiresIn: string) {
return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn })
}
private async sendVerificationEmail(email: string, token: string) {
// Implement with SendGrid, Resend, etc.
}
private async sendPasswordResetEmail(email: string, token: string) {
// Implement with SendGrid, Resend, etc.
}
}
```
### **OAuth2 Setup (Google)**
**oauth.controller.ts:**
```typescript
import { OAuth2Client } from 'google-auth-library'
const googleClient = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI
)
export class OAuthController {
async googleLogin(req: Request, res: Response) {
const authUrl = googleClient.generateAuthUrl({
access_type: 'offline',
scope: ['profile', 'email']
})
res.redirect(authUrl)
}
async googleCallback(req: Request, res: Response) {
const { code } = req.query
const { tokens } = await googleClient.getToken(code as string)
googleClient.setCredentials(tokens)
const ticket = await googleClient.verifyIdToken({
idToken: tokens.id_token!,
audience: process.env.GOOGLE_CLIENT_ID
})
const payload = ticket.getPayload()
if (!payload) {
throw new Error('Invalid token')
}
// Find or create user
let user = await User.findOne({ email: payload.email })
if (!user) {
user = await User.create({
email: payload.email,
name: payload.name,
avatar: payload.picture,
emailVerified: true,
provider: 'google',
providerId: payload.sub
})
}
// Generate tokens
const accessToken = generateAccessToken(user)
const refreshToken = generateRefreshToken(user)
// Redirect with tokens
res.redirect(`/auth/success?token=${accessToken}&refresh=${refreshToken}`)
}
}
```
### **Authentication Middleware**
**auth.middleware.ts:**
```typescript
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'
declare global {
namespace Express {
interface Request {
user?: {
userId: string
email: string
}
}
}
}
export async function authenticate(req: Request, res: Response, next: NextFunction) {
try {
const authHeader = req.headers.authorization
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' })
}
const token = authHeader.split(' ')[1]
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
req.user = {
userId: decoded.userId,
email: decoded.email
}
next()
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: 'Token expired' })
}
return res.status(401).json({ error: 'Invalid token' })
}
}
export function authorize(...roles: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' })
}
const user = await User.findById(req.user.userId)
if (!user || !roles.includes(user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' })
}
next()
}
}
```
### **Rate Limiting**
```typescript
import rateLimit from 'express-rate-limit'
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 requests per window
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false
})
// Usage
app.post('/api/auth/login', authLimiter, authController.login)
```
---
## Environment Variables
```bash
# JWT
JWT_SECRET=your-super-secret-key-min-32-chars
JWT_REFRESH_SECRET=your-refresh-secret-key
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# OAuth - Google
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/google/callback
# OAuth - GitHub
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_REDIRECT_URI=http://localhost:3000/api/auth/github/callback
# Email
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASSWORD=your-sendgrid-api-key
FROM_EMAIL=[email protected]
```
---
## API Routes
```typescript
// routes/auth.routes.ts
import { Router } from 'express'
import { AuthController } from '../controllers/auth.controller'
import { authenticate } from '../middleware/auth.middleware'
import { authLimiter } from '../middleware/rate-limit'
const router = Router()
const authController = new AuthController()
// Registration & Login
router.post('/register', authController.register)
router.post('/login', authLimiter, authController.login)
router.post('/refresh', authController.refreshToken)
router.post('/logout', authenticate, authController.logout)
// Email Verification
router.post('/verify-email', authController.verifyEmail)
router.post('/resend-verification', authController.resendVerification)
// Password Reset
router.post('/forgot-password', authLimiter, authController.forgotPassword)
router.post('/reset-password', authController.resetPassword)
// OAuth
router.get('/google', authController.googleLogin)
router.get('/google/callback', authController.googleCallback)
router.get('/github', authController.githubLogin)
router.get('/github/callback', authController.githubCallback)
// Profile
router.get('/me', authenticate, authController.getProfile)
router.patch('/me', authenticate, authController.updateProfile)
router.post('/change-password', authenticate, authController.changePassword)
export default router
```
---
## Related Commands
- `/env-config-setup` - Generate environment config
- `/express-api-scaffold` - Generate Express API
- `/fastapi-scaffold` - Generate FastAPI
---
**Secure authentication. Easy integration. Production-ready.**

View File

@@ -0,0 +1,342 @@
---
description: Generate React components from descriptions with TypeScript and tests
shortcut: cg
category: frontend
difficulty: beginner
estimated_time: 2-5 minutes
---
# Component Generator
Generates production-ready React components from natural language descriptions, including TypeScript types, styling, and tests.
## What This Command Does
**Component Generation:**
- Creates React component with TypeScript
- Generates prop types and interfaces
- Adds basic styling (CSS modules or Tailwind)
- Creates test file (React Testing Library)
- Includes JSDoc comments
- Follows React best practices
**Output:** Complete component with types, styles, and tests
**Time:** 2-5 minutes
---
## Usage
```bash
# Generate component from description
/component-generator "Create a Card component with image, title, description, and action button"
# Shortcut
/cg "Button component with loading state and icon support"
# Specify framework/style
/cg "Modal dialog with animations" --style tailwind --test vitest
# Generate with directory
/cg "UserProfile component" --output src/components/UserProfile
```
---
## Example Output
**Input:**
```
/cg "Create a Button component with variants (primary, secondary, ghost), sizes (sm, md, lg), loading state, and icon support"
```
**Generated Files:**
**Button.tsx:**
```tsx
import { ButtonHTMLAttributes, ReactNode } from 'react'
import styles from './Button.module.css'
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** Button visual variant */
variant?: 'primary' | 'secondary' | 'ghost'
/** Button size */
size?: 'sm' | 'md' | 'lg'
/** Show loading spinner */
loading?: boolean
/** Icon to display before text */
icon?: ReactNode
/** Button content */
children: ReactNode
}
/**
* Button component with multiple variants and sizes
*
* @example
* ```tsx
* <Button variant="primary" size="md" onClick={handleClick}>
* Click me
* </Button>
* ```
*/
export function Button({
variant = 'primary',
size = 'md',
loading = false,
icon,
children,
disabled,
className = '',
...props
}: ButtonProps) {
return (
<button
className={`${styles.button} ${styles[variant]} ${styles[size]} ${className}`}
disabled={disabled || loading}
aria-busy={loading}
{...props}
>
{loading ? (
<span className={styles.spinner} aria-label="Loading" />
) : (
<>
{icon && <span className={styles.icon}>{icon}</span>}
{children}
</>
)}
</button>
)
}
```
**Button.module.css:**
```css
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
border: none;
border-radius: 0.375rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Variants */
.primary {
background: #3b82f6;
color: white;
}
.primary:hover:not(:disabled) {
background: #2563eb;
}
.secondary {
background: #e5e7eb;
color: #374151;
}
.ghost {
background: transparent;
color: #3b82f6;
}
/* Sizes */
.sm {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
.md {
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
.lg {
padding: 1rem 2rem;
font-size: 1.125rem;
}
.spinner {
display: inline-block;
width: 1em;
height: 1em;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
```
**Button.test.tsx:**
```tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button', () => {
it('renders button with text', () => {
render(<Button>Click me</Button>)
expect(screen.getByRole('button')).toHaveTextContent('Click me')
})
it('handles click events', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click</Button>)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('shows loading state', () => {
render(<Button loading>Submit</Button>)
const button = screen.getByRole('button')
expect(button).toHaveAttribute('aria-busy', 'true')
expect(button).toBeDisabled()
})
it('disables button when disabled prop is true', () => {
render(<Button disabled>Disabled</Button>)
expect(screen.getByRole('button')).toBeDisabled()
})
it('renders with icon', () => {
const icon = <span data-testid="icon"></span>
render(<Button icon={icon}>With Icon</Button>)
expect(screen.getByTestId('icon')).toBeInTheDocument()
})
it('applies correct variant classes', () => {
const { rerender } = render(<Button variant="primary">Primary</Button>)
expect(screen.getByRole('button')).toHaveClass('primary')
rerender(<Button variant="secondary">Secondary</Button>)
expect(screen.getByRole('button')).toHaveClass('secondary')
})
})
```
---
## Component Templates
### Common Components Generated
**1. Card Component:**
```tsx
<Card
image="/product.jpg"
title="Product Name"
description="Product description"
action={<Button>Buy Now</Button>}
/>
```
**2. Modal Component:**
```tsx
<Modal
isOpen={isOpen}
onClose={handleClose}
title="Confirm Action"
>
<p>Are you sure?</p>
</Modal>
```
**3. Form Field Component:**
```tsx
<FormField
label="Email"
type="email"
error={errors.email}
required
/>
```
**4. Dropdown Component:**
```tsx
<Dropdown
items={options}
value={selected}
onChange={handleChange}
placeholder="Select option"
/>
```
---
## Features
### Accessibility Built-In
- Semantic HTML elements
- ARIA attributes where needed
- Keyboard navigation support
- Focus management
- Screen reader announcements
### TypeScript Support
- Full type definitions
- Prop validation
- IntelliSense support
- Generic types where appropriate
### Testing Included
- Unit tests with React Testing Library
- Accessibility tests
- User interaction tests
- Edge case coverage
### Styling Options
- CSS Modules (default)
- Tailwind CSS
- Styled Components
- Emotion
- Plain CSS
---
## Best Practices Applied
**Component Structure:**
- Single responsibility
- Composable design
- Prop drilling avoided
- Performance optimized (React.memo where beneficial)
**Code Quality:**
- ESLint compliant
- Prettier formatted
- TypeScript strict mode
- JSDoc comments
**Testing:**
- 80%+ code coverage
- User-centric tests (not implementation details)
- Accessibility assertions
- Happy path + edge cases
---
## Related Commands
- `/css-utility-generator` - Generate utility CSS classes
- React Specialist (agent) - React architecture guidance
- UI/UX Expert (agent) - Design review
---
**Generate components in seconds. Ship features faster.**

View File

@@ -0,0 +1,620 @@
---
description: Generate utility CSS classes for spacing, colors, typography, and layout
shortcut: cug
category: frontend
difficulty: beginner
estimated_time: 2-5 minutes
---
# CSS Utility Generator
Generates utility CSS classes similar to Tailwind CSS for common styling needs, creating a custom utility-first CSS framework.
## What This Command Does
**Utility Class Generation:**
- Spacing utilities (margin, padding)
- Color utilities (background, text, border)
- Typography utilities (font size, weight, line height)
- Layout utilities (flexbox, grid, display)
- Responsive breakpoints
- Custom design tokens
**Output:** Complete utility CSS file with organized sections
**Time:** 2-5 minutes
---
## Usage
```bash
# Generate full utility class system
/css-utility-generator
# Shortcut
/cug
# Generate specific categories
/cug --categories spacing,colors,typography
# Custom design tokens
/cug --config design-tokens.json
# With custom breakpoints
/cug --breakpoints mobile:640px,tablet:768px,desktop:1024px
```
---
## Example Output
**Input:**
```
/cug --categories spacing,colors,flex
```
**Generated utilities.css:**
```css
/* ==========================================================================
Utility CSS Classes
Generated by CSS Utility Generator
========================================================================== */
/* Spacing Utilities
========================================================================== */
/* Margin */
.m-0 { margin: 0; }
.m-1 { margin: 0.25rem; } /* 4px */
.m-2 { margin: 0.5rem; } /* 8px */
.m-3 { margin: 0.75rem; } /* 12px */
.m-4 { margin: 1rem; } /* 16px */
.m-5 { margin: 1.25rem; } /* 20px */
.m-6 { margin: 1.5rem; } /* 24px */
.m-8 { margin: 2rem; } /* 32px */
.m-10 { margin: 2.5rem; } /* 40px */
.m-12 { margin: 3rem; } /* 48px */
.m-16 { margin: 4rem; } /* 64px */
.m-auto { margin: auto; }
/* Margin Top */
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mt-12 { margin-top: 3rem; }
/* Margin Right */
.mr-0 { margin-right: 0; }
.mr-1 { margin-right: 0.25rem; }
.mr-2 { margin-right: 0.5rem; }
.mr-3 { margin-right: 0.75rem; }
.mr-4 { margin-right: 1rem; }
.mr-6 { margin-right: 1.5rem; }
.mr-8 { margin-right: 2rem; }
.mr-auto { margin-right: auto; }
/* Margin Bottom */
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }
.mb-12 { margin-bottom: 3rem; }
/* Margin Left */
.ml-0 { margin-left: 0; }
.ml-1 { margin-left: 0.25rem; }
.ml-2 { margin-left: 0.5rem; }
.ml-3 { margin-left: 0.75rem; }
.ml-4 { margin-left: 1rem; }
.ml-6 { margin-left: 1.5rem; }
.ml-8 { margin-left: 2rem; }
.ml-auto { margin-left: auto; }
/* Margin X-axis (horizontal) */
.mx-0 { margin-left: 0; margin-right: 0; }
.mx-1 { margin-left: 0.25rem; margin-right: 0.25rem; }
.mx-2 { margin-left: 0.5rem; margin-right: 0.5rem; }
.mx-4 { margin-left: 1rem; margin-right: 1rem; }
.mx-auto { margin-left: auto; margin-right: auto; }
/* Margin Y-axis (vertical) */
.my-0 { margin-top: 0; margin-bottom: 0; }
.my-1 { margin-top: 0.25rem; margin-bottom: 0.25rem; }
.my-2 { margin-top: 0.5rem; margin-bottom: 0.5rem; }
.my-4 { margin-top: 1rem; margin-bottom: 1rem; }
.my-8 { margin-top: 2rem; margin-bottom: 2rem; }
/* Padding */
.p-0 { padding: 0; }
.p-1 { padding: 0.25rem; }
.p-2 { padding: 0.5rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.p-5 { padding: 1.25rem; }
.p-6 { padding: 1.5rem; }
.p-8 { padding: 2rem; }
.p-10 { padding: 2.5rem; }
.p-12 { padding: 3rem; }
/* Padding Top */
.pt-0 { padding-top: 0; }
.pt-1 { padding-top: 0.25rem; }
.pt-2 { padding-top: 0.5rem; }
.pt-4 { padding-top: 1rem; }
.pt-6 { padding-top: 1.5rem; }
.pt-8 { padding-top: 2rem; }
/* Padding Right */
.pr-0 { padding-right: 0; }
.pr-2 { padding-right: 0.5rem; }
.pr-4 { padding-right: 1rem; }
.pr-6 { padding-right: 1.5rem; }
.pr-8 { padding-right: 2rem; }
/* Padding Bottom */
.pb-0 { padding-bottom: 0; }
.pb-2 { padding-bottom: 0.5rem; }
.pb-4 { padding-bottom: 1rem; }
.pb-6 { padding-bottom: 1.5rem; }
.pb-8 { padding-bottom: 2rem; }
/* Padding Left */
.pl-0 { padding-left: 0; }
.pl-2 { padding-left: 0.5rem; }
.pl-4 { padding-left: 1rem; }
.pl-6 { padding-left: 1.5rem; }
.pl-8 { padding-left: 2rem; }
/* Padding X-axis */
.px-0 { padding-left: 0; padding-right: 0; }
.px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
.px-8 { padding-left: 2rem; padding-right: 2rem; }
/* Padding Y-axis */
.py-0 { padding-top: 0; padding-bottom: 0; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
/* Color Utilities
========================================================================== */
/* Background Colors */
.bg-white { background-color: #ffffff; }
.bg-gray-50 { background-color: #f9fafb; }
.bg-gray-100 { background-color: #f3f4f6; }
.bg-gray-200 { background-color: #e5e7eb; }
.bg-gray-300 { background-color: #d1d5db; }
.bg-gray-400 { background-color: #9ca3af; }
.bg-gray-500 { background-color: #6b7280; }
.bg-gray-600 { background-color: #4b5563; }
.bg-gray-700 { background-color: #374151; }
.bg-gray-800 { background-color: #1f2937; }
.bg-gray-900 { background-color: #111827; }
.bg-black { background-color: #000000; }
.bg-primary { background-color: #3b82f6; }
.bg-secondary { background-color: #6b7280; }
.bg-success { background-color: #10b981; }
.bg-danger { background-color: #ef4444; }
.bg-warning { background-color: #f59e0b; }
.bg-info { background-color: #3b82f6; }
.bg-transparent { background-color: transparent; }
/* Text Colors */
.text-white { color: #ffffff; }
.text-gray-50 { color: #f9fafb; }
.text-gray-100 { color: #f3f4f6; }
.text-gray-200 { color: #e5e7eb; }
.text-gray-300 { color: #d1d5db; }
.text-gray-400 { color: #9ca3af; }
.text-gray-500 { color: #6b7280; }
.text-gray-600 { color: #4b5563; }
.text-gray-700 { color: #374151; }
.text-gray-800 { color: #1f2937; }
.text-gray-900 { color: #111827; }
.text-black { color: #000000; }
.text-primary { color: #3b82f6; }
.text-secondary { color: #6b7280; }
.text-success { color: #10b981; }
.text-danger { color: #ef4444; }
.text-warning { color: #f59e0b; }
.text-info { color: #3b82f6; }
/* Flexbox Utilities
========================================================================== */
/* Display */
.flex { display: flex; }
.inline-flex { display: inline-flex; }
/* Flex Direction */
.flex-row { flex-direction: row; }
.flex-row-reverse { flex-direction: row-reverse; }
.flex-col { flex-direction: column; }
.flex-col-reverse { flex-direction: column-reverse; }
/* Flex Wrap */
.flex-wrap { flex-wrap: wrap; }
.flex-nowrap { flex-wrap: nowrap; }
.flex-wrap-reverse { flex-wrap: wrap-reverse; }
/* Justify Content */
.justify-start { justify-content: flex-start; }
.justify-end { justify-content: flex-end; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-around { justify-content: space-around; }
.justify-evenly { justify-content: space-evenly; }
/* Align Items */
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.items-center { align-items: center; }
.items-baseline { align-items: baseline; }
.items-stretch { align-items: stretch; }
/* Align Self */
.self-auto { align-self: auto; }
.self-start { align-self: flex-start; }
.self-end { align-self: flex-end; }
.self-center { align-self: center; }
.self-stretch { align-self: stretch; }
/* Gap */
.gap-0 { gap: 0; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.gap-8 { gap: 2rem; }
/* Flex Grow/Shrink */
.flex-1 { flex: 1 1 0%; }
.flex-auto { flex: 1 1 auto; }
.flex-initial { flex: 0 1 auto; }
.flex-none { flex: none; }
.flex-grow { flex-grow: 1; }
.flex-grow-0 { flex-grow: 0; }
.flex-shrink { flex-shrink: 1; }
.flex-shrink-0 { flex-shrink: 0; }
/* Responsive Breakpoints
========================================================================== */
@media (min-width: 640px) {
/* sm: spacing */
.sm\:m-0 { margin: 0; }
.sm\:m-4 { margin: 1rem; }
.sm\:p-4 { padding: 1rem; }
/* sm: flexbox */
.sm\:flex { display: flex; }
.sm\:flex-row { flex-direction: row; }
.sm\:justify-center { justify-content: center; }
}
@media (min-width: 768px) {
/* md: spacing */
.md\:m-0 { margin: 0; }
.md\:m-6 { margin: 1.5rem; }
.md\:p-6 { padding: 1.5rem; }
/* md: flexbox */
.md\:flex { display: flex; }
.md\:flex-row { flex-direction: row; }
.md\:justify-between { justify-content: space-between; }
}
@media (min-width: 1024px) {
/* lg: spacing */
.lg\:m-0 { margin: 0; }
.lg\:m-8 { margin: 2rem; }
.lg\:p-8 { padding: 2rem; }
/* lg: flexbox */
.lg\:flex { display: flex; }
.lg\:flex-row { flex-direction: row; }
.lg\:gap-8 { gap: 2rem; }
}
```
---
## Complete Utility Categories
### 1. Spacing (Margin & Padding)
```css
/* Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24 */
/* Directions: all, t, r, b, l, x, y */
.m-4 /* margin: 1rem */
.mt-2 /* margin-top: 0.5rem */
.px-4 /* padding-left/right: 1rem */
.my-8 /* margin-top/bottom: 2rem */
```
### 2. Typography
```css
/* Font Size */
.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.875rem; }
.text-base { font-size: 1rem; }
.text-lg { font-size: 1.125rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }
.text-3xl { font-size: 1.875rem; }
.text-4xl { font-size: 2.25rem; }
/* Font Weight */
.font-thin { font-weight: 100; }
.font-light { font-weight: 300; }
.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.font-extrabold { font-weight: 800; }
/* Text Align */
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-justify { text-align: justify; }
/* Line Height */
.leading-none { line-height: 1; }
.leading-tight { line-height: 1.25; }
.leading-normal { line-height: 1.5; }
.leading-relaxed { line-height: 1.75; }
.leading-loose { line-height: 2; }
```
### 3. Layout
```css
/* Display */
.block { display: block; }
.inline { display: inline; }
.inline-block { display: inline-block; }
.flex { display: flex; }
.inline-flex { display: inline-flex; }
.grid { display: grid; }
.hidden { display: none; }
/* Position */
.static { position: static; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.sticky { position: sticky; }
/* Width */
.w-auto { width: auto; }
.w-full { width: 100%; }
.w-screen { width: 100vw; }
.w-1\/2 { width: 50%; }
.w-1\/3 { width: 33.333333%; }
.w-1\/4 { width: 25%; }
/* Height */
.h-auto { height: auto; }
.h-full { height: 100%; }
.h-screen { height: 100vh; }
```
### 4. Grid System
```css
/* Grid Template Columns */
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.grid-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }
/* Grid Gap */
.gap-0 { gap: 0; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.gap-8 { gap: 2rem; }
```
### 5. Borders & Radius
```css
/* Border Width */
.border-0 { border-width: 0; }
.border { border-width: 1px; }
.border-2 { border-width: 2px; }
.border-4 { border-width: 4px; }
/* Border Radius */
.rounded-none { border-radius: 0; }
.rounded-sm { border-radius: 0.125rem; }
.rounded { border-radius: 0.25rem; }
.rounded-md { border-radius: 0.375rem; }
.rounded-lg { border-radius: 0.5rem; }
.rounded-xl { border-radius: 0.75rem; }
.rounded-full { border-radius: 9999px; }
/* Border Color */
.border-gray-200 { border-color: #e5e7eb; }
.border-gray-300 { border-color: #d1d5db; }
.border-primary { border-color: #3b82f6; }
```
### 6. Effects
```css
/* Shadow */
.shadow-none { box-shadow: none; }
.shadow-sm { box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); }
.shadow { box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); }
.shadow-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); }
.shadow-lg { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); }
.shadow-xl { box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); }
/* Opacity */
.opacity-0 { opacity: 0; }
.opacity-25 { opacity: 0.25; }
.opacity-50 { opacity: 0.5; }
.opacity-75 { opacity: 0.75; }
.opacity-100 { opacity: 1; }
```
---
## Usage Examples
### Example 1: Card Component
```html
<div class="bg-white rounded-lg shadow-md p-6 mb-4">
<h2 class="text-2xl font-bold mb-2 text-gray-900">Card Title</h2>
<p class="text-gray-600 mb-4">Card description goes here.</p>
<button class="bg-primary text-white px-4 py-2 rounded">
Action
</button>
</div>
```
### Example 2: Flexbox Layout
```html
<div class="flex justify-between items-center p-4 bg-gray-50">
<div class="flex items-center gap-2">
<img src="logo.png" class="w-8 h-8" />
<span class="font-semibold">Brand</span>
</div>
<nav class="flex gap-4">
<a href="#" class="text-gray-700">Home</a>
<a href="#" class="text-gray-700">About</a>
<a href="#" class="text-gray-700">Contact</a>
</nav>
</div>
```
### Example 3: Responsive Grid
```html
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-8">
<div class="bg-white p-4 rounded shadow">Item 1</div>
<div class="bg-white p-4 rounded shadow">Item 2</div>
<div class="bg-white p-4 rounded shadow">Item 3</div>
</div>
```
---
## Customization Options
### Design Tokens Configuration
**design-tokens.json:**
```json
{
"colors": {
"primary": "#3b82f6",
"secondary": "#6b7280",
"success": "#10b981",
"danger": "#ef4444",
"warning": "#f59e0b"
},
"spacing": {
"scale": [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64]
},
"typography": {
"fontSizes": {
"xs": "0.75rem",
"sm": "0.875rem",
"base": "1rem",
"lg": "1.125rem",
"xl": "1.25rem",
"2xl": "1.5rem"
},
"fontWeights": {
"normal": 400,
"medium": 500,
"semibold": 600,
"bold": 700
}
},
"breakpoints": {
"sm": "640px",
"md": "768px",
"lg": "1024px",
"xl": "1280px"
}
}
```
---
## Benefits
**1. No Build Step Required**
- Pure CSS, works immediately
- No JavaScript runtime
- No npm dependencies
**2. Familiar Syntax**
- Tailwind-like class names
- Easy to learn for Tailwind users
- Predictable naming conventions
**3. Customizable**
- Define your own design tokens
- Choose which categories to include
- Adjust spacing scales and breakpoints
**4. Lightweight**
- Generate only what you need
- ~10-50KB depending on categories
- Much smaller than full Tailwind
**5. Framework Agnostic**
- Works with React, Vue, vanilla HTML
- No framework lock-in
- Pure CSS solution
---
## Integration
### Add to HTML
```html
<link rel="stylesheet" href="utilities.css">
```
### Import in CSS
```css
@import url('utilities.css');
```
### Import in JavaScript
```javascript
import './utilities.css'
```
---
## Related Commands
- `/component-generator` - Generate React components using these utilities
- React Specialist (agent) - Component architecture guidance
- UI/UX Expert (agent) - Design system review
---
**Build your design system. Style faster. Ship consistent UIs.**

View File

@@ -0,0 +1,337 @@
---
description: Generate environment configuration files and validation schemas
shortcut: ecs
category: devops
difficulty: beginner
estimated_time: 2-3 minutes
---
# Environment Config Setup
Generates environment configuration files (.env templates, validation schemas, and type-safe config loading) for multiple environments.
## What This Command Does
**Generated Configuration:**
- .env.example (committed template)
- .env.development, .env.production
- Config validation schema (Zod)
- Type-safe config loader
- Secret management guidance
- Docker environment setup
**Output:** Complete environment configuration system
**Time:** 2-3 minutes
---
## Usage
```bash
# Generate basic environment config
/env-config-setup
# Shortcut
/ecs --services database,redis,email
# With specific platform
/ecs --platform aws --features secrets-manager
```
---
## Generated Files
### **.env.example** (Template - Committed to Repo)
```bash
# Application
NODE_ENV=development
PORT=3000
APP_NAME=My Application
APP_URL=http://localhost:3000
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10
# Redis
REDIS_URL=redis://localhost:6379
REDIS_PREFIX=myapp:
# Authentication
JWT_SECRET=generate-random-32-char-secret-here
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=generate-random-32-char-refresh-secret
JWT_REFRESH_EXPIRES_IN=7d
# Email (SendGrid)
SENDGRID_API_KEY=SG.your-api-key-here
FROM_EMAIL=[email protected]
# AWS (Optional)
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
S3_BUCKET=your-bucket-name
# External APIs
STRIPE_SECRET_KEY=sk_test_your-stripe-key
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret
# Monitoring
SENTRY_DSN=https://your-sentry-dsn
LOG_LEVEL=info
# Feature Flags
ENABLE_FEATURE_X=false
```
### **.env.development**
```bash
NODE_ENV=development
PORT=3000
DATABASE_URL=postgresql://postgres:password@localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379
LOG_LEVEL=debug
```
### **.env.production**
```bash
NODE_ENV=production
PORT=8080
# Use environment variables or secrets manager for sensitive values
DATABASE_URL=${DATABASE_URL}
REDIS_URL=${REDIS_URL}
JWT_SECRET=${JWT_SECRET}
LOG_LEVEL=warn
```
### **config/env.ts** (Type-Safe Config Loader)
```typescript
import { z } from 'zod'
import dotenv from 'dotenv'
// Load appropriate .env file
const envFile = process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development'
dotenv.config({ path: envFile })
// Define validation schema
const envSchema = z.object({
// Application
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().min(1).max(65535).default(3000),
APP_NAME: z.string().min(1),
APP_URL: z.string().url(),
// Database
DATABASE_URL: z.string().url(),
DATABASE_POOL_MIN: z.coerce.number().min(0).default(2),
DATABASE_POOL_MAX: z.coerce.number().min(1).default(10),
// Redis
REDIS_URL: z.string().url(),
REDIS_PREFIX: z.string().default(''),
// Authentication
JWT_SECRET: z.string().min(32),
JWT_EXPIRES_IN: z.string().default('15m'),
JWT_REFRESH_SECRET: z.string().min(32),
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
// Email
SENDGRID_API_KEY: z.string().startsWith('SG.'),
FROM_EMAIL: z.string().email(),
// AWS (optional)
AWS_ACCESS_KEY_ID: z.string().optional(),
AWS_SECRET_ACCESS_KEY: z.string().optional(),
AWS_REGION: z.string().default('us-east-1'),
S3_BUCKET: z.string().optional(),
// External APIs
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_'),
// Monitoring
SENTRY_DSN: z.string().url().optional(),
LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
// Feature Flags
ENABLE_FEATURE_X: z.coerce.boolean().default(false)
})
// Parse and validate
const parsedEnv = envSchema.safeParse(process.env)
if (!parsedEnv.success) {
console.error(' Invalid environment variables:')
console.error(parsedEnv.error.flatten().fieldErrors)
process.exit(1)
}
export const env = parsedEnv.data
// Type-safe access
export type Env = z.infer<typeof envSchema>
```
### **config/secrets.ts** (AWS Secrets Manager)
```typescript
import { SecretsManager } from '@aws-sdk/client-secrets-manager'
const client = new SecretsManager({ region: process.env.AWS_REGION })
export async function loadSecrets(secretName: string) {
try {
const response = await client.getSecretValue({ SecretId: secretName })
return JSON.parse(response.SecretString || '{}')
} catch (error) {
console.error('Failed to load secrets:', error)
throw error
}
}
// Usage
const secrets = await loadSecrets('prod/myapp/secrets')
process.env.JWT_SECRET = secrets.JWT_SECRET
```
### **docker-compose.env.yml**
```yaml
version: '3.8'
services:
app:
build: .
env_file:
- .env.development
environment:
- NODE_ENV=development
- PORT=3000
ports:
- "3000:3000"
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
POSTGRES_DB: ${POSTGRES_DB:-myapp_dev}
ports:
- "5432:5432"
```
---
## Security Best Practices
**1. Never Commit Secrets:**
```bash
# .gitignore
.env
.env.local
.env.*.local
.env.production
*.key
*.pem
secrets/
```
**2. Use Secret Rotation:**
```bash
# Rotate secrets regularly
# Use AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault
# Example: Rotate JWT secrets every 30 days
```
**3. Least Privilege:**
```bash
# Only provide necessary permissions
# Use separate credentials for dev/staging/prod
# Implement role-based access control
```
**4. Environment Validation:**
```typescript
// Validate on startup
if (process.env.NODE_ENV === 'production') {
if (!env.JWT_SECRET || env.JWT_SECRET.length < 32) {
throw new Error('Production JWT_SECRET must be at least 32 characters')
}
}
```
---
## Secret Generation
```bash
# Generate secure random secrets
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Or use openssl
openssl rand -hex 32
# For JWT secrets (base64)
openssl rand -base64 32
```
---
## Platform-Specific Setup
**Vercel:**
```bash
# Set environment variables via CLI
vercel env add DATABASE_URL production
vercel env add JWT_SECRET production
```
**Railway:**
```bash
# Environment variables in dashboard
# Or via railway.json
{
"deploy": {
"envVars": {
"NODE_ENV": "production"
}
}
}
```
**AWS ECS:**
```json
{
"containerDefinitions": [{
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:name"
}
]
}]
}
```
---
## Related Commands
- `/auth-setup` - Generate authentication system
- `/project-scaffold` - Generate full project structure
---
**Manage secrets safely. Configure environments easily. Deploy confidently.**

View File

@@ -0,0 +1,658 @@
---
description: Generate production-ready Express.js REST API with TypeScript and auth
shortcut: eas
category: backend
difficulty: intermediate
estimated_time: 5-10 minutes
---
# Express API Scaffold
Generates a complete Express.js REST API boilerplate with TypeScript, authentication, database integration, and testing setup.
## What This Command Does
**Generated Project:**
- Express.js with TypeScript
- JWT authentication
- Database integration (Prisma or TypeORM)
- Input validation (Zod)
- Error handling middleware
- Rate limiting & security (Helmet, CORS)
- Testing setup (Jest + Supertest)
- Docker configuration
- Example CRUD endpoints
**Output:** Complete API project ready for development
**Time:** 5-10 minutes
---
## Usage
```bash
# Generate full Express API
/express-api-scaffold "Task Management API"
# Shortcut
/eas "E-commerce API"
# With specific database
/eas "Blog API" --database postgresql
# With authentication type
/eas "Social API" --auth jwt --database mongodb
```
---
## Example Output
**Input:**
```
/eas "Task Management API" --database postgresql
```
**Generated Project Structure:**
```
task-api/
├── src/
│ ├── controllers/ # Request handlers
│ │ ├── auth.controller.ts
│ │ └── task.controller.ts
│ ├── middleware/ # Express middleware
│ │ ├── auth.middleware.ts
│ │ ├── error.middleware.ts
│ │ └── validation.middleware.ts
│ ├── models/ # Database models
│ │ └── task.model.ts
│ ├── routes/ # API routes
│ │ ├── auth.routes.ts
│ │ └── task.routes.ts
│ ├── services/ # Business logic
│ │ ├── auth.service.ts
│ │ └── task.service.ts
│ ├── utils/ # Utilities
│ │ ├── jwt.util.ts
│ │ └── password.util.ts
│ ├── config/ # Configuration
│ │ └── database.ts
│ ├── types/ # TypeScript types
│ │ └── express.d.ts
│ ├── app.ts # Express app setup
│ └── server.ts # Server entry point
├── tests/
│ ├── auth.test.ts
│ └── task.test.ts
├── prisma/
│ └── schema.prisma # Database schema
├── .env.example
├── .gitignore
├── package.json
├── tsconfig.json
├── jest.config.js
├── Dockerfile
├── docker-compose.yml
└── README.md
```
---
## Generated Files
### 1. **src/server.ts** (Entry Point)
```typescript
import app from './app'
import { config } from './config'
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
console.log(`Environment: ${process.env.NODE_ENV}`)
})
```
### 2. **src/app.ts** (Express Setup)
```typescript
import express, { Application } from 'express'
import cors from 'cors'
import helmet from 'helmet'
import morgan from 'morgan'
import rateLimit from 'express-rate-limit'
import authRoutes from './routes/auth.routes'
import taskRoutes from './routes/task.routes'
import { errorHandler } from './middleware/error.middleware'
import { notFoundHandler } from './middleware/notFound.middleware'
const app: Application = express()
// Security middleware
app.use(helmet())
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
credentials: true
}))
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later'
})
app.use('/api/', limiter)
// Parsing middleware
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// Logging
if (process.env.NODE_ENV !== 'test') {
app.use(morgan('combined'))
}
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() })
})
// Routes
app.use('/api/auth', authRoutes)
app.use('/api/tasks', taskRoutes)
// Error handling
app.use(notFoundHandler)
app.use(errorHandler)
export default app
```
### 3. **src/controllers/auth.controller.ts**
```typescript
import { Request, Response, NextFunction } from 'express'
import { AuthService } from '../services/auth.service'
import { ApiError } from '../utils/ApiError'
const authService = new AuthService()
export class AuthController {
async register(req: Request, res: Response, next: NextFunction) {
try {
const { email, password, name } = req.body
const result = await authService.register({ email, password, name })
res.status(201).json({
data: {
user: result.user,
token: result.token
}
})
} catch (error) {
next(error)
}
}
async login(req: Request, res: Response, next: NextFunction) {
try {
const { email, password } = req.body
const result = await authService.login(email, password)
res.json({
data: {
user: result.user,
token: result.token
}
})
} catch (error) {
next(error)
}
}
async getProfile(req: Request, res: Response, next: NextFunction) {
try {
const userId = req.user!.id
const user = await authService.getUserById(userId)
res.json({ data: user })
} catch (error) {
next(error)
}
}
}
```
### 4. **src/middleware/auth.middleware.ts**
```typescript
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'
import { ApiError } from '../utils/ApiError'
interface JwtPayload {
userId: string
email: string
}
declare global {
namespace Express {
interface Request {
user?: {
id: string
email: string
}
}
}
}
export function authenticate(req: Request, res: Response, next: NextFunction) {
try {
const authHeader = req.headers.authorization
if (!authHeader?.startsWith('Bearer ')) {
throw new ApiError(401, 'No token provided')
}
const token = authHeader.split(' ')[1]
const decoded = jwt.verify(
token,
process.env.JWT_SECRET!
) as JwtPayload
req.user = {
id: decoded.userId,
email: decoded.email
}
next()
} catch (error) {
if (error instanceof jwt.JsonWebTokenError) {
next(new ApiError(401, 'Invalid token'))
} else {
next(error)
}
}
}
```
### 5. **src/middleware/error.middleware.ts**
```typescript
import { Request, Response, NextFunction } from 'express'
import { ApiError } from '../utils/ApiError'
import { ZodError } from 'zod'
export function errorHandler(
err: Error,
req: Request,
res: Response,
next: NextFunction
) {
console.error('Error:', err)
// Handle known API errors
if (err instanceof ApiError) {
return res.status(err.statusCode).json({
error: {
code: err.name,
message: err.message,
...(err.details && { details: err.details })
}
})
}
// Handle validation errors (Zod)
if (err instanceof ZodError) {
return res.status(400).json({
error: {
code: 'VALIDATION_ERROR',
message: 'Validation failed',
details: err.errors.map(e => ({
field: e.path.join('.'),
message: e.message
}))
}
})
}
// Handle unexpected errors
res.status(500).json({
error: {
code: 'INTERNAL_SERVER_ERROR',
message: process.env.NODE_ENV === 'production'
? 'An unexpected error occurred'
: err.message
}
})
}
```
### 6. **src/routes/task.routes.ts**
```typescript
import { Router } from 'express'
import { TaskController } from '../controllers/task.controller'
import { authenticate } from '../middleware/auth.middleware'
import { validate } from '../middleware/validation.middleware'
import { createTaskSchema, updateTaskSchema } from '../schemas/task.schema'
const router = Router()
const taskController = new TaskController()
// All routes require authentication
router.use(authenticate)
router.get('/', taskController.list)
router.post('/', validate(createTaskSchema), taskController.create)
router.get('/:id', taskController.getById)
router.patch('/:id', validate(updateTaskSchema), taskController.update)
router.delete('/:id', taskController.delete)
export default router
```
### 7. **src/services/task.service.ts**
```typescript
import { PrismaClient } from '@prisma/client'
import { ApiError } from '../utils/ApiError'
const prisma = new PrismaClient()
export class TaskService {
async create(userId: string, data: { title: string; description?: string }) {
return await prisma.task.create({
data: {
...data,
userId
}
})
}
async findAll(userId: string) {
return await prisma.task.findMany({
where: { userId },
orderBy: { createdAt: 'desc' }
})
}
async findById(id: string, userId: string) {
const task = await prisma.task.findUnique({
where: { id }
})
if (!task) {
throw new ApiError(404, 'Task not found')
}
if (task.userId !== userId) {
throw new ApiError(403, 'Access denied')
}
return task
}
async update(id: string, userId: string, data: Partial<{ title: string; description: string; completed: boolean }>) {
await this.findById(id, userId) // Check ownership
return await prisma.task.update({
where: { id },
data
})
}
async delete(id: string, userId: string) {
await this.findById(id, userId) // Check ownership
await prisma.task.delete({
where: { id }
})
}
}
```
### 8. **prisma/schema.prisma** (Database Schema)
```prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
password String
name String
tasks Task[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
model Task {
id String @id @default(uuid())
title String
description String?
completed Boolean @default(false)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@map("tasks")
}
```
### 9. **tests/task.test.ts** (Integration Tests)
```typescript
import request from 'supertest'
import app from '../src/app'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
describe('Task API', () => {
let authToken: string
let userId: string
beforeAll(async () => {
// Create test user and get token
const res = await request(app)
.post('/api/auth/register')
.send({
email: '[email protected]',
password: 'password123',
name: 'Test User'
})
authToken = res.body.data.token
userId = res.body.data.user.id
})
afterAll(async () => {
// Cleanup
await prisma.task.deleteMany({ where: { userId } })
await prisma.user.delete({ where: { id: userId } })
await prisma.$disconnect()
})
describe('POST /api/tasks', () => {
it('should create a new task', async () => {
const res = await request(app)
.post('/api/tasks')
.set('Authorization', `Bearer ${authToken}`)
.send({
title: 'Test Task',
description: 'Test description'
})
expect(res.status).toBe(201)
expect(res.body.data).toHaveProperty('id')
expect(res.body.data.title).toBe('Test Task')
})
it('should require authentication', async () => {
const res = await request(app)
.post('/api/tasks')
.send({ title: 'Test' })
expect(res.status).toBe(401)
})
})
describe('GET /api/tasks', () => {
it('should list user tasks', async () => {
const res = await request(app)
.get('/api/tasks')
.set('Authorization', `Bearer ${authToken}`)
expect(res.status).toBe(200)
expect(Array.isArray(res.body.data)).toBe(true)
})
})
})
```
### 10. **package.json**
```json
{
"name": "task-api",
"version": "1.0.0",
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "jest --coverage",
"lint": "eslint src/**/*.ts",
"format": "prettier --write \"src/**/*.ts\"",
"db:migrate": "prisma migrate dev",
"db:push": "prisma db push",
"db:generate": "prisma generate"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"express-rate-limit": "^7.1.5",
"morgan": "^1.10.0",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"zod": "^3.22.4",
"@prisma/client": "^5.8.0",
"dotenv": "^16.3.1"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.10.6",
"@types/cors": "^2.8.17",
"@types/morgan": "^1.9.9",
"@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.5",
"@types/jest": "^29.5.11",
"@types/supertest": "^6.0.2",
"typescript": "^5.3.3",
"ts-node-dev": "^2.0.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"supertest": "^6.3.3",
"prisma": "^5.8.0",
"eslint": "^8.56.0",
"prettier": "^3.1.1"
}
}
```
---
## Features
**Security:**
- Helmet.js for HTTP headers
- CORS with configurable origins
- Rate limiting (100 req/15min)
- JWT authentication
- Password hashing (bcrypt)
- Input validation (Zod)
**Database:**
- Prisma ORM with TypeScript
- Automatic migrations
- Type-safe queries
- Supports PostgreSQL, MySQL, SQLite
**Testing:**
- Jest + Supertest
- Integration tests
- Coverage reporting
- Test database isolation
**Development:**
- Hot reload (ts-node-dev)
- TypeScript with strict mode
- ESLint + Prettier
- Environment variables
**Production:**
- Docker support
- Health check endpoint
- Error logging
- Graceful shutdown
---
## Getting Started
**1. Install dependencies:**
```bash
npm install
```
**2. Configure environment:**
```bash
cp .env.example .env
# Edit .env with your database URL and secrets
```
**3. Run database migrations:**
```bash
npm run db:migrate
```
**4. Start development server:**
```bash
npm run dev
```
**5. Run tests:**
```bash
npm test
```
---
## Related Commands
- `/fastapi-scaffold` - Generate FastAPI boilerplate
- Backend Architect (agent) - Architecture review
- API Builder (agent) - API design guidance
---
**Build production-ready APIs. Ship faster. Scale confidently.**

View File

@@ -0,0 +1,673 @@
---
description: Generate production-ready FastAPI REST API with async and authentication
shortcut: fas
category: backend
difficulty: intermediate
estimated_time: 5-10 minutes
---
# FastAPI Scaffold
Generates a complete FastAPI REST API boilerplate with async support, authentication, database integration, and testing setup.
## What This Command Does
**Generated Project:**
- FastAPI with Python 3.10+
- Async/await throughout
- JWT authentication
- Database integration (SQLAlchemy async)
- Pydantic models & validation
- Automatic OpenAPI docs
- Testing setup (Pytest + httpx)
- Docker configuration
- Example CRUD endpoints
**Output:** Complete API project ready for development
**Time:** 5-10 minutes
---
## Usage
```bash
# Generate full FastAPI API
/fastapi-scaffold "Task Management API"
# Shortcut
/fas "E-commerce API"
# With specific database
/fas "Blog API" --database postgresql
# With authentication type
/fas "Social API" --auth jwt --database postgresql
```
---
## Example Output
**Input:**
```
/fas "Task Management API" --database postgresql
```
**Generated Project Structure:**
```
task-api/
├── app/
│ ├── api/
│ │ ├── deps.py # Dependencies
│ │ └── v1/
│ │ ├── __init__.py
│ │ ├── auth.py # Auth endpoints
│ │ └── tasks.py # Task endpoints
│ ├── core/
│ │ ├── config.py # Settings
│ │ ├── security.py # JWT, password hashing
│ │ └── database.py # Database connection
│ ├── models/ # SQLAlchemy models
│ │ ├── user.py
│ │ └── task.py
│ ├── schemas/ # Pydantic schemas
│ │ ├── user.py
│ │ └── task.py
│ ├── services/ # Business logic
│ │ ├── auth.py
│ │ └── task.py
│ ├── db/
│ │ └── init_db.py # Database initialization
│ ├── main.py # FastAPI app
│ └── __init__.py
├── tests/
│ ├── conftest.py
│ ├── test_auth.py
│ └── test_tasks.py
├── alembic/ # Database migrations
│ ├── versions/
│ └── env.py
├── .env.example
├── .gitignore
├── requirements.txt
├── pyproject.toml
├── Dockerfile
├── docker-compose.yml
└── README.md
```
---
## Generated Files
### 1. **app/main.py** (Application Entry)
```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from app.api.v1 import auth, tasks
from app.core.config import settings
from app.core.database import engine
from app.models import Base
# Create database tables
Base.metadata.create_all(bind=engine)
app = FastAPI(
title=settings.PROJECT_NAME,
version="1.0.0",
openapi_url=f"{settings.API_V1_STR}/openapi.json",
docs_url=f"{settings.API_V1_STR}/docs",
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Security middleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=settings.ALLOWED_HOSTS
)
@app.get("/health")
async def health_check():
return {
"status": "ok",
"version": "1.0.0"
}
# Include routers
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
app.include_router(tasks.router, prefix=f"{settings.API_V1_STR}/tasks", tags=["tasks"])
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
### 2. **app/core/config.py** (Settings)
```python
from pydantic_settings import BaseSettings
from typing import List
class Settings(BaseSettings):
PROJECT_NAME: str = "Task API"
API_V1_STR: str = "/api/v1"
# Database
DATABASE_URL: str
# Security
SECRET_KEY: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 days
# CORS
ALLOWED_ORIGINS: List[str] = ["http://localhost:3000"]
ALLOWED_HOSTS: List[str] = ["*"]
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()
```
### 3. **app/core/security.py** (Authentication)
```python
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt, JWTError
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode,
settings.SECRET_KEY,
algorithm=settings.ALGORITHM
)
return encoded_jwt
def decode_access_token(token: str) -> dict:
try:
payload = jwt.decode(
token,
settings.SECRET_KEY,
algorithms=[settings.ALGORITHM]
)
return payload
except JWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
```
### 4. **app/core/database.py** (Database Setup)
```python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
engine = create_engine(
settings.DATABASE_URL,
pool_pre_ping=True,
echo=False
)
SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
```
### 5. **app/models/user.py** (User Model)
```python
from sqlalchemy import Column, String, DateTime
from sqlalchemy.orm import relationship
from datetime import datetime
import uuid
from app.core.database import Base
class User(Base):
__tablename__ = "users"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
name = Column(String, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
tasks = relationship("Task", back_populates="owner", cascade="all, delete-orphan")
```
### 6. **app/models/task.py** (Task Model)
```python
from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
import uuid
from app.core.database import Base
class Task(Base):
__tablename__ = "tasks"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
title = Column(String, nullable=False)
description = Column(String, nullable=True)
completed = Column(Boolean, default=False)
user_id = Column(String, ForeignKey("users.id", ondelete="CASCADE"))
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
owner = relationship("User", back_populates="tasks")
```
### 7. **app/schemas/user.py** (Pydantic Schemas)
```python
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Optional
class UserBase(BaseModel):
email: EmailStr
name: str
class UserCreate(UserBase):
password: str
class UserUpdate(BaseModel):
name: Optional[str] = None
email: Optional[EmailStr] = None
class UserInDB(UserBase):
id: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class User(UserInDB):
pass
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
email: Optional[str] = None
```
### 8. **app/schemas/task.py** (Task Schemas)
```python
from pydantic import BaseModel
from datetime import datetime
from typing import Optional
class TaskBase(BaseModel):
title: str
description: Optional[str] = None
class TaskCreate(TaskBase):
pass
class TaskUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
completed: Optional[bool] = None
class TaskInDB(TaskBase):
id: str
completed: bool
user_id: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class Task(TaskInDB):
pass
```
### 9. **app/api/deps.py** (Dependencies)
```python
from typing import Generator
from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import oauth2_scheme, decode_access_token
from app.models.user import User
def get_current_user(
db: Session = Depends(get_db),
token: str = Depends(oauth2_scheme)
) -> User:
payload = decode_access_token(token)
email: str = payload.get("sub")
if email is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials"
)
user = db.query(User).filter(User.email == email).first()
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found"
)
return user
```
### 10. **app/api/v1/tasks.py** (Task Endpoints)
```python
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.api.deps import get_current_user
from app.core.database import get_db
from app.models.user import User
from app.models.task import Task as TaskModel
from app.schemas.task import Task, TaskCreate, TaskUpdate
router = APIRouter()
@router.get("/", response_model=List[Task])
async def list_tasks(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
skip: int = 0,
limit: int = 100
):
tasks = db.query(TaskModel)\
.filter(TaskModel.user_id == current_user.id)\
.offset(skip)\
.limit(limit)\
.all()
return tasks
@router.post("/", response_model=Task, status_code=status.HTTP_201_CREATED)
async def create_task(
task_in: TaskCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
task = TaskModel(
**task_in.dict(),
user_id=current_user.id
)
db.add(task)
db.commit()
db.refresh(task)
return task
@router.get("/{task_id}", response_model=Task)
async def get_task(
task_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
task = db.query(TaskModel)\
.filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
.first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
return task
@router.patch("/{task_id}", response_model=Task)
async def update_task(
task_id: str,
task_in: TaskUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
task = db.query(TaskModel)\
.filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
.first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
for field, value in task_in.dict(exclude_unset=True).items():
setattr(task, field, value)
db.commit()
db.refresh(task)
return task
@router.delete("/{task_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_task(
task_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
task = db.query(TaskModel)\
.filter(TaskModel.id == task_id, TaskModel.user_id == current_user.id)\
.first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
db.delete(task)
db.commit()
```
### 11. **tests/test_tasks.py** (Pytest Tests)
```python
import pytest
from httpx import AsyncClient
from app.main import app
@pytest.mark.asyncio
async def test_create_task(client: AsyncClient, test_user_token):
response = await client.post(
"/api/v1/tasks/",
json={
"title": "Test Task",
"description": "Test description"
},
headers={"Authorization": f"Bearer {test_user_token}"}
)
assert response.status_code == 201
data = response.json()
assert data["title"] == "Test Task"
assert "id" in data
@pytest.mark.asyncio
async def test_list_tasks(client: AsyncClient, test_user_token):
response = await client.get(
"/api/v1/tasks/",
headers={"Authorization": f"Bearer {test_user_token}"}
)
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
@pytest.mark.asyncio
async def test_create_task_unauthorized(client: AsyncClient):
response = await client.post(
"/api/v1/tasks/",
json={"title": "Test"}
)
assert response.status_code == 401
```
### 12. **requirements.txt**
```
fastapi==0.109.0
uvicorn[standard]==0.27.0
sqlalchemy==2.0.25
pydantic==2.5.3
pydantic-settings==2.1.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6
alembic==1.13.1
psycopg2-binary==2.9.9
# Development
pytest==7.4.4
pytest-asyncio==0.23.3
httpx==0.26.0
black==23.12.1
isort==5.13.2
mypy==1.8.0
```
---
## Features
**Performance:**
- Async/await for high concurrency
- Background tasks support
- WebSocket support (optional)
- Automatic Pydantic validation
**Documentation:**
- Auto-generated OpenAPI (Swagger)
- ReDoc documentation
- Type hints throughout
**Database:**
- SQLAlchemy ORM with async support
- Alembic migrations
- Connection pooling
**Security:**
- JWT authentication
- Password hashing (bcrypt)
- CORS middleware
- Trusted host middleware
**Testing:**
- Pytest with async support
- Test fixtures
- Coverage reporting
---
## Getting Started
**1. Install dependencies:**
```bash
pip install -r requirements.txt
```
**2. Configure environment:**
```bash
cp .env.example .env
# Edit .env with your database URL and secrets
```
**3. Run database migrations:**
```bash
alembic upgrade head
```
**4. Start development server:**
```bash
uvicorn app.main:app --reload
```
**5. View API docs:**
- Swagger UI: http://localhost:8000/api/v1/docs
- ReDoc: http://localhost:8000/api/v1/redoc
**6. Run tests:**
```bash
pytest
```
---
## Related Commands
- `/express-api-scaffold` - Generate Express.js boilerplate
- Backend Architect (agent) - Architecture review
- API Builder (agent) - API design guidance
---
**Build high-performance APIs. Scale effortlessly. Deploy confidently.**

View File

@@ -0,0 +1,582 @@
---
description: Generate Prisma schema from natural language descriptions
shortcut: psg
category: database
difficulty: beginner
estimated_time: 3-5 minutes
---
# Prisma Schema Generator
Generates complete Prisma schema files from natural language descriptions, including models, relationships, indexes, and validation.
## What This Command Does
**Generated Schema:**
- Prisma models with fields
- Relationships (one-to-one, one-to-many, many-to-many)
- Indexes and unique constraints
- Default values and validators
- Database-specific configuration
- Example queries
**Output:** Complete `schema.prisma` file ready for migration
**Time:** 3-5 minutes
---
## Usage
```bash
# Generate schema from description
/prisma-schema-gen "Blog with users, posts, and comments"
# Shortcut
/psg "E-commerce with products, orders, and customers"
# Specify database
/psg "Task management app" --database postgresql
# With specific relationships
/psg "Social network with users, posts, likes, and follows"
```
---
## Example Output
**Input:**
```
/psg "Blog platform with users, posts, comments, and tags"
```
**Generated schema.prisma:**
```prisma
// This is your Prisma schema file
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String
password String
bio String?
avatar String?
posts Post[]
comments Comment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@map("users")
}
model Post {
id String @id @default(uuid())
title String
slug String @unique
content String @db.Text
excerpt String?
published Boolean @default(false)
publishedAt DateTime?
viewCount Int @default(0)
authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
comments Comment[]
tags PostTag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
@@index([slug])
@@index([published, publishedAt])
@@map("posts")
}
model Comment {
id String @id @default(uuid())
content String @db.Text
postId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([postId])
@@index([authorId])
@@map("comments")
}
model Tag {
id String @id @default(uuid())
name String @unique
slug String @unique
posts PostTag[]
@@index([slug])
@@map("tags")
}
// Junction table for many-to-many
model PostTag {
id String @id @default(uuid())
postId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tagId String
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@unique([postId, tagId])
@@index([postId])
@@index([tagId])
@@map("post_tags")
}
```
---
## Generated Files
### **Migrations**
```bash
# After generating schema, run:
npx prisma migrate dev --name init
# This creates:
# - migrations/
# └── 20250110000000_init/
# └── migration.sql
```
### **Example Queries (TypeScript)**
```typescript
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// Create user
async function createUser() {
const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'John Doe',
password: 'hashed_password_here'
}
})
return user
}
// Create post with tags
async function createPost() {
const post = await prisma.post.create({
data: {
title: 'Getting Started with Prisma',
slug: 'getting-started-with-prisma',
content: 'Full blog post content...',
published: true,
publishedAt: new Date(),
authorId: 'user-uuid-here',
tags: {
create: [
{
tag: {
connectOrCreate: {
where: { slug: 'prisma' },
create: { name: 'Prisma', slug: 'prisma' }
}
}
}
]
}
},
include: {
author: true,
tags: {
include: {
tag: true
}
}
}
})
return post
}
// Get posts with related data
async function getPosts() {
const posts = await prisma.post.findMany({
where: {
published: true
},
include: {
author: {
select: {
id: true,
name: true,
avatar: true
}
},
tags: {
include: {
tag: true
}
},
_count: {
select: {
comments: true
}
}
},
orderBy: {
publishedAt: 'desc'
},
take: 10
})
return posts
}
// Create comment
async function createComment() {
const comment = await prisma.comment.create({
data: {
content: 'Great article!',
postId: 'post-uuid-here',
authorId: 'user-uuid-here'
},
include: {
author: {
select: {
name: true,
avatar: true
}
}
}
})
return comment
}
// Search posts
async function searchPosts(query: string) {
const posts = await prisma.post.findMany({
where: {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ content: { contains: query, mode: 'insensitive' } }
],
published: true
},
include: {
author: true
}
})
return posts
}
// Get post with comments
async function getPostWithComments(slug: string) {
const post = await prisma.post.findUnique({
where: { slug },
include: {
author: true,
comments: {
include: {
author: {
select: {
name: true,
avatar: true
}
}
},
orderBy: {
createdAt: 'desc'
}
},
tags: {
include: {
tag: true
}
}
}
})
if (!post) {
throw new Error('Post not found')
}
// Increment view count
await prisma.post.update({
where: { id: post.id },
data: { viewCount: { increment: 1 } }
})
return post
}
// Get posts by tag
async function getPostsByTag(tagSlug: string) {
const posts = await prisma.post.findMany({
where: {
published: true,
tags: {
some: {
tag: {
slug: tagSlug
}
}
}
},
include: {
author: true,
tags: {
include: {
tag: true
}
}
},
orderBy: {
publishedAt: 'desc'
}
})
return posts
}
```
---
## Common Patterns
### **1. E-commerce Schema**
```prisma
model Customer {
id String @id @default(uuid())
email String @unique
name String
phone String?
orders Order[]
cart Cart?
}
model Product {
id String @id @default(uuid())
name String
description String?
price Decimal @db.Decimal(10, 2)
stock Int @default(0)
orderItems OrderItem[]
cartItems CartItem[]
}
model Order {
id String @id @default(uuid())
customerId String
customer Customer @relation(fields: [customerId], references: [id])
items OrderItem[]
total Decimal @db.Decimal(10, 2)
status String // 'pending', 'paid', 'shipped', 'delivered'
createdAt DateTime @default(now())
}
model OrderItem {
id String @id @default(uuid())
orderId String
order Order @relation(fields: [orderId], references: [id])
productId String
product Product @relation(fields: [productId], references: [id])
quantity Int
price Decimal @db.Decimal(10, 2)
}
```
### **2. Social Network Schema**
```prisma
model User {
id String @id @default(uuid())
username String @unique
email String @unique
posts Post[]
likes Like[]
following Follow[] @relation("Following")
followers Follow[] @relation("Followers")
}
model Post {
id String @id @default(uuid())
content String
authorId String
author User @relation(fields: [authorId], references: [id])
likes Like[]
createdAt DateTime @default(now())
}
model Like {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
postId String
post Post @relation(fields: [postId], references: [id])
@@unique([userId, postId])
}
model Follow {
id String @id @default(uuid())
followerId String
followingId String
follower User @relation("Following", fields: [followerId], references: [id])
following User @relation("Followers", fields: [followingId], references: [id])
@@unique([followerId, followingId])
}
```
### **3. Multi-tenant SaaS Schema**
```prisma
model Organization {
id String @id @default(uuid())
name String
slug String @unique
members Member[]
projects Project[]
}
model User {
id String @id @default(uuid())
email String @unique
memberships Member[]
}
model Member {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
orgId String
org Organization @relation(fields: [orgId], references: [id])
role String // 'owner', 'admin', 'member'
@@unique([userId, orgId])
}
model Project {
id String @id @default(uuid())
name String
orgId String
org Organization @relation(fields: [orgId], references: [id])
tasks Task[]
}
model Task {
id String @id @default(uuid())
title String
completed Boolean @default(false)
projectId String
project Project @relation(fields: [projectId], references: [id])
}
```
---
## Database Support
**PostgreSQL:**
```prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// PostgreSQL-specific types
model Example {
jsonData Json
textData String @db.Text
amount Decimal @db.Decimal(10, 2)
}
```
**MySQL:**
```prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
```
**SQLite (Development):**
```prisma
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
```
**MongoDB:**
```prisma
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
}
```
---
## Getting Started
**1. Install Prisma:**
```bash
npm install @prisma/client
npm install -D prisma
```
**2. Initialize Prisma:**
```bash
npx prisma init
```
**3. Use generated schema:**
- Replace `prisma/schema.prisma` with generated content
- Set `DATABASE_URL` in `.env`
**4. Create migration:**
```bash
npx prisma migrate dev --name init
```
**5. Generate Prisma Client:**
```bash
npx prisma generate
```
**6. Use in code:**
```typescript
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
```
---
## Related Commands
- `/sql-query-builder` - Generate SQL queries
- Database Designer (agent) - Schema design review
---
**Generate schemas fast. Migrate safely. Query confidently.**

View File

@@ -0,0 +1,354 @@
---
description: Generate complete fullstack project structure with all boilerplate
shortcut: ps
category: devops
difficulty: beginner
estimated_time: 5-10 minutes
---
# Project Scaffold
Generates a complete fullstack project structure with frontend, backend, database, authentication, testing, and deployment configuration.
## What This Command Does
**Generated Project:**
- Frontend (React + TypeScript + Vite)
- Backend (Express or FastAPI)
- Database (PostgreSQL + Prisma/SQLAlchemy)
- Authentication (JWT + OAuth)
- Testing (Jest/Pytest + E2E)
- CI/CD (GitHub Actions)
- Docker setup
- Documentation
**Output:** Production-ready fullstack application
**Time:** 5-10 minutes
---
## Usage
```bash
# Generate fullstack project
/project-scaffold "Task Management App"
# Shortcut
/ps "E-commerce Platform" --stack react,express,postgresql
# With specific features
/ps "Blog Platform" --features auth,admin,payments,analytics
```
---
## Generated Structure
```
my-app/
├── client/ # Frontend (React + TypeScript + Vite)
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── pages/ # Page components
│ │ ├── hooks/ # Custom hooks
│ │ ├── services/ # API services
│ │ ├── context/ # Context providers
│ │ ├── utils/ # Utilities
│ │ ├── types/ # TypeScript types
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── public/
│ ├── index.html
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ └── tailwind.config.js
├── server/ # Backend (Express + TypeScript)
│ ├── src/
│ │ ├── controllers/ # Request handlers
│ │ ├── services/ # Business logic
│ │ ├── models/ # Database models
│ │ ├── routes/ # API routes
│ │ ├── middleware/ # Express middleware
│ │ ├── utils/ # Utilities
│ │ ├── config/ # Configuration
│ │ ├── app.ts
│ │ └── server.ts
│ ├── tests/
│ ├── prisma/
│ │ └── schema.prisma
│ ├── package.json
│ ├── tsconfig.json
│ └── jest.config.js
├── .github/
│ └── workflows/
│ ├── ci.yml # Continuous Integration
│ └── deploy.yml # Deployment
├── docker-compose.yml # Development environment
├── Dockerfile # Production container
├── .env.example # Environment template
├── .gitignore
├── README.md
└── package.json # Root workspace
```
---
## Example: Task Management App
**Frontend (client/src/pages/Dashboard.tsx):**
```tsx
import { useState, useEffect } from 'react'
import { TaskList } from '../components/TaskList'
import { CreateTaskForm } from '../components/CreateTaskForm'
import { useAuth } from '../context/AuthContext'
import { taskService } from '../services/api'
export function Dashboard() {
const { user } = useAuth()
const [tasks, setTasks] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
loadTasks()
}, [])
async function loadTasks() {
try {
const data = await taskService.getAll()
setTasks(data)
} catch (error) {
console.error('Failed to load tasks:', error)
} finally {
setLoading(false)
}
}
async function handleCreateTask(task: CreateTaskInput) {
const newTask = await taskService.create(task)
setTasks([newTask, ...tasks])
}
async function handleToggleTask(id: string) {
const updated = await taskService.toggle(id)
setTasks(tasks.map(t => t.id === id ? updated : t))
}
if (loading) return <div>Loading...</div>
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold mb-6">
Welcome, {user?.name}
</h1>
<CreateTaskForm onSubmit={handleCreateTask} />
<TaskList
tasks={tasks}
onToggle={handleToggleTask}
/>
</div>
)
}
```
**Backend (server/src/controllers/task.controller.ts):**
```typescript
import { Request, Response } from 'express'
import { TaskService } from '../services/task.service'
const taskService = new TaskService()
export class TaskController {
async getAll(req: Request, res: Response) {
const tasks = await taskService.findAll(req.user!.userId)
res.json({ data: tasks })
}
async create(req: Request, res: Response) {
const task = await taskService.create(req.user!.userId, req.body)
res.status(201).json({ data: task })
}
async toggle(req: Request, res: Response) {
const task = await taskService.toggle(req.params.id, req.user!.userId)
res.json({ data: task })
}
async delete(req: Request, res: Response) {
await taskService.delete(req.params.id, req.user!.userId)
res.status(204).send()
}
}
```
---
## Quick Start
**1. Install dependencies:**
```bash
# Install all dependencies (client + server)
npm install
# Or individually
cd client && npm install
cd server && npm install
```
**2. Setup environment:**
```bash
cp .env.example .env
# Edit .env with your configuration
```
**3. Setup database:**
```bash
cd server
npx prisma migrate dev
npx prisma generate
```
**4. Start development:**
```bash
# Start all services (client, server, database)
docker-compose up
# Or start individually
npm run dev:client # Frontend on http://localhost:5173
npm run dev:server # Backend on http://localhost:3000
```
**5. Run tests:**
```bash
npm run test # All tests
npm run test:client # Frontend tests
npm run test:server # Backend tests
```
---
## Stack Options
**Frontend:**
- React + TypeScript + Vite (default)
- Next.js 14 (App Router)
- Vue 3 + TypeScript
**Backend:**
- Express + TypeScript (default)
- FastAPI + Python
- NestJS
**Database:**
- PostgreSQL + Prisma (default)
- MongoDB + Mongoose
- MySQL + TypeORM
**Styling:**
- Tailwind CSS (default)
- CSS Modules
- Styled Components
---
## Included Features
**Authentication:**
- JWT authentication
- OAuth (Google, GitHub)
- Email verification
- Password reset
**Testing:**
- Frontend: Jest + React Testing Library + Cypress
- Backend: Jest + Supertest
- E2E: Playwright
**CI/CD:**
- GitHub Actions workflows
- Automated testing
- Docker build and push
- Deployment to cloud platforms
**Development:**
- Hot reload (frontend + backend)
- Docker development environment
- Database migrations
- Seed data
**Production:**
- Optimized Docker images
- Health checks
- Logging and monitoring
- Environment-based config
---
## Customization
**Add Features:**
```bash
# Add payment processing
/ps --add-feature payments --provider stripe
# Add file uploads
/ps --add-feature uploads --storage s3
# Add email service
/ps --add-feature email --provider sendgrid
# Add admin dashboard
/ps --add-feature admin
```
**Change Stack:**
```bash
# Use Next.js instead of React
/ps --frontend nextjs
# Use FastAPI instead of Express
/ps --backend fastapi
# Use MongoDB instead of PostgreSQL
/ps --database mongodb
```
---
## Deployment
**Vercel (Frontend):**
```bash
cd client
vercel
```
**Railway (Backend):**
```bash
cd server
railway up
```
**Docker (Full Stack):**
```bash
docker-compose -f docker-compose.prod.yml up -d
```
---
## Related Commands
- `/express-api-scaffold` - Generate Express API
- `/fastapi-scaffold` - Generate FastAPI
- `/auth-setup` - Authentication boilerplate
- `/env-config-setup` - Environment configuration
---
**Start building immediately. Ship faster. Scale effortlessly.**

View File

@@ -0,0 +1,460 @@
---
description: Generate optimized SQL queries from natural language descriptions
shortcut: sqb
category: database
difficulty: beginner
estimated_time: 2-3 minutes
---
# SQL Query Builder
Generates optimized SQL queries from natural language descriptions, supporting SELECT, INSERT, UPDATE, DELETE with JOINs, indexes, and best practices.
## What This Command Does
**Generated Queries:**
- SELECT queries with JOINs
- INSERT, UPDATE, DELETE operations
- Aggregations and GROUP BY
- Subqueries and CTEs
- Indexes and optimization tips
- PostgreSQL, MySQL, SQLite syntax
**Output:** Production-ready SQL queries
**Time:** 2-3 minutes
---
## Usage
```bash
# Generate query from description
/sql-query-builder "Get all users with their posts"
# Shortcut
/sqb "Find top 10 products by sales"
# Complex query
/sqb "Get users who have made purchases in last 30 days, grouped by country"
# Specific database
/sqb "List orders with customer info" --database postgresql
```
---
## Example Output
**Input:**
```
/sqb "Get all blog posts with author info and comment count, ordered by recent"
```
**Generated SQL:**
```sql
-- Query: Get all blog posts with author info and comment count
-- Database: PostgreSQL
-- Optimized for performance with proper indexes
-- Main query
SELECT
p.id,
p.title,
p.slug,
p.content,
p.published,
p.created_at,
p.updated_at,
-- Author information
u.id AS author_id,
u.name AS author_name,
u.email AS author_email,
-- Comment count (calculated)
COUNT(c.id) AS comment_count
FROM posts p
INNER JOIN users u ON p.author_id = u.id
LEFT JOIN comments c ON p.id = c.post_id
WHERE p.published = true
GROUP BY
p.id,
p.title,
p.slug,
p.content,
p.published,
p.created_at,
p.updated_at,
u.id,
u.name,
u.email
ORDER BY p.created_at DESC
LIMIT 20 OFFSET 0;
-- Recommended indexes for optimal performance
CREATE INDEX IF NOT EXISTS idx_posts_published_created ON posts(published, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_posts_author_id ON posts(author_id);
CREATE INDEX IF NOT EXISTS idx_comments_post_id ON comments(post_id);
-- Explain plan (run to verify performance)
EXPLAIN ANALYZE
SELECT /* ... query above ... */;
```
---
## Query Examples
### **1. Simple SELECT**
**Request:** "Get all active users"
```sql
SELECT
id,
email,
name,
created_at
FROM users
WHERE active = true
ORDER BY created_at DESC;
-- Index recommendation
CREATE INDEX idx_users_active ON users(active, created_at DESC);
```
### **2. JOIN Queries**
**Request:** "Get orders with customer and product information"
```sql
SELECT
o.id AS order_id,
o.order_date,
o.total,
o.status,
-- Customer info
c.id AS customer_id,
c.name AS customer_name,
c.email AS customer_email,
-- Order items
oi.quantity,
oi.price AS unit_price,
-- Product info
p.id AS product_id,
p.name AS product_name
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
INNER JOIN order_items oi ON o.id = oi.order_id
INNER JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY o.created_at DESC;
-- Indexes
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_orders_created_at ON orders(created_at DESC);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);
```
### **3. Aggregations**
**Request:** "Get total sales by product category"
```sql
SELECT
c.name AS category,
COUNT(DISTINCT o.id) AS order_count,
SUM(oi.quantity) AS units_sold,
SUM(oi.quantity * oi.price) AS total_revenue,
AVG(oi.price) AS avg_price
FROM categories c
INNER JOIN products p ON c.id = p.category_id
INNER JOIN order_items oi ON p.id = oi.product_id
INNER JOIN orders o ON oi.order_id = o.id
WHERE o.status = 'completed'
AND o.created_at >= CURRENT_DATE - INTERVAL '1 year'
GROUP BY c.id, c.name
HAVING SUM(oi.quantity * oi.price) > 1000
ORDER BY total_revenue DESC;
```
### **4. Subqueries**
**Request:** "Get users who have never made a purchase"
```sql
SELECT
u.id,
u.email,
u.name,
u.created_at
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = u.id
)
ORDER BY u.created_at DESC;
-- Alternative using LEFT JOIN (often faster)
SELECT
u.id,
u.email,
u.name,
u.created_at
FROM users u
LEFT JOIN orders o ON u.id = o.customer_id
WHERE o.id IS NULL
ORDER BY u.created_at DESC;
```
### **5. Common Table Expressions (CTEs)**
**Request:** "Get top customers by purchase amount with their order history"
```sql
WITH customer_totals AS (
SELECT
c.id,
c.name,
c.email,
COUNT(o.id) AS order_count,
SUM(o.total) AS total_spent
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
WHERE o.status = 'completed'
GROUP BY c.id, c.name, c.email
HAVING SUM(o.total) > 500
)
SELECT
ct.*,
o.id AS order_id,
o.order_date,
o.total AS order_total
FROM customer_totals ct
INNER JOIN orders o ON ct.id = o.customer_id
ORDER BY ct.total_spent DESC, o.order_date DESC;
```
### **6. Window Functions**
**Request:** "Rank products by sales within each category"
```sql
SELECT
p.id,
p.name AS product_name,
c.name AS category_name,
SUM(oi.quantity * oi.price) AS total_sales,
RANK() OVER (
PARTITION BY p.category_id
ORDER BY SUM(oi.quantity * oi.price) DESC
) AS rank_in_category
FROM products p
INNER JOIN categories c ON p.category_id = c.id
INNER JOIN order_items oi ON p.id = oi.product_id
INNER JOIN orders o ON oi.order_id = o.id
WHERE o.status = 'completed'
GROUP BY p.id, p.name, p.category_id, c.name
ORDER BY c.name, rank_in_category;
```
### **7. INSERT Queries**
**Request:** "Insert new user with validation"
```sql
-- Insert single user
INSERT INTO users (id, email, name, password, created_at, updated_at)
VALUES (
gen_random_uuid(),
'[email protected]',
'John Doe',
'hashed_password_here',
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
)
ON CONFLICT (email) DO NOTHING
RETURNING id, email, name, created_at;
-- Bulk insert
INSERT INTO users (id, email, name, password, created_at, updated_at)
VALUES
(gen_random_uuid(), '[email protected]', 'User 1', 'hash1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
(gen_random_uuid(), '[email protected]', 'User 2', 'hash2', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
(gen_random_uuid(), '[email protected]', 'User 3', 'hash3', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (email) DO NOTHING;
```
### **8. UPDATE Queries**
**Request:** "Update product stock after order"
```sql
-- Single update
UPDATE products
SET
stock = stock - 5,
updated_at = CURRENT_TIMESTAMP
WHERE id = 'product-uuid-here'
AND stock >= 5 -- Safety check
RETURNING id, name, stock;
-- Batch update with JOIN
UPDATE products p
SET
stock = p.stock - oi.quantity,
updated_at = CURRENT_TIMESTAMP
FROM order_items oi
WHERE p.id = oi.product_id
AND oi.order_id = 'order-uuid-here'
AND p.stock >= oi.quantity;
```
### **9. DELETE Queries**
**Request:** "Delete old inactive users"
```sql
-- Soft delete (recommended)
UPDATE users
SET
deleted_at = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP
WHERE active = false
AND last_login_at < CURRENT_DATE - INTERVAL '1 year'
RETURNING id, email;
-- Hard delete (with safety checks)
DELETE FROM users
WHERE active = false
AND last_login_at < CURRENT_DATE - INTERVAL '2 years'
AND id NOT IN (
SELECT DISTINCT customer_id FROM orders
);
```
### **10. Full-Text Search**
**Request:** "Search blog posts by keyword"
**PostgreSQL:**
```sql
-- Create text search index
CREATE INDEX idx_posts_search ON posts
USING GIN (to_tsvector('english', title || ' ' || content));
-- Search query
SELECT
id,
title,
content,
ts_rank(
to_tsvector('english', title || ' ' || content),
plainto_tsquery('english', 'search keywords')
) AS relevance
FROM posts
WHERE to_tsvector('english', title || ' ' || content) @@
plainto_tsquery('english', 'search keywords')
AND published = true
ORDER BY relevance DESC, created_at DESC
LIMIT 20;
```
**MySQL:**
```sql
-- Create fulltext index
CREATE FULLTEXT INDEX idx_posts_search ON posts(title, content);
-- Search query
SELECT
id,
title,
content,
MATCH(title, content) AGAINST('search keywords' IN NATURAL LANGUAGE MODE) AS relevance
FROM posts
WHERE MATCH(title, content) AGAINST('search keywords' IN NATURAL LANGUAGE MODE)
AND published = true
ORDER BY relevance DESC, created_at DESC
LIMIT 20;
```
---
## Optimization Tips
**1. Use Indexes Wisely:**
```sql
-- GOOD: Index foreign keys
CREATE INDEX idx_posts_author_id ON posts(author_id);
-- GOOD: Index columns in WHERE clauses
CREATE INDEX idx_posts_published ON posts(published, created_at DESC);
-- GOOD: Partial index for specific queries
CREATE INDEX idx_active_users ON users(email) WHERE active = true;
```
**2. Avoid SELECT *:**
```sql
-- BAD
SELECT * FROM users;
-- GOOD
SELECT id, email, name FROM users;
```
**3. Use LIMIT:**
```sql
-- BAD (fetches all rows)
SELECT * FROM posts ORDER BY created_at DESC;
-- GOOD (pagination)
SELECT * FROM posts ORDER BY created_at DESC LIMIT 20 OFFSET 0;
```
**4. Optimize JOINs:**
```sql
-- Use INNER JOIN when possible (faster than LEFT JOIN)
-- Use EXISTS instead of IN for large datasets
-- BAD
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
-- GOOD
SELECT u.* FROM users u WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
```
---
## Database-Specific Syntax
**PostgreSQL:**
- `gen_random_uuid()` for UUIDs
- `INTERVAL` for date math
- `RETURNING` clause
- Full-text search with `tsvector`
**MySQL:**
- `UUID()` for UUIDs
- `DATE_SUB()` for date math
- FULLTEXT indexes for search
**SQLite:**
- `hex(randomblob(16))` for UUIDs
- `datetime()` for dates
- Limited JOIN types
---
## Related Commands
- `/prisma-schema-gen` - Generate Prisma schemas
- Database Designer (agent) - Schema design review
---
**Query smarter. Optimize faster. Scale confidently.**