With the rapid advancement of large language model (LLM) technology, more and more web applications are integrating AI capabilities—from intelligent customer support and content summarization to automatic translation. AI is reshaping how frontend development works. For developers building full-stack applications with Nuxt 3, knowing how to safely and efficiently call AI APIs within a project is a topic worth exploring in depth. This article will guide you step by step through integrating OpenAI-compatible APIs in a Nuxt 3 project to implement smart content generation and multilingual translation.
1. Why Server-Side Calls
When calling AI APIs in a Nuxt 3 project, the primary consideration is security. All major AI services (OpenAI, Claude, DeepSeek, etc.) require API Key authentication. If you hardcode the API Key in frontend code, anyone can obtain your key through browser developer tools, causing serious security risks and cost leakage.
Nuxt 3's server/api directory provides the perfect solution: API routes run on the server side (Nitro engine), the API Key is stored in .env environment variables, and the frontend calls its own endpoints via $fetch. The server then forwards requests to the AI service. This way, the API Key is never exposed to the client.
2. Environment Setup
First, create a .env file in your Nuxt 3 project root:
NUXT_OPENAI_API_KEY=sk-your-api-key-here
NUXT_OPENAI_BASE_URL=https://api.openai.com/v1
NUXT_AI_MODEL=gpt-4o-miniNo additional configuration is needed in nuxt.config.ts—Nitro automatically reads environment variables prefixed with NUXT_ and exposes them via useRuntimeConfig() on the server side.
3. Creating the AI Service Layer
For code reusability and maintainability, we first create a generic AI service wrapper. In server/utils/ai.ts:
interface ChatMessage {
role: 'system' | 'user' | 'assistant'
content: string
}
interface ChatOptions {
messages: ChatMessage[]
temperature?: number
maxTokens?: number
stream?: boolean
}
export async function chatCompletion(options: ChatOptions) {
const config = useRuntimeConfig()
const response = await $fetch(`${config.openaiBaseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.openaiApiKey}`,
'Content-Type': 'application/json',
},
body: {
model: config.aiModel,
messages: options.messages,
temperature: options.temperature ?? 0.7,
max_tokens: options.maxTokens ?? 2048,
stream: options.stream ?? false,
},
})
return response
}4. Implementing the Content Generation API Route
Create server/api/ai/generate.post.ts:
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { prompt, systemPrompt } = body
if (!prompt) {
throw createError({
statusCode: 400,
message: 'The prompt parameter is required',
})
}
try {
const result = await chatCompletion({
messages: [
{ role: 'system', content: systemPrompt || 'You are a professional technical content creator.' },
{ role: 'user', content: prompt },
],
temperature: 0.8,
})
return {
success: true,
content: result.choices[0].message.content,
usage: result.usage,
}
} catch (error: any) {
throw createError({
statusCode: 500,
message: error.message || 'AI service call failed',
})
}
})5. Implementing Streaming Responses (SSE)
For content generation scenarios, streaming responses significantly improve user experience—users can see text being generated word by word rather than waiting several seconds for a one-shot display. Create server/api/ai/stream.post.ts:
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { prompt, systemPrompt } = body
const config = useRuntimeConfig()
setHeader(event, 'Content-Type', 'text/event-stream')
setHeader(event, 'Cache-Control', 'no-cache')
setHeader(event, 'Connection', 'keep-alive')
const response = await fetch(
`${config.openaiBaseUrl}/chat/completions`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${config.openaiApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: config.aiModel,
messages: [
{ role: 'system', content: systemPrompt || 'You are a professional assistant.' },
{ role: 'user', content: prompt },
],
stream: true,
}),
}
)
const reader = response.body?.getReader()
if (!reader) throw createError({ statusCode: 500 })
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) {
sendEventStream(event, { data: '[DONE]' })
break
}
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
try {
const parsed = JSON.parse(line.slice(6))
const content = parsed.choices[0]?.delta?.content
if (content) {
sendEventStream(event, { data: content })
}
} catch {}
}
}
}
})6. Frontend Vue Component Integration
Create a reusable AI content generation component:
<script setup lang="ts">
const prompt = ref('')
const generatedContent = ref('')
const isLoading = ref(false)
const isStreaming = ref(false)
async function generateContent() {
if (!prompt.value.trim()) return
isLoading.value = true
generatedContent.value = ''
try {
const data = await $fetch('/api/ai/generate', {
method: 'POST',
body: { prompt: prompt.value },
})
generatedContent.value = data.content
} catch (error) {
console.error('Generation failed:', error)
} finally {
isLoading.value = false
}
}
async function generateStream() {
if (!prompt.value.trim()) return
isStreaming.value = true
generatedContent.value = ''
const eventSource = new EventSource(
`/api/ai/stream?${new URLSearchParams({ prompt: prompt.value })}`
)
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close()
isStreaming.value = false
return
}
generatedContent.value += event.data
}
eventSource.onerror = () => {
eventSource.close()
isStreaming.value = false
}
}
</script>7. Implementing Multilingual Translation
Based on the AI service layer above, implementing multilingual translation is straightforward. Create server/api/ai/translate.post.ts:
export default defineEventHandler(async (event) => {
const { text, targetLang, sourceLang } = await readBody(event)
const langMap: Record<string, string> = {
'zh-cn': 'Simplified Chinese',
'zh-tw': 'Traditional Chinese',
'en': 'English',
'ja': 'Japanese',
}
const result = await chatCompletion({
messages: [
{
role: 'system',
content: `You are a professional translator. Translate the following${sourceLang ? ' ' + (langMap[sourceLang] || sourceLang) : ''} text into ${langMap[targetLang] || targetLang}. Return only the translation, without any explanations.`,
},
{ role: 'user', content: text },
],
temperature: 0.3,
})
return {
success: true,
translation: result.choices[0].message.content,
sourceLang,
targetLang,
}
})8. Error Handling and Rate Limiting
In production environments, AI API calls can fail for various reasons—network timeouts, rate limits, insufficient balance, etc. We need robust error handling mechanisms.
Add retry logic in server/utils/ai.ts:
export async function chatCompletionWithRetry(
options: ChatOptions,
maxRetries = 3
) {
for (let i = 0; i < maxRetries; i++) {
try {
return await chatCompletion(options)
} catch (error: any) {
if (error.statusCode === 429) {
// Rate limited — wait and retry
const retryAfter = error.headers?.get('retry-after') || 5
await new Promise(r => setTimeout(r, retryAfter * 1000))
continue
}
if (i === maxRetries - 1) throw error
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)))
}
}
}It's also recommended to set request body size limits in the Nitro configuration:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
routeRules: {
'/api/ai/**': {
maxBodySize: '1mb',
},
},
},
})9. Summary
Through Nuxt 3's Server API routes, we've integrated AI capabilities in a secure and efficient manner. Key takeaways:
- Security: API Keys are stored in server-side environment variables, never exposed to the client
- Streaming Responses: SSE enables real-time content generation, enhancing user experience
- Reusable Architecture: Through a service layer abstraction, content generation and translation share the same AI calling logic
- Robustness: Exponential backoff retry strategy handles rate limiting and network anomalies
This architecture has been running reliably across multiple projects at Salong Network—whether automatically generating technical articles and product descriptions, or handling multilingual site content translation, it performs efficiently. I hope this article helps you successfully integrate AI capabilities into your Nuxt 3 projects.




