AI for Vue developers: the map
Decide where a model runs before you pick a library: on the person's device, or at a provider behind your own server route that holds the key and the caps.
What you'll learn
When you finish, you can do these.- 1Choose where a model runs: the device or a provider
- 2Estimate what an AI feature costs each month
- 3Keep your app's API key on the server, out of every page
- 4Cap a paid model's input, requests and answer length
Picture a notes app, a site where people keep short notes. It wants a Summarise button that turns a long note into three sentences with an AI model, a program trained on huge amounts of text. Before you choose a library, choose where the model runs. That decides who can use the button, whether the note leaves the person's device, who pays for each click, and where the secret key that pays is kept.
The Code tab holds the server half of that button, written for Nuxt, a Vue framework that also runs code on a server: a config file that keeps the key private, and a route, an address the button calls, that asks the model with three limits. By the end you can say why each line is there. You need one idea from the lesson Lifecycle and template refs, in the Beginner track: a component's code, such as a timer it starts, runs in the browser, on the visitor's device.
Where a model can run
A model, a large file of numbers plus the code that runs it, can run in one of two places. One is the person's device, in the browser where your components run. The other is a provider, a company that runs large models and charges for each use. Your page reaches a provider through a route on your own server.
- On the deviceChrome AI, WebLLM
- Your serverAI SDK, the key, caps
- Providerhosted model
On the device, Chrome's built-in AI offers a summariser and a prompt API (a prompt is a request to a model), backed by Gemini Nano, a small model Chrome downloads on first use. Calls are free and send nothing anywhere. Gemini Nano needs Chrome on desktop, 22 GB of free disk, more than 4 GB of video memory or 16 GB of RAM, and an unmetered connection for the download.
Also on the device, the libraries WebLLM and Transformers.js load an open model, free for anyone to run, into the page. WebLLM needs WebGPU (the browser's graphics-card access) and a chat model of roughly 1 to 5 GB. Transformers.js runs on the CPU through WebAssembly (compiled code every current browser runs) by default: slower, but it needs no WebGPU and suits small tasks such as classifying short texts.
The leading providers, Anthropic, OpenAI and Google, host the strongest models, reached directly or through a gateway (one service for many providers). The text travels to the provider, and each request is billed to your API key, the secret that tells the provider who pays. This track calls these models with the AI SDK, a library installed as ai.
Agents, assistants that act for a person, are not a third place: they call your app through WebMCP or MCP (later in this track) and bring their own model.
Choosing a place
Three questions settle most choices: whether the text must stay on the device, which browsers and devices must work, and who pays per request.
Say the notes are private and must work offline, in Safari too. That rules out a hosted model (it needs the network and sees the text) and Chrome's built-in AI (Chrome only), and leaves a model in the page: Transformers.js, which runs in any current browser once its model is cached.
If most people use phones, the answer flips: Chrome's built-in AI is desktop only, and a gigabyte download is a lot to ask on mobile data, so a hosted model behind your route is usually realistic. The Code tab takes this path.
The third question, who pays: nobody on the device, and you at a provider, where the bill is arithmetic. Providers count text in tokens, pieces of words, and charge per million, with output costing more than input.
Say each summary sends 1,500 tokens and gets 300 back, and 2,000 people summarise 5 notes a day: 300,000 requests a month. At an example price of $1 per million input tokens and $5 per million output tokens, input is 300,000 × 1,500 = 450 million tokens, so $450, and output is 90 million tokens, another $450: $900 a month. Check the estimate against your provider's current prices and usage, the token counts each response reports.
Sometimes the answer is no AI: a sort order, a filter or an exact-match search is faster, free and the same every time. Add a model where fuzzy language is the point and a wrong answer is cheap to spot.
Keys and caps live on the server
When the model runs at a provider, the key and the caps, limits on what one visitor can spend, belong on your server. A page's code runs on the visitor's device, where anyone can read or change it.
In Nuxt, the key goes in private runtime config: settings that only server code reads. They sit in nuxt.config.ts, the config file in the Code tab:
export default defineNuxtConfig({
runtimeConfig: {
aiGatewayApiKey: '',
},
})
Nuxt fills the empty string from NUXT_AI_GATEWAY_API_KEY, an environment variable you set on your host; .env.example in the Code tab shows the line for development.
The route, server/api/summarize.post.ts in the Code tab, answers notes posted to /api/summarize. Its first two caps allow notes of 1 to 8,000 characters and 50 requests per visitor a day. Then it calls generateText(), the AI SDK's function that sends one request and waits for the answer:
const { text: summary, usage } = await generateText({
model: gateway('anthropic/claude-haiku-5.5'),
instructions: 'Summarise the note in at most three short sentences.',
prompt: text,
maxOutputTokens: 300,
})
model names the model behind the gateway, and instructions holds the standing rules for every request. maxOutputTokens, the third cap, stops the answer at 300 tokens, and usage reports the tokens spent.
What goes wrong
The first mistake puts the key under public, the part of runtime config Nuxt also sends to the browser.
runtimeConfig: {
public: {
aiGatewayApiKey: '', // NUXT_PUBLIC_AI_GATEWAY_API_KEY
},
},
Nothing looks wrong. Nuxt writes public config into every page as window.__NUXT__.config, so the page source reads public:{aiGatewayApiKey:"sk-...". Anyone who opens View Source can copy the key and spend your budget. Keep it private, outside public:
runtimeConfig: {
aiGatewayApiKey: '', // NUXT_AI_GATEWAY_API_KEY
},
The page source then holds no key.
The second mistake is a route with no caps: readBody() takes whatever is posted.
const { text } = await readBody(event)
const { text: summary } = await generateText({ model: gateway('anthropic/claude-haiku-5.5'), prompt: text })
Nothing stops a loop that posts a 50-page document every second, and the first sign is the invoice. Check the note against Body, the route's rule of 1 to 8,000 characters, and cap the answer:
const { text } = await readValidatedBody(event, Body.parse)
const { text: summary } = await generateText({ model: gateway('anthropic/claude-haiku-5.5'), prompt: text, maxOutputTokens: 300 })
The document is now refused before the model sees it, and no answer passes 300 tokens.
What to remember
- Choose where the model runs first: on the device, or at a provider through your own server route.
- On the device: Chrome's built-in AI (desktop only), WebLLM (WebGPU and a large download) or Transformers.js (any current browser).
- Cost is tokens per request times requests times the price; each response's
usagegives the real numbers. - The app's key lives in private runtime config; anything under
publicis in every page's HTML. - Every route that calls a paid model caps input length, requests per person and
maxOutputTokens.
In your project
Do these in a real project. Each one says how to confirm it worked.- Pick one AI feature you want and write two sentences: where the model runs and why, answering the three questions from Choosing a place. It is done when someone else could tell from them whether the text leaves the device.
- Estimate its monthly cost: tokens in and out per request, requests per person per day, people, and your provider's current prices. Log
usage.totalTokensfrom the route for a day of testing and compare it with your guess. - Put a fake key under
runtimeConfig.public, setNUXT_PUBLIC_AI_GATEWAY_API_KEY=sk-test-123and runnpx nuxt generate, thengrep -rl sk-test .output/public: every page is listed. Move the key out ofpublic, generate again, and the grep finds nothing.