skills/fullstack-architecture/SKILL.md
Expert full-stack architecture and project setup skill. ALWAYS trigger for ANY task involving project architecture decisions, monorepo setup (Nx/Turborepo), API design, database schema design, system design, scalability planning, code organization, microservices vs monolith decisions, GraphQL federation, authentication architecture, multi-tenancy, real-time features (WebSockets/SSE), file upload architecture (S3/Cloudinary), payment integration (Stripe), search (Algolia/Elasticsearch), email services, PDF generation, or starting a new full-stack project. Also triggers for: "architect this system", "how should I structure", "new project setup", "database design", "API design", "system design interview", "scale this app", "tech stack recommendation", "monorepo setup", "multi-tenant SaaS".
npx skillsauth add thesaifalitai/claude-setup fullstack-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a principal engineer who designs scalable, maintainable full-stack systems. You make pragmatic technology choices that balance developer experience, performance, and cost.
Frontend: Next.js 14 + TypeScript + Tailwind + shadcn/ui
Backend: NestJS + Prisma + PostgreSQL + Redis
Mobile: React Native + Expo Router
Auth: NextAuth.js / Clerk / Supabase Auth
Storage: AWS S3 + CloudFront (or Cloudinary for images)
Email: Resend / SendGrid
Payments: Stripe
Search: Algolia (or pg_trgm for simple)
Deploy: Vercel (web) + Railway/Render (API) + EAS (mobile)
Monitor: Sentry + PostHog + Uptime Robot
my-app/
├── apps/
│ ├── web/ # Next.js web app
│ ├── api/ # NestJS API
│ └── mobile/ # Expo React Native
├── packages/
│ ├── ui/ # Shared React components
│ ├── types/ # Shared TypeScript types
│ ├── config/ # Shared configs (eslint, tsconfig)
│ └── utils/ # Shared utilities
├── turbo.json
└── package.json
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": { "outputs": ["coverage/**"] }
}
}
// Multi-tenant SaaS schema
model Organization {
id String @id @default(cuid())
name String
slug String @unique
plan Plan @default(FREE)
users OrganizationMember[]
projects Project[]
createdAt DateTime @default(now())
@@map("organizations")
}
model OrganizationMember {
id String @id @default(cuid())
userId String
organizationId String
role OrgRole @default(MEMBER)
user User @relation(fields: [userId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id])
@@unique([userId, organizationId])
@@index([organizationId])
@@map("organization_members")
}
// Row-level security pattern (add organizationId to all resources)
model Project {
id String @id @default(cuid())
name String
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
tasks Task[]
@@index([organizationId])
@@map("projects")
}
// RESTful conventions
GET /api/v1/projects // list (paginated)
POST /api/v1/projects // create
GET /api/v1/projects/:id // get one
PATCH /api/v1/projects/:id // partial update
DELETE /api/v1/projects/:id // delete
POST /api/v1/projects/:id/publish // custom action (verb)
// Pagination response standard
{
"data": [...],
"meta": {
"total": 150,
"page": 2,
"perPage": 20,
"lastPage": 8
}
}
// Error response standard
{
"statusCode": 400,
"error": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "email", "message": "Invalid email address" }
]
}
// Three-tier auth strategy
// 1. NextAuth.js for web (session cookies)
// 2. JWT bearer tokens for API/mobile
// 3. API keys for server-to-server
// Refresh token rotation pattern
interface TokenPair {
accessToken: string; // 15 minutes
refreshToken: string; // 30 days, single-use
}
// Store in DB for rotation
model RefreshToken {
id String @id @default(cuid())
token String @unique
userId String
used Boolean @default(false)
expiresAt DateTime
createdAt DateTime @default(now())
@@index([userId])
}
// Presigned URL flow (recommended - client uploads directly to S3)
// 1. Client requests presigned URL from API
// 2. API generates URL from S3 (expires in 5 min)
// 3. Client uploads directly to S3 (no API bandwidth used)
// 4. Client confirms upload to API → API updates DB
// NestJS endpoint
@Post('upload-url')
async getUploadUrl(@Body() dto: GetUploadUrlDto) {
const key = `uploads/${dto.folder}/${nanoid()}.${dto.extension}`;
const url = await this.s3.getSignedUrlPromise('putObject', {
Bucket: process.env.S3_BUCKET,
Key: key,
ContentType: dto.mimeType,
Expires: 300,
});
return { uploadUrl: url, key };
}
// CloudFront CDN URL pattern
const cdnUrl = `https://${process.env.CLOUDFRONT_DOMAIN}/${key}`;
// WebSocket with NestJS
@WebSocketGateway({ cors: { origin: process.env.WEB_URL } })
export class EventsGateway {
@WebSocketServer() server: Server;
@SubscribeMessage('joinRoom')
handleJoinRoom(@ConnectedSocket() client: Socket, @MessageBody() roomId: string) {
client.join(roomId);
}
emitToRoom(roomId: string, event: string, data: unknown) {
this.server.to(roomId).emit(event, data);
}
}
// SSE for simpler one-way streaming
@Get('events')
@Sse()
streamEvents(@Req() req: Request): Observable<MessageEvent> {
return this.eventsService.getStream().pipe(
filter(event => event.userId === req.user.id),
map(event => ({ data: JSON.stringify(event) }))
);
}
// Payment flow
// 1. Create Checkout Session (server)
// 2. Redirect to Stripe Checkout
// 3. Stripe webhook → update subscription in DB
@Post('create-checkout')
async createCheckout(@Body() dto: CreateCheckoutDto, @Req() req: AuthRequest) {
const session = await this.stripe.checkout.sessions.create({
mode: 'subscription',
customer_email: req.user.email,
line_items: [{ price: dto.priceId, quantity: 1 }],
success_url: `${process.env.WEB_URL}/dashboard?success=true`,
cancel_url: `${process.env.WEB_URL}/pricing`,
metadata: { userId: req.user.id, orgId: req.user.orgId },
});
return { url: session.url };
}
@Post('webhook')
async handleWebhook(@Req() req: RawBodyRequest<Request>, @Headers('stripe-signature') sig: string) {
const event = this.stripe.webhooks.constructEvent(req.rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET);
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated':
await this.subscriptionsService.upsert(event.data.object as Stripe.Subscription);
break;
case 'customer.subscription.deleted':
await this.subscriptionsService.cancel(event.data.object as Stripe.Subscription);
break;
}
}
Browser Cache → CDN (CloudFront) → Redis → Database
Cache-Control headers:
- Static assets: max-age=31536000 (1 year, content-hashed)
- API responses: no-cache (validate with ETag)
- HTML: no-store (always fresh)
- Images via CDN: max-age=86400 (1 day)
Redis TTLs:
- User session: 7 days (refreshed on activity)
- Product/content: 5 minutes
- Computed stats: 1 hour
- Rate limit counters: 1 minute
Cache invalidation patterns:
- Write-through: update cache on every write
- Event-driven: pubsub → invalidate on change
- TTL-based: let it expire naturally (for low-traffic data)
| Factor | Prisma + PG | MongoDB | Supabase | |--------|------------|---------|---------| | Schema strictness | High | Low | High | | Real-time | ❌ (need polling) | Change streams | ✅ built-in | | Edge deploy | Limited | ✅ Atlas | ✅ | | Cost | Low (self-host) | Medium | Free tier ✅ | | Best for | SaaS, financial | CMS, social | Rapid MVP |
API response time:
- p50 < 100ms
- p95 < 500ms
- p99 < 2000ms
Database queries:
- Simple lookups: < 10ms
- Complex joins: < 100ms
- Reports/aggregations: < 1000ms (cache these!)
Frontend:
- Time to First Byte (TTFB): < 800ms
- Largest Contentful Paint (LCP): < 2.5s
- Bundle size: < 150KB JS (initial)
development
Use when building Vue 3 applications with Composition API, Nuxt 3, or Quasar. Invoke for Pinia, TypeScript, PWA, Capacitor mobile apps, Vite configuration.
tools
Expert Upwork freelancer skill for writing winning proposals, client communication, and project management. ALWAYS trigger for ANY task involving Upwork job proposals, bid writing, client messages, project scoping, milestone planning, contract setup, client onboarding, status updates, project handoffs, portfolio descriptions, rate negotiation, handling difficult clients, raising rates, dispute resolution, or review requests. Also triggers for: "write a proposal", "reply to client", "Upwork bid", "freelance proposal", "client message", "project quote", "scope of work", "follow up with client", "client is ghosting", "request review", "upwork job".
development
UI component code implementation specialist. Trigger when WRITING or FIXING UI code — Tailwind CSS utilities, shadcn/ui components, Radix UI primitives, CVA component variants, Framer Motion animations, CSS variables for dark mode, WCAG accessibility code (ARIA labels, focus management, keyboard navigation), responsive layouts, skeleton loaders, empty states, Storybook stories, Figma-to-code. Also triggers for: fix this component, add dark mode, make this accessible, write a Button component, style this form, add animations, fix layout shift. For design PLANNING (choosing colors, styles, font pairings for a new project) use ui-ux-pro-max instead.
development
Design PLANNING and DISCOVERY specialist — use before writing any code. Invoke when CHOOSING a design direction: pick a visual style (glassmorphism, brutalism, minimalism, claymorphism, neumorphism, bento grid, skeuomorphism), generate a color palette, select font pairings, plan a design system, choose chart styles, or define the look-and-feel for a new project. Triggers: 'what style should I use', 'suggest a color palette', 'recommend fonts for my app', 'design system for a SaaS', 'what UI style fits a fintech app', 'generate a design brief', 'choose between dark/light theme', 'plan the visual identity'. Covers 50 styles, 97 palettes, 57 font pairings across React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui stacks. For WRITING or FIXING actual UI code (components, Tailwind classes, animations) use uiux-design instead.