TechSetupGuides
IntermediateNext.jsAIVercel AI SDKSupabaseShadcn/uiTailwind CSSTypeScriptZod

AI-Native Full Stack: Next.js 15 + Vercel AI SDK

Build production-ready AI-powered web applications with Next.js 15, Vercel AI SDK, Supabase, Shadcn/ui, Tailwind CSS 4, and Zod. Includes complete setup, AI streaming patterns, RAG implementation, and deployment best practices.

  1. Step 1

    Project Scaffolding

    Initialize a new Next.js 15 project with Turbopack and TypeScript. This sets up the modern Next.js App Router with React 19.

    npx create-next-app@latest my-ai-app --typescript --tailwind --app --turbopack --no-src-dir
    cd my-ai-app
  2. Step 2

    Install Core Dependencies

    Add the essential packages for the AI-native stack. The Vercel AI SDK provides streaming and tool-calling capabilities, Supabase handles backend services, Shadcn/ui provides accessible components, and Zod ensures type-safe validation.

    npm install ai @ai-sdk/anthropic @ai-sdk/openai zod
    npm install @supabase/supabase-js @supabase/ssr
    npm install -D shadcn-ui @radix-ui/react-slot class-variance-authority clsx tailwind-merge
  3. Step 3

    Initialize Shadcn/ui

    Set up Shadcn/ui component library with the new Tailwind CSS 4 JIT compiler. This creates the necessary configuration and utilities.

    npx shadcn@latest init -y
    npx shadcn@latest add button card input textarea scroll-area
    ⚠ Heads up: Ensure you select 'New York' style and Zinc as the base color when prompted during init.
  4. Step 4

    Upgrade to Tailwind CSS 4

    Migrate to Tailwind CSS 4 which uses CSS-first configuration and improved JIT compilation. Replace the existing Tailwind config with the new CSS-based approach.

    npm install tailwindcss@next @tailwindcss/postcss@next
    npm uninstall tailwindcss
  5. Step 5

    Configure Tailwind CSS 4

    Create the new CSS-first configuration. Tailwind CSS 4 moves configuration from JS to CSS using @theme directive.

    @import "tailwindcss";
    
    @theme {
      /* Custom design tokens */
      --color-primary: #3b82f6;
      --color-secondary: #8b5cf6;
      --font-sans: system-ui, -apple-system, sans-serif;
      --breakpoint-3xl: 1920px;
    }
    
    @layer base {
      :root {
        --background: 0 0% 100%;
        --foreground: 222.2 84% 4.9%;
        --card: 0 0% 100%;
        --card-foreground: 222.2 84% 4.9%;
        --primary: 222.2 47.4% 11.2%;
        --primary-foreground: 210 40% 98%;
      }
      .dark {
        --background: 222.2 84% 4.9%;
        --foreground: 210 40% 98%;
      }
    }
  6. Step 6

    Set Up Environment Variables

    Create .env.local file with required API keys and configuration. Never commit this file to version control.

    # AI Provider Keys (choose one or both)
    ANTHROPIC_API_KEY=your_anthropic_key
    OPENAI_API_KEY=your_openai_key
    
    # Supabase Configuration
    NEXT_PUBLIC_SUPABASE_URL=your_project_url
    NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
    SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
    ⚠ Heads up: The service role key bypasses Row Level Security. Only use it in API routes, never expose it to the client.
  7. Step 7

    Configure Supabase Client

    Create type-safe Supabase clients for both client and server components. Next.js 15 requires careful handling of Server Components.

    // lib/supabase/client.ts
    import { createBrowserClient } from '@supabase/ssr'
    import type { Database } from '@/types/database.types'
    
    export function createClient() {
      return createBrowserClient<Database>(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
      )
    }
    
    // lib/supabase/server.ts
    import { createServerClient } from '@supabase/ssr'
    import { cookies } from 'next/headers'
    import type { Database } from '@/types/database.types'
    
    export async function createClient() {
      const cookieStore = await cookies()
    
      return createServerClient<Database>(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
          cookies: {
            getAll() {
              return cookieStore.getAll()
            },
            setAll(cookiesToSet) {
              cookiesToSet.forEach(({ name, value, options }) => {
                cookieStore.set(name, value, options)
              })
            },
          },
        }
      )
    }
  8. Step 8

    Initialize Supabase Project

    Set up your Supabase project with essential tables for AI-powered features. This includes user management, chat history, and vector embeddings for RAG.

    -- Enable required extensions
    create extension if not exists "uuid-ossp";
    create extension if not exists "vector";
    
    -- Chat conversations table
    create table conversations (
      id uuid primary key default uuid_generate_v4(),
      user_id uuid references auth.users(id) on delete cascade,
      title text not null,
      created_at timestamp with time zone default now(),
      updated_at timestamp with time zone default now()
    );
    
    -- Chat messages table
    create table messages (
      id uuid primary key default uuid_generate_v4(),
      conversation_id uuid references conversations(id) on delete cascade,
      role text not null check (role in ('user', 'assistant', 'system')),
      content text not null,
      created_at timestamp with time zone default now()
    );
    
    -- Vector embeddings for RAG
    create table embeddings (
      id uuid primary key default uuid_generate_v4(),
      content text not null,
      embedding vector(1536),
      metadata jsonb,
      created_at timestamp with time zone default now()
    );
    
    -- Enable Row Level Security
    alter table conversations enable row level security;
    alter table messages enable row level security;
    alter table embeddings enable row level security;
    
    -- RLS Policies
    create policy "Users can view own conversations"
      on conversations for select
      using (auth.uid() = user_id);
    
    create policy "Users can insert own conversations"
      on conversations for insert
      with check (auth.uid() = user_id);
    
    create policy "Users can view messages in own conversations"
      on messages for select
      using (exists (
        select 1 from conversations
        where conversations.id = messages.conversation_id
        and conversations.user_id = auth.uid()
      ));
    
    -- Create indexes for performance
    create index messages_conversation_id_idx on messages(conversation_id);
    create index embeddings_embedding_idx on embeddings using ivfflat (embedding vector_cosine_ops);
  9. Step 9

    Create Zod Validation Schemas

    Define type-safe schemas for your API routes and forms. Zod provides runtime validation that matches TypeScript types.

    // lib/schemas/chat.ts
    import { z } from 'zod'
    
    export const messageSchema = z.object({
      role: z.enum(['user', 'assistant', 'system']),
      content: z.string().min(1).max(10000),
    })
    
    export const chatRequestSchema = z.object({
      messages: z.array(messageSchema),
      conversationId: z.string().uuid().optional(),
      temperature: z.number().min(0).max(2).default(0.7),
      maxTokens: z.number().min(1).max(4096).default(2048),
    })
    
    export const ragQuerySchema = z.object({
      query: z.string().min(1).max(500),
      topK: z.number().min(1).max(10).default(5),
      threshold: z.number().min(0).max(1).default(0.7),
    })
    
    export type Message = z.infer<typeof messageSchema>
    export type ChatRequest = z.infer<typeof chatRequestSchema>
    export type RagQuery = z.infer<typeof ragQuerySchema>
  10. Step 10

    Create AI Chat API Route with Streaming

    Implement a chat endpoint using the Vercel AI SDK's streaming capabilities. This provides real-time responses with proper error handling.

    // app/api/chat/route.ts
    import { anthropic } from '@ai-sdk/anthropic'
    import { streamText } from 'ai'
    import { chatRequestSchema } from '@/lib/schemas/chat'
    import { createClient } from '@/lib/supabase/server'
    
    export const runtime = 'edge'
    
    export async function POST(req: Request) {
      try {
        const body = await req.json()
        const { messages, conversationId, temperature, maxTokens } = chatRequestSchema.parse(body)
    
        const supabase = await createClient()
        const { data: { user } } = await supabase.auth.getUser()
    
        if (!user) {
          return new Response('Unauthorized', { status: 401 })
        }
    
        // Verify conversation ownership if conversationId provided
        if (conversationId) {
          const { data: conversation } = await supabase
            .from('conversations')
            .select('user_id')
            .eq('id', conversationId)
            .single()
    
          if (!conversation || conversation.user_id !== user.id) {
            return new Response('Forbidden', { status: 403 })
          }
        }
    
        // Stream the AI response
        const result = streamText({
          model: anthropic('claude-3-5-sonnet-20241022'),
          messages,
          temperature,
          maxTokens,
          async onFinish({ text }) {
            // Save messages to database after streaming completes
            if (conversationId) {
              await supabase.from('messages').insert([
                { conversation_id: conversationId, role: 'user', content: messages[messages.length - 1].content },
                { conversation_id: conversationId, role: 'assistant', content: text },
              ])
            }
          },
        })
    
        return result.toDataStreamResponse()
      } catch (error) {
        console.error('Chat API error:', error)
        return new Response('Internal Server Error', { status: 500 })
      }
    }
    ⚠ Heads up: The edge runtime has limitations. For complex operations like vector embeddings, use the Node.js runtime instead.
  11. Step 11

    Create RAG Implementation

    Build a Retrieval-Augmented Generation system using Supabase's vector database. This allows AI to answer questions based on your custom knowledge base.

    // app/api/rag/query/route.ts
    import { anthropic } from '@ai-sdk/anthropic'
    import { generateText, embed } from 'ai'
    import { openai } from '@ai-sdk/openai'
    import { ragQuerySchema } from '@/lib/schemas/chat'
    import { createClient } from '@/lib/supabase/server'
    
    export async function POST(req: Request) {
      try {
        const body = await req.json()
        const { query, topK, threshold } = ragQuerySchema.parse(body)
    
        const supabase = await createClient()
    
        // Generate embedding for the query
        const { embedding } = await embed({
          model: openai.embedding('text-embedding-3-small'),
          value: query,
        })
    
        // Search for similar documents
        const { data: documents, error } = await supabase.rpc(
          'match_embeddings',
          {
            query_embedding: embedding,
            match_threshold: threshold,
            match_count: topK,
          }
        )
    
        if (error) throw error
    
        // Build context from retrieved documents
        const context = documents
          .map((doc: any) => doc.content)
          .join('\n\n')
    
        // Generate response with context
        const { text } = await generateText({
          model: anthropic('claude-3-5-sonnet-20241022'),
          messages: [
            {
              role: 'system',
              content: `You are a helpful assistant. Use the following context to answer the user's question. If the answer cannot be found in the context, say so.\n\nContext:\n${context}`,
            },
            {
              role: 'user',
              content: query,
            },
          ],
        })
    
        return Response.json({
          answer: text,
          sources: documents.map((d: any) => ({
            content: d.content,
            similarity: d.similarity,
          })),
        })
      } catch (error) {
        console.error('RAG query error:', error)
        return new Response('Internal Server Error', { status: 500 })
      }
    }
  12. Step 12

    Create Vector Similarity Search Function

    Add a PostgreSQL function to perform efficient vector similarity search. This is required for the RAG implementation.

    -- Create vector similarity search function
    create or replace function match_embeddings (
      query_embedding vector(1536),
      match_threshold float,
      match_count int
    )
    returns table (
      id uuid,
      content text,
      metadata jsonb,
      similarity float
    )
    language sql stable
    as $$
      select
        embeddings.id,
        embeddings.content,
        embeddings.metadata,
        1 - (embeddings.embedding <=> query_embedding) as similarity
      from embeddings
      where 1 - (embeddings.embedding <=> query_embedding) > match_threshold
      order by embeddings.embedding <=> query_embedding
      limit match_count;
    $$;
  13. Step 13

    Build AI Chat Component

    Create a reusable chat interface component using Shadcn/ui and the Vercel AI SDK's React hooks. The useChat hook handles streaming, message management, and error states automatically.

    // components/chat.tsx
    'use client'
    
    import { useChat } from 'ai/react'
    import { Button } from '@/components/ui/button'
    import { Card } from '@/components/ui/card'
    import { Input } from '@/components/ui/input'
    import { ScrollArea } from '@/components/ui/scroll-area'
    
    export function Chat() {
      const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
        api: '/api/chat',
      })
    
      return (
        <Card className="w-full max-w-2xl mx-auto p-4">
          <ScrollArea className="h-[500px] pr-4">
            <div className="space-y-4">
              {messages.map((message) => (
                <div
                  key={message.id}
                  className={`flex ${
                    message.role === 'user' ? 'justify-end' : 'justify-start'
                  }`}
                >
                  <div
                    className={`rounded-lg px-4 py-2 max-w-[80%] ${
                      message.role === 'user'
                        ? 'bg-primary text-primary-foreground'
                        : 'bg-muted'
                    }`}
                  >
                    {message.content}
                  </div>
                </div>
              ))}
            </div>
          </ScrollArea>
    
          <form onSubmit={handleSubmit} className="mt-4 flex gap-2">
            <Input
              value={input}
              onChange={handleInputChange}
              placeholder="Type your message..."
              disabled={isLoading}
              className="flex-1"
            />
            <Button type="submit" disabled={isLoading}>
              {isLoading ? 'Sending...' : 'Send'}
            </Button>
          </form>
        </Card>
      )
    }
  14. Step 14

    Implement Supabase Authentication

    Set up GitHub OAuth authentication with proper session management. This integrates seamlessly with Supabase's built-in auth system.

    // app/auth/callback/route.ts
    import { createClient } from '@/lib/supabase/server'
    import { NextResponse } from 'next/server'
    
    export async function GET(request: Request) {
      const requestUrl = new URL(request.url)
      const code = requestUrl.searchParams.get('code')
      const origin = requestUrl.origin
    
      if (code) {
        const supabase = await createClient()
        await supabase.auth.exchangeCodeForSession(code)
      }
    
      return NextResponse.redirect(`${origin}/`)
    }
    
    // components/auth-button.tsx
    'use client'
    
    import { createClient } from '@/lib/supabase/client'
    import { Button } from '@/components/ui/button'
    
    export function AuthButton({ user }: { user: any }) {
      const supabase = createClient()
    
      const handleSignIn = async () => {
        await supabase.auth.signInWithOAuth({
          provider: 'github',
          options: {
            redirectTo: `${location.origin}/auth/callback`,
          },
        })
      }
    
      const handleSignOut = async () => {
        await supabase.auth.signOut()
        location.reload()
      }
    
      return user ? (
        <Button onClick={handleSignOut}>Sign Out</Button>
      ) : (
        <Button onClick={handleSignIn}>Sign In with GitHub</Button>
      )
    }
  15. Step 15

    Configure Supabase Auth Provider

    Enable GitHub OAuth in your Supabase dashboard. Go to Authentication > Providers > GitHub and add your OAuth credentials.

    # 1. Create a GitHub OAuth App at https://github.com/settings/developers
    # 2. Set Authorization callback URL to: https://your-project.supabase.co/auth/v1/callback
    # 3. Copy Client ID and Client Secret to Supabase dashboard
    # 4. Enable GitHub provider in Supabase Auth settings
    ⚠ Heads up: For local development, add http://localhost:54321/auth/v1/callback as an additional callback URL.
  16. Step 16

    Create Next.js Middleware for Auth

    Implement middleware to refresh Supabase sessions automatically and protect authenticated routes.

    // middleware.ts
    import { createServerClient } from '@supabase/ssr'
    import { NextResponse, type NextRequest } from 'next/server'
    
    export async function middleware(request: NextRequest) {
      let response = NextResponse.next({
        request: {
          headers: request.headers,
        },
      })
    
      const supabase = createServerClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
          cookies: {
            getAll() {
              return request.cookies.getAll()
            },
            setAll(cookiesToSet) {
              cookiesToSet.forEach(({ name, value, options }) => {
                request.cookies.set(name, value)
              })
              response = NextResponse.next({
                request,
              })
              cookiesToSet.forEach(({ name, value, options }) => {
                response.cookies.set(name, value, options)
              })
            },
          },
        }
      )
    
      const { data: { user } } = await supabase.auth.getUser()
    
      // Redirect to login if accessing protected route without auth
      if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
        return NextResponse.redirect(new URL('/login', request.url))
      }
    
      return response
    }
    
    export const config = {
      matcher: [
        '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
      ],
    }
  17. Step 17

    Implement AI Tool Calling

    Add function calling capabilities to enable AI to interact with external tools and APIs. This is powerful for building AI agents.

    // app/api/chat-with-tools/route.ts
    import { anthropic } from '@ai-sdk/anthropic'
    import { streamText, tool } from 'ai'
    import { z } from 'zod'
    
    export const runtime = 'edge'
    
    export async function POST(req: Request) {
      const { messages } = await req.json()
    
      const result = streamText({
        model: anthropic('claude-3-5-sonnet-20241022'),
        messages,
        tools: {
          weather: tool({
            description: 'Get the current weather for a location',
            parameters: z.object({
              location: z.string().describe('The city and state, e.g. San Francisco, CA'),
            }),
            execute: async ({ location }) => {
              // In production, call a real weather API
              return {
                location,
                temperature: 72,
                condition: 'sunny',
              }
            },
          }),
          searchDatabase: tool({
            description: 'Search the knowledge base for relevant information',
            parameters: z.object({
              query: z.string().describe('The search query'),
            }),
            execute: async ({ query }) => {
              // Call your RAG endpoint
              const response = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/rag/query`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ query }),
              })
              return await response.json()
            },
          }),
        },
        maxSteps: 5,
      })
    
      return result.toDataStreamResponse()
    }
  18. Step 18

    Set Up Production Deployment on Vercel

    Configure your project for deployment to Vercel with optimized settings for AI workloads.

    // vercel.json
    {
      "buildCommand": "next build",
      "devCommand": "next dev --turbopack",
      "framework": "nextjs",
      "regions": ["iad1"],
      "env": {
        "NEXT_PUBLIC_SUPABASE_URL": "@supabase-url",
        "NEXT_PUBLIC_SUPABASE_ANON_KEY": "@supabase-anon-key"
      },
      "functions": {
        "app/api/chat/route.ts": {
          "maxDuration": 60
        },
        "app/api/rag/query/route.ts": {
          "maxDuration": 30
        }
      }
    }
    ⚠ Heads up: Set environment variables in Vercel dashboard, not in vercel.json for sensitive keys like ANTHROPIC_API_KEY.
  19. Step 19

    Configure TypeScript Paths and Compilers

    Optimize TypeScript configuration for Next.js 15 and enable strict mode for better type safety.

    // tsconfig.json
    {
      "compilerOptions": {
        "target": "ES2022",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "strict": true,
        "noEmit": true,
        "esModuleInterop": true,
        "module": "esnext",
        "moduleResolution": "bundler",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "preserve",
        "incremental": true,
        "plugins": [
          {
            "name": "next"
          }
        ],
        "paths": {
          "@/*": ["./*"]
        }
      },
      "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
      "exclude": ["node_modules"]
    }
  20. Step 20

    Best Practices: Error Handling

    Implement comprehensive error handling for AI responses and stream interruptions. The Vercel AI SDK provides hooks for error management.

    Key practices:

    1. Always validate inputs with Zod before processing AI requests
    2. Use try-catch blocks in API routes and wrap async operations
    3. Implement timeout handlers for long-running AI operations
    4. Provide fallback responses when AI services are unavailable
    5. Log errors properly but never expose API keys or sensitive data
    6. Handle stream interruptions gracefully when users navigate away

    Common pitfalls to avoid:

    • Not validating user inputs before sending to AI APIs
    • Exposing error stack traces to users in production
    • Missing rate limiting on AI endpoints (can be expensive)
    • Not handling Supabase session expiration
    • Forgetting to clean up event listeners in streaming components
  21. Step 21

    Best Practices: Security

    Security is critical when building AI applications with user data and API access.

    Essential security measures:

    1. Row Level Security (RLS): Always enable RLS on Supabase tables
    2. Input sanitization: Validate and sanitize all user inputs before AI processing
    3. API key protection: Never expose service role keys to the client
    4. Rate limiting: Implement rate limits on AI endpoints to prevent abuse
    5. Content filtering: Add content moderation for user-generated prompts
    6. CORS configuration: Restrict API access to your domains only

    Supabase RLS example:

    -- Only allow users to read their own data
    create policy "Users can only view own conversations"
      on conversations for select
      using (auth.uid() = user_id);
    
    -- Prevent privilege escalation
    create policy "Users cannot modify other users' data"
      on conversations for update
      using (auth.uid() = user_id);
    
  22. Step 22

    Best Practices: Performance Optimization

    Optimize your AI application for production performance and cost efficiency.

    Performance tips:

    1. Enable prompt caching: Anthropic's prompt caching can reduce costs by 90%
    2. Use streaming: Always stream AI responses for better UX
    3. Implement request deduplication: Prevent duplicate AI calls
    4. Optimize vector search: Use proper indexing (IVFFlat or HNSW)
    5. Edge runtime: Use edge functions for low-latency responses
    6. Database connection pooling: Use Supabase pooler for high traffic

    Next.js 15 specific optimizations:

    • Use loading.tsx files for instant loading states
    • Implement React Suspense boundaries around AI components
    • Use Server Components for initial data fetching
    • Enable Turbopack for faster local development
    • Leverage Next.js 15's improved caching with unstable_cache
  23. Step 23

    Common Pitfalls and Solutions

    Learn from common mistakes when building AI-native applications.

    Pitfall #1: Not handling streaming errors

    Problem: Stream interruptions cause UI to freeze

    Solution: Always implement onError handlers in useChat

    Pitfall #2: Supabase session expiration

    Problem: Users get logged out during long AI conversations

    Solution: Implement session refresh in middleware and use @supabase/ssr

    Pitfall #3: Vector embedding costs

    Problem: Expensive embeddings for every query

    Solution: Cache embeddings and use hybrid search (keyword + vector)

    Pitfall #4: Turbopack compatibility issues

    Problem: Some packages don't work with Turbopack

    Solution: Use next dev without --turbopack if needed, or update dependencies

    Pitfall #5: Edge runtime limitations

    Problem: Node.js APIs not available in edge runtime

    Solution: Use Node.js runtime for complex operations, edge for simple streaming

  24. Step 24

    Migration Path from Older Stacks

    If you're migrating from an older tech stack, follow this upgrade path:

    From Next.js 13/14 to 15:

    1. Update to React 19: npm install react@rc react-dom@rc
    2. Update Next.js: npm install next@latest
    3. Replace metadata exports with async functions if needed
    4. Update middleware to handle new cookie API
    5. Test App Router changes (stricter now)

    From Tailwind CSS 3 to 4:

    1. Install v4: npm install tailwindcss@next
    2. Move tailwind.config.js content to CSS using @theme
    3. Update @import statements in global CSS
    4. Remove PostCSS tailwindcss plugin if using new defaults

    From LangChain to Vercel AI SDK:

    1. Replace LangChain streaming with streamText()
    2. Convert LangChain tools to Zod-based tool() definitions
    3. Update prompt templates to message arrays
    4. Migrate callbacks to onFinish hooks
  25. Step 25

    Testing AI Features

    Set up comprehensive testing for AI-powered features.

    Unit testing with Jest:

    // __tests__/api/chat.test.ts
    import { POST } from '@/app/api/chat/route'
    import { chatRequestSchema } from '@/lib/schemas/chat'
    
    describe('/api/chat', () => {
      it('validates request schema', () => {
        const validRequest = {
          messages: [{ role: 'user', content: 'Hello' }],
          temperature: 0.7,
          maxTokens: 100,
        }
        expect(() => chatRequestSchema.parse(validRequest)).not.toThrow()
      })
    
      it('rejects invalid messages', () => {
        const invalidRequest = {
          messages: [{ role: 'invalid', content: '' }],
        }
        expect(() => chatRequestSchema.parse(invalidRequest)).toThrow()
      })
    })
    

    Integration testing:

    Use Playwright for E2E tests with actual AI responses. Mock AI responses in CI to avoid costs.

  26. Step 26

    Production Checklist

    Before deploying to production, verify these items:

    Environment & Deployment:

    • [ ] All environment variables set in Vercel dashboard
    • [ ] Supabase production instance configured
    • [ ] Database migrations applied
    • [ ] RLS policies tested and enabled
    • [ ] API keys rotated and secured

    Performance:

    • [ ] Prompt caching enabled for Anthropic API calls
    • [ ] Vector indexes created on embeddings table
    • [ ] Edge functions deployed for low-latency routes
    • [ ] Database connection pooling configured
    • [ ] Rate limiting implemented on AI endpoints

    Security:

    • [ ] Content moderation enabled
    • [ ] Input validation on all API routes
    • [ ] CORS configured properly
    • [ ] Service role key never exposed to client
    • [ ] Session refresh working correctly

    Monitoring:

    • [ ] Error tracking configured (Sentry, LogRocket)
    • [ ] AI usage monitoring set up
    • [ ] Cost alerts configured for API usage
    • [ ] Performance monitoring enabled

    User Experience:

    • [ ] Loading states for all AI interactions
    • [ ] Error messages user-friendly
    • [ ] Streaming working smoothly
    • [ ] Mobile responsive design
    • [ ] Accessibility tested

Feature requests

Sign in to suggest features or vote on existing ones.

No feature requests yet.

Discussion

0 people marked this as worked·Sign in to mark your own.

Sign in to join the discussion.

No comments yet.