Your First AI Feature

Mini-project: a 'summarize this note' button powered by the Lovable AI Gateway.

20 min read·6 parts·intermediate

You now have auth (Lesson 10), a database (Lesson 11), relationships (Lesson 12), and file storage (Lesson 13). Time for the fourth pillar of Cloud: AI. In this lesson you bolt a Summarize this note button onto the notes app from Lesson 11 — one click, three tidy bullets, no API keys to babysit.


Part 1 · Why an AI Gateway

Wiring AI the traditional way means picking a provider (OpenAI, Anthropic, Google), creating an account, generating a key, storing it as a secret, learning that provider's SDK, and rewriting the whole thing when you want to try a different model. The Lovable AI Gateway collapses all of that into one endpoint you can call from a server function. One key (already provisioned for you), one shape, many models behind it.

Avoid
Direct-to-provider
Separate SDK per provider. Separate key per provider. Separate billing dashboard. Switching models means rewriting the call site.
Do
Through the Gateway
One LOVABLE_API_KEY. One helper. Change the model string to switch families. Usage rolls up into your Cloud account.
Figure 1.1
your appAI Gatewayone key · many modelsGeminiGPTClaude
One hub instead of a fan of provider SDKs. Your app talks to the Gateway; the Gateway talks to whichever model you ask for.
Why not just call the API from the browser?
Two reasons: secrets and cost. Your API key never belongs in client code — anyone viewing source could burn your credits. And you want a chokepoint where you can rate-limit, log, and swap models. That chokepoint is a server function.

Part 2 · The mental model

Every AI feature in your app has the same five-step shape. Memorise this and you'll never have to guess where to put the code again.

  1. 1Click

    User clicks a button (or submits a form) in the browser.

  2. 2Server fn

    Client calls a createServerFn that fetches whatever it needs from the DB.

  3. 3Gateway

    Server fn sends prompt + model choice to the AI Gateway.

  4. 4Model

    The model returns text (or structured JSON, or a stream).

  5. 5Save + render

    Server fn writes the result back to the DB and returns it. UI updates.

Ask for a server function named summarizeNote that takes a noteId, loads the note, sends its body to the AI Gateway, saves the summary onto the note row, and returns it.
One prompt, one function
Keep one server function per AI job. Don't build a giant "callAI" endpoint that dispatches on strings. When you need a second feature (categorise, translate, expand), write a second function — clearer prompts, cleaner logs.

Part 3 · Picking a model

The Gateway exposes several model families. You don't need to know them all — you need to know the axis they trade on: speed & cost on one end, reasoning power on the other. For 90% of app features (summaries, tags, classify, short rewrites), the small/fast model is the right default.

Do
Fast & cheap (default)
Sub-second responses, tiny cost per call, plenty smart for summaries, tags, rewrites, extraction. Start here for every feature. Example: gemini-flash-class.
Do
Slow & smart
Multi-second responses, higher cost, worth it for multi-step reasoning, long documents, code, complex structured output. Example: gpt- or claude-class.
  • Default to small. Ship the feature on the fast model, only upgrade when you can point at an output that's genuinely worse.
  • Match model to task. "Summarize a 200-word note" is not the same job as "refactor this file" — don't pay Opus prices for Flash-tier work.
  • Model choice is a one-line change. With the Gateway you swap a string. Try two side by side before committing.

Part 4 · Mini-project — Summarize this note

We're adding a single button to each note card. Click it → the server function runs → a summary appears under the note body. Nothing else in the notes app changes.

Figure 1.2
notes.lovable.app
Idle
Long walk after lunch. Ideas about the launch page and a nagging thought about our onboarding email cadence…
Loading
Long walk after lunch. Ideas about the launch page…
Thinking…
Done
Long walk after lunch. Ideas about the launch page…
Summary
  • Reflected on launch page priorities.
  • Questioned current onboarding email cadence.
  • Wants to revisit both this week.
Three states of the same note card. The button label and body slot swap; the rest of the layout is stable.

Under the hood: add a summary text column to notes, write a server function that loads the note (RLS ensures the caller owns it), calls the Gateway, and updates the row. The UI reads note.summary and renders it when present.

Add a summary column to the notes table. Create a summarizeNote({ noteId }) server function using the Lovable AI Gateway. Wire a Summarize button on each note card with loading and error states.

Part 5 · Prompting for reliable output

The model does what you ask. If you ask vaguely, you get vaguely. A prompt for an app feature has two parts: a system prompt (the role and rules, written once) and a user prompt (the actual content, different every call).

Figure 1.3
SYSTEMYou summarise notes. Reply with 3 bullet points.Each bullet ≤ 20 words. No preamble. No emoji.USERLong walk after lunch. Ideas about the launch…model
System sets the shape; user provides the content. Both are sent on every call — the model doesn't remember between requests.
Avoid
Sloppy prompt
"summarize this: {note}"
Response: a two-paragraph essay that starts with "Sure! Here's a summary…" and buries the point.
Do
Tight prompt
System: 3 bullets, ≤20 words each, no preamble. User: the note.
Response: three crisp bullets you can render directly.
  • Name the role. "You summarise notes" beats "please help." The model leans into the role.
  • Pin the shape. Bullets? Word count? JSON? Say it in the system prompt, not the user prompt.
  • Ban preambles. "No preamble. No 'Sure!'." Saves tokens and looks better in the UI.
  • Give one example if the shape is unusual. One example is worth a paragraph of instructions.

Part 6 · Traps & debugging

Figure 1.4
No summary in the UI
├── did the server fn run? → no → button not wired / auth failed
├── did the Gateway respond 2xx? → no → 429 / 402 / bad model id
├── was the response non-empty? → no → prompt too strict / token cap
├── did the UPDATE land on the row? → no → RLS or wrong noteId
└── is the UI reading the right field? → no → stale query cache
Summary didn't appear? Walk the same five-step path from Part 2 and find where it fell off.
  • Rate limit (429) — you hit the model's per-minute cap. Back off and retry; for spikey UIs, debounce the button.
  • Out of credits (402) — top up in Workspace → Usage. Surface a clear message to the user, don't fail silently.
  • Empty response — usually a prompt so strict the model gave up. Loosen one constraint at a time until output returns.
  • Length cutoff — long notes can blow past the output token budget. Cap the input, or ask for a shorter summary.
  • Hallucinations on tiny inputs — a two-word note gives the model nothing to summarise. Guard: skip the call under N characters.
  • Streaming vs single — for a button-and-wait feature, a single response is simpler. Stream when the user is watching the text appear.
  • Cost creep — every click costs a fraction of a cent. Cache the summary on the row (that's why we saved it) so a second view is free.

AI cheat sheet

Quick prompts

  • "Add a summary column to notes" → migration, one column.
  • "Create a summarizeNote server function using the AI Gateway" → server fn wired to LOVABLE_API_KEY.
  • "3 bullets, ≤20 words each, no preamble" → system prompt that produces clean UI output.
  • "Skip the call if the note body is under 40 characters" → cheap guard against hallucinations.

Model decision rules

  • Default: small/fast model (Flash-class). Ship on this.
  • Upgrade only when you can point at a real output that's worse than you need.
  • Compare by A/B on the same input — swap the model string, keep everything else identical.

Debug checklist

  • Button click does nothing → check the server fn was actually called (network tab, server logs).
  • 4xx from Gateway → wrong model id, missing key, or you're calling from the client instead of a server fn.
  • Summary is empty string → prompt too restrictive, or the model refused; log the raw response.
  • Summary saved but UI stale → invalidate the notes query after the mutation resolves.
Next up
Lesson 15 turns a one-shot summary into a full chatbot: streaming responses, conversation memory, and system prompts that give your assistant a real personality — all still through the same Gateway.