$ ls index/

engineering / frontend / nuxt

AI Integration in Nuxt 4

$cat engineering/frontend/nuxt/ai-integration.md

Secure pattern for proxying AI APIs (OpenAI, Gemini, Grok) through Nuxt server routes. Comprehensive Guide Jan 2026 — model names refreshed mid-2026.


Why Server Routes (Not Direct Client Calls)?

ReasonExplanation
SecurityAPI keys stay on the server — never exposed to the browser
FlexibilityHandle streaming, retries, and errors in one place
SSR compatibilityWorks with Nitro; no CORS issues
Cost controlAdd rate limiting, usage tracking, and guards

Step 1: Environment Variables

# .env
OPENAI_API_KEY=sk-your-openai-key
GEMINI_API_KEY=your-google-gemini-key
GROK_API_KEY=your-xai-grok-key
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    openaiApiKey: process.env.OPENAI_API_KEY,
    geminiApiKey: process.env.GEMINI_API_KEY,
    grokApiKey: process.env.GROK_API_KEY,
  }
})

Step 2: Server Routes

OpenAI — server/api/openai/chat.post.ts

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const config = useRuntimeConfig(event)

  const response = await $fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${config.openaiApiKey}`,
      'Content-Type': 'application/json',
    },
    body: {
      model: body.model || 'gpt-5-mini',
      messages: body.messages,
      stream: body.stream || false,
    },
  })

  return response
})

Google Gemini — server/api/gemini/generate.post.ts

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const config = useRuntimeConfig(event)

  const response = await $fetch(
    `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent?key=${config.geminiApiKey}`,
    {
      method: 'POST',
      body: { contents: body.contents },
    }
  )

  return response
})

xAI Grok — server/api/grok/chat.post.ts

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const config = useRuntimeConfig(event)

  return await $fetch('https://api.x.ai/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${config.grokApiKey}`,
      'Content-Type': 'application/json',
    },
    body: {
      model: 'grok-4.3',
      messages: body.messages,
    },
  })
})

Step 3: Frontend Usage

<script setup lang="ts">
const messages = ref<{ role: string; content: string }[]>([])
const userInput = ref('')
const loading = ref(false)

async function sendMessage() {
  if (!userInput.value) return
  const text = userInput.value
  messages.value.push({ role: 'user', content: text })
  userInput.value = ''
  loading.value = true

  const res = await $fetch('/api/openai/chat', {
    method: 'POST',
    body: {
      messages: messages.value,
      model: 'gpt-5-mini',
    },
  })

  messages.value.push({ role: 'assistant', content: res.choices[0].message.content })
  loading.value = false
}
</script>

<template>
  <div class="chat">
    <div v-for="msg in messages" :key="msg.content" :class="msg.role">
      {{ msg.content }}
    </div>
    <input v-model="userInput" @keyup.enter="sendMessage" placeholder="Ask AI..." />
    <button @click="sendMessage" :disabled="loading">Send</button>
  </div>
</template>

Security Checklist

  • Never use AI API keys in useRuntimeConfig().public — only in private runtimeConfig
  • Validate and sanitize all input before forwarding to the API
  • Add rate limiting per user/session (e.g., h3 middleware or Redis)
  • Set spending limits on provider dashboards
  • Sanitize AI output before rendering (avoid XSS if rendering as HTML)
  • Monitor for unusual usage patterns

Cost-Saving Strategies

StrategyHow
CachingStore common responses in Redis/KV; skip API call on cache hit
Prompt optimizationShorter system prompts = fewer tokens per request
Model routingUse cheap/fast model first, escalate only if needed
Batch processingUse batch APIs for non-real-time tasks (often 50% cheaper)
StreamingUse streaming for perceived speed; cancel early if user navigates away

Prompt Engineering Checklist

  • Clear objective stated upfront
  • Specific constraints (length, format, tone)
  • Relevant context provided
  • Examples included for complex tasks
  • Output format specified
  • Edge cases addressed

Let's build something impactful together.

ertughaskan@gmail.com Find me online
Availability
WorkAvailable
RelocationOpen
FreelanceClosed
© 2026 Ertugrul Alex Haskan /cookiesVancouver, BC 🇨🇦

This site uses one optional Google Analytics cookie to see which pages get read. Nothing is set until you choose — cookie details.