Skills Development Next.js Fullstack Production QA Validator

Next.js Fullstack Production QA Validator

v20260620
vibecode-production-qa-validator
A comprehensive, multi-phase quality assurance tool designed for Next.js fullstack applications. It verifies production readiness by checking code integrity, build success, SEO metadata, API authentication, route stability, performance, and security vulnerabilities. Use it as a mandatory pre-deployment checklist before promoting an app to production.
Get Skill
344 downloads
Overview

Production QA Validator

Run phases in order. Fix failures before moving to next.

When to Use

  • Use before shipping or promoting a fullstack Next.js app to production.
  • Use after large UI, SEO, auth, API, database, or dependency changes need a concrete launch-readiness pass.
  • Use when you need a compact command-driven checklist for build, route, metadata, performance, security, and cleanup checks.
export PROD_URL="https://yourdomain.com"
export QA_AUTH_HEADER=""       # optional: "Bearer eyJ..."
export PAGESPEED_API_KEY=""    # optional: for auto PageSpeed API

Consolidated Runner

qa:all() { qa:code && qa:build && qa:routes / /about /contact /privacy /terms /faq /sitemap.xml /robots.txt /api/health && qa:seo && qa:api /api/health /api/tools && qa:git && qa:smoke; }
qa:full() { qa:all && qa:auth && qa:auth:cookies && qa:lazyload && qa:heavyload && qa:vulns && qa:cleanup && qa:ux:cards && qa:ux:boundaries && qa:ux:animation && qa:database && qa:secure; }

Phase 1: Code Integrity

  • npx tsc --noEmit
  • npx eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0
  • npm test -- --runInBand --passWithNoTests
qa:code() { npx tsc --noEmit && npx eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0 && npm test -- --runInBand --passWithNoTests; }

Phase 2: Build Verification

  • npm run build succeeds
  • SEO pages show / not λ
  • Build log has no errors
qa:build() { local log; log="$(mktemp "${TMPDIR:-/tmp}/qa-build.XXXXXX.log")" || return 1; set -o pipefail; npm run build 2>&1 | tee "$log"; local rc=$?; set +o pipefail; [ "$rc" -eq 0 ] && ! grep -qi "error\|failed" "$log"; local ok=$?; rm -f "$log"; return "$ok"; }
Symbol Meaning
Static
SSG
λ Dynamic/serverless
Partial prerender

Phase 3: API Session & Authentication

  • Auth endpoints respond (login, session, logout)
  • Protected routes return 401/403
  • Session cookie: HttpOnly + Secure + SameSite
  • Cookie not expired, Path/Domain correct
  • No rate limiting bypass
qa:auth() {
  local F=0
  for ep in /api/auth/login /api/auth/session /api/auth/logout; do
    curl -so /dev/null -w "%{http_code}" "$PROD_URL$ep" | grep -q "200\|401" || { echo "  ✗ $ep unreachable"; ((F++)); }
  done
  curl -so /dev/null -w "%{http_code}" "$PROD_URL/api/protected" | grep -q "401\|403" || echo "  ⚠ Protected route not denying unauthenticated"
  return $F
}
qa:auth:cookies() {
  for ep in /api/auth/session /api/auth/login; do
    curl -sI "$PROD_URL$ep" | grep -i "^set-cookie:" | while IFS= read -r c; do
      echo "  $ep: $(echo "$c" | cut -d= -f1)"
      echo "$c" | grep -qi "HttpOnly" || echo "    ✗ Missing HttpOnly"
      echo "$c" | grep -qi "Secure" || echo "    ✗ Missing Secure"
      echo "$c" | grep -qi "SameSite" || echo "    ⚠ Missing SameSite"
    done
  done
}

Phase 4: Route Regression

  • Core pages, sitemap, robots.txt all 200
  • URLs use kebab-case, no duplicate slugs
  • robots.txt allows indexing
  • Sitemap XML valid, all URLs resolve 200
qa:routes() { local F=0; for p; do local C=$(curl -so /dev/null -w "%{http_code}" "$PROD_URL$p"); echo "$C $p"; [ "$C" = "200" ] || ((F++)); done; return $F; }
qa:robots() { curl -s "$PROD_URL/robots.txt" | grep -qi "Disallow: /$" && echo "  ✗ Blocks all crawlers" || echo "  ✓ OK"; }
qa:sitemap() { curl -s "$PROD_URL/sitemap.xml" | python3 -c "import sys,xml.etree.ElementTree as ET; ET.parse(sys.stdin); print('✓ Valid XML')"; }

Phase 5: SEO — Tags, Images, Favicon, Slugs

  • <title> 30–60 chars, unique per page
  • <meta name="description"> in raw HTML
  • og:title matches <title>, og:url matches canonical
  • og:image ≥ 1200×630px, absolute URL, loads 200
  • twitter:card = summary_large_image
  • Canonical self-referencing, no duplicates
  • /favicon.ico 200, apple-touch-icon present
  • hreflang tags if multilingual
  • JSON-LD structured data present
  • Slugs: kebab-case, < 80 chars, no stop words
qa:seo() {
  local H=$(curl -s "$PROD_URL"); local F=0
  for t in "og:title" "og:description" "og:image" "twitter:card" "canonical" "description"; do echo "$H" | grep -qi "$t" || { echo "  ✗ $t"; ((F++)); }; done
  echo "$H" | grep -qi "<title>" || { echo "  ✗ <title>"; ((F++)); }
  local T=$(echo "$H" | grep -oP '<title>\K[^<]+'); local L=${#T}; [ $L -ge 30 -a $L -le 60 ] || echo "  ⚠ Title ${L}chars (target 30-60)"
  curl -so /dev/null -w "%{http_code}" "$PROD_URL/favicon.ico" | grep -q 200 || echo "  ⚠ No favicon.ico"
  return $F
}
qa:seo:ogimage() {
  local I=$(curl -s "$PROD_URL" | grep -oP 'og:image" content="\K[^"]+'); [[ "$I" =~ ^http ]] || I="$PROD_URL$I"
  curl -so /dev/null -w "%{http_code}" "$I" | grep -q 200 || { echo "  ✗ og:image returns non-200"; return 1; }
  command -v identify &>/dev/null && curl -s "$I" | identify -format "%wx%h" - 2>/dev/null | grep -qP "12\d{2}x6\d{2}" && echo "  ✓ ≥ 1200x630" || echo "  ⚠ Install imagemagick to check dimensions"
}

Phase 6: API Route Behavior

  • Correct status codes + Content-Type
  • Errors return consistent JSON { error, message }
  • Response times < 200ms
  • CORS headers correct (if cross-origin)
qa:api() {
  for p; do
    local R=$(curl -so /dev/null -w "%{http_code} %{content_type}" "$PROD_URL$p")
    echo "  $p → $R"
  done
  local E=$(curl -s "$PROD_URL/api/nonexistent")
  echo "$E" | python3 -c "import sys,json; d=json.load(sys.stdin); assert 'error' in d; print('✓ Consistent errors')" 2>/dev/null || echo "  ⚠ Inconsistent error shape"
}

Phase 7: Git Hygiene

  • No secrets/credentials in diff
  • No .next/node_modules staged
  • Commit: type(scope): message
qa:git() {
  local S=$(git diff HEAD 2>/dev/null | grep -i "password\|secret\|api_key\|localhost:3000" | grep "^+")
  [ -n "$S" ] && { echo "  ✗ Secrets in diff!"; echo "$S"; return 1; } || echo "  ✓ No secrets"
  local A=$(git status --short 2>/dev/null | grep -E "\.next|node_modules" | head -3)
  [ -n "$A" ] && echo "  ⚠ Build artifacts:" && echo "$A" || echo "  ✓ No artifacts"
}

Phase 8: Post-Deployment Smoke Test

  • Homepage 200, key pages 200
  • OG image loads 200
  • No console errors (manual)
  • Auth flow works (manual)
qa:smoke() {
  curl -sI "$PROD_URL" | head -1 | grep -q "200" && echo "  ✓ Homepage" || echo "  ✗ Homepage"
  curl -sI "$PROD_URL/sitemap.xml" | head -1 | grep -q "200" && echo "  ✓ Sitemap" || echo "  ✗ Sitemap"
}

Phase 9: Page Speed, Lazy Load & Bundles

  • Lighthouse ≥ 90 (Perf, A11y, SEO)
  • FCP < 2.5s, LCP < 4.0s, CLS < 0.1
  • Images lazy-loaded (loading="lazy"), WebP/AVIF
  • Dynamic imports for heavy components
  • Largest JS chunk < 200KB gzipped
  • font-display: swap, no FOIT
  • Total page weight < 1MB
qa:lazyload() {
  local N=$(grep -r "loading=" app/ --include="*.tsx" 2>/dev/null | grep -c "lazy" || true)
  echo "  Lazy images: $N"
  grep -rn "next/dynamic\|dynamic((" app/ --include="*.tsx" 2>/dev/null | head -5 | grep . || echo "  ⚠ No dynamic imports"
}
qa:heavyload() {
  ls -lhS .next/static/chunks/*.js 2>/dev/null | head -5
  local W=$(curl -so /dev/null -w "%{size_download}" "$PROD_URL" 2>/dev/null || echo 0)
  echo "  HTML weight: ~$((W/1024))KB"
  echo "  ⚠ Run 'npx lighthouse $PROD_URL --view' for full weight analysis"
}
# PageSpeed: open "https://pagespeed.web.dev/?url=$PROD_URL"

Phase 10: Cleanup & Vulnerability Scan

  • npm prune, depcheck — no unused deps
  • No console.log/debugger in staged code
  • npm audit — zero critical/high vulnerabilities
  • No eval/new Function/document.write
  • TODOs resolved
qa:vulns() {
  npm audit 2>/dev/null | grep -E "critical|high" | grep . && echo "  ✗ Vulnerabilities!" || echo "  ✓ No critical/high vulns"
  npm outdated 2>/dev/null | head -5 | grep . || echo "  ✓ All up to date"
  local D=$(grep -rn "eval(\|new Function(\|document.write(" app/ src/ --include="*.ts" --include="*.tsx" 2>/dev/null | head -5)
  [ -n "$D" ] && echo "  ⚠ Dangerous patterns:" && echo "$D" || echo "  ✓ No dangerous patterns"
}
qa:cleanup() {
  local D=$(git diff --cached 2>/dev/null | grep "^+" | grep -i "console\.log\|debugger" | head -5)
  [ -n "$D" ] && echo "  ✗ Debug artifacts:" && echo "$D" || echo "  ✓ No debug artifacts"
  local T=$(git diff --cached 2>/dev/null | grep "^+" | grep -i "TODO\|FIXME\|HACK" | head -5)
  [ -n "$T" ] && echo "  ⚠ TODOs remain:" && echo "$T"
}

Phase 11: UI/UX — Cards, Animation, Error Boundaries

  • Cards: equal height grid, no overlap, text ellipsis, responsive (1→2→3 col)
  • No horizontal scroll at any viewport (320–1440px)
  • Images: consistent aspect-ratio + object-fit: cover
  • Touch targets ≥ 44×44px
  • Animations use transform+opacity only (not layout props)
  • prefers-reduced-motion respected
  • Error boundaries at root + route level (app/error.tsx, app/global-error.tsx)
  • app/not-found.tsx and app/loading.tsx exist
  • All client fetches show loading + error + empty states
  • Buttons: hover, focus-visible, active, disabled, loading states
  • Forms disable submit on click (no double-submit)
qa:ux:cards() {
  local E=$(grep -rn "text-overflow\|line-clamp\|truncate" app/ --include="*.css" --include="*.tsx" 2>/dev/null | head -3)
  [ -n "$E" ] && echo "  ✓ Text overflow handling" || echo "  ⚠ No text overflow handling"
  local A=$(grep -rn "aspect-\|object-fit" app/ --include="*.css" --include="*.tsx" 2>/dev/null | head -3)
  [ -n "$A" ] && echo "  ✓ aspect-ratio/object-fit used" || echo "  ⚠ No aspect-ratio set"
}
qa:ux:boundaries() {
  for f in app/error.tsx app/global-error.tsx app/not-found.tsx app/loading.tsx; do
    [ -f "$f" ] && echo "  ✓ $f" || echo "  ⚠ Missing $f"
  done
}
qa:ux:animation() {
  local A=$(grep -rn "animation.*width\|transition.*height\|@keyframes.*top\|@keyframes.*margin" app/ --include="*.css" --include="*.tsx" 2>/dev/null | head -5)
  [ -n "$A" ] && echo "  ⚠ Layout-triggering animations:" && echo "$A" || echo "  ✓ No layout-triggering animations"
  local P=$(grep -r "@media.*prefers-reduced-motion" app/ --include="*.css" --include="*.tsx" 2>/dev/null | head -3)
  [ -n "$P" ] && echo "  ✓ prefers-reduced-motion found in CSS" || echo "  ⚠ No prefers-reduced-motion in CSS"
}

Phase 12: Database & Data Layer

  • Connection pool configured (no starvation)
  • Schema in sync with migrations
  • Indexes on all queried columns, no N+1
  • No hardcoded DB credentials in source
  • No raw SQL injection risk
  • No sensitive data leaked in API responses
  • Migrations are idempotent
qa:database() {
  local H=$(grep -rn "postgres://\|mysql://\|mongodb://" app/ src/ --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v ".env" | head -5)
  [ -n "$H" ] && { echo "  ✗ Hardcoded DB URL:"; echo "$H"; } || echo "  ✓ No hardcoded DB URLs"
  local R=$(grep -rn "\$queryRaw\|\.raw(" app/ src/ --include="*.ts" --include="*.tsx" 2>/dev/null | head -5)
  [ -n "$R" ] && echo "  ⚠ Raw SQL:" && echo "$R" || echo "  ✓ No raw SQL"
  local N=$(grep -rn "\.findMany\|\.findUnique" app/ src/ --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v "include:" | head -5)
  [ -n "$N" ] && echo "  ⚠ Possible N+1:" && echo "$N" || echo "  ✓ No N+1 patterns"
}
qa:db:migrations() {
  [ -d "prisma/migrations" ] && echo "  ✓ Prisma: $(ls prisma/migrations 2>/dev/null | wc -l) migrations" || echo "  - No prisma migrations dir"
  local M=$(ls db/migrations/*.sql 2>/dev/null | head -5); [ -n "$M" ] && echo "  ✓ SQL migrations:" && echo "$M" || echo "  - No SQL migration files"
}

Phase 13: Secure Data Rendering

  • No secrets/tokens in client source or localStorage
  • No dangerouslySetInnerHTML without DOMPurify
  • API errors don't leak stack traces
  • Internal IDs use UUIDs not auto-increment
  • User emails masked in UI
  • NEXT_PUBLIC_ vars contain no secrets
qa:secure() {
  local S=$(git grep -n "api_key\|API_KEY\|secret_key\|PRIVATE_KEY" -- ':!*.env*' ':!*test*' 2>/dev/null | head -5)
  [ -n "$S" ] && echo "  ✗ Secrets in source:" && echo "$S" || echo "  ✓ No hardcoded secrets"
  local D=$(grep -rn "dangerouslySetInnerHTML" app/ src/ --include="*.tsx" 2>/dev/null | head -5)
  [ -n "$D" ] && echo "  ⚠ XSS risk — use DOMPurify:" && echo "$D" || echo "  ✓ No dangerouslySetInnerHTML"
  local T=$(grep -rn "localStorage\|sessionStorage" app/ src/ --include="*.ts" --include="*.tsx" 2>/dev/null | grep -i "token\|jwt\|secret" | head -5)
  [ -n "$T" ] && echo "  ⚠ Tokens in storage — use httpOnly cookies:" && echo "$T" || echo "  ✓ No tokens in storage"
  curl -s "$PROD_URL/api/nonexistent" 2>/dev/null | grep -qi "stack\|Error:" && echo "  ✗ Stack trace leak" || echo "  ✓ No stack leak"
}

Pre-Commit Hook

cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
npx tsc --noEmit || exit 1
npx eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0 || exit 1
EOF
chmod +x .git/hooks/pre-commit

CI/CD (GitHub Actions)

name: QA
on: [push, pull_request]
jobs:
  qa:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx tsc --noEmit
      - run: npx eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0
      - run: npm test -- --runInBand --passWithNoTests
      - run: npm run build

Best Practices

✅ Do ❌ Don't
Run full 13-phase flow before deploy Skip typecheck or lint
Set PROD_URL in profile/.envrc Hardcode URLs in scripts
OG images ≥ 1200×630 Use small OG images
Animate with transform+opacity Animate width/height/top
Show loading/error/empty states Leave users on blank screens
prefers-reduced-motion for animations Force motion on all users
HttpOnly + Secure cookies for tokens localStorage for auth tokens
Error boundaries at all levels White screen on crash
Database indexes + include/populate N+1 queries in loops
npm audit before deploy Deploy with known vulns

Common Pitfalls

Problem Solution
OG tags missing in raw HTML Use export const metadata in Next.js
Disallow: / in robots.txt Blocks all crawlers — use specific paths
Cards different heights in grid Use display: grid with equal-height rows, not flex
Text overflows card Add text-overflow: ellipsis + overflow: hidden
Animation jank Animate transform not width/height
Form submits twice Disable button on first click
Console errors in prod Add no-console ESLint rule
DB connection timeout Add connection pooling (PgBouncer/Prisma Accelerate)
Sensitive data in API Strip passwordHash/secret in response transformer
App crashes on error Add app/error.tsx error boundary
Large JS bundles Dynamic import heavy components, analyze with next/bundle-analyzer
Images load slowly Add loading="lazy", use WebP/AVIF, resize to display size

Security Notes

  • All qa:* functions are read-only (tsc, lint, test, build, curl, grep)
  • PROD_URL and QA_AUTH_HEADER only for environments you own
  • Basic secret scanning in git diff — for prod, use trufflehog/git-secrets
  • Auth tests with real credentials against prod is destructive — use staging

Limitations

  • Passing all phases reduces risk but doesn't eliminate production bugs
  • Some checks depend on project-specific tooling (Prisma, NextAuth, etc.)
  • Manual UX testing still required for critical user journeys
  • SEO checks verify raw HTML only — not social preview rendering
  • Route checks verify status codes, not content correctness

Master Checklist

Phase 1: Code

  • tsc --noEmit, eslint, npm test pass

Phase 2: Build

  • npm run build succeeds, no errors, pages static

Phase 3: Auth

  • Endpoints respond, protected routes denied, secure cookies

Phase 4: Routes

  • All core pages 200, sitemap valid, robots.txt correct

Phase 5: SEO

  • title, description, og:*, twitter:card, canonical, favicon, slugs

Phase 6: API

  • Status, Content-Type, consistent errors, timing

Phase 7: Git

  • No secrets, no artifacts, conventional commit

Phase 8: Smoke

  • Homepage + key pages 200, og:image loads

Phase 9: Speed

  • Lighthouse ≥ 90, lazy images, dynamic imports, font-display: swap

Phase 10: Clean

  • No vulns, no debug artifacts, unused deps pruned

Phase 11: UI/UX

  • Cards responsive, error boundaries, button states, reduced-motion

Phase 12: Database

  • Indexes, no N+1, no hardcoded URLs, no sensitive leaks

Phase 13: Secure Rendering

  • No secrets in client, no XSS, no stack leaks, UUIDs
Info
Category Development
Name vibecode-production-qa-validator
Version v20260620
Size 17.45KB
Updated At 2026-06-21
Language