---
title: "16 — Implementing AI — Three Providers, One Shape"
uid: implementing-ai
tags: ["integration", "anthropic", "openai", "ai", "gemini", "api", "roadmap:frontend"]
excerpt: "Every provider exposes the same shape — POST messages plus a model name, get tokens back. The real decisions: provider, model tier, and where the call runs."
date: 2026-08-12T18:35:09+0000
source: https://www.aveshina.my.id/en/blog/implementing-ai
---

"Implementing AI" sounded enormous until I noticed every provider does the same thing. But before any of it, three words need pinning down, because the whole topic is built from them.

A **model provider** is a company that runs an AI model on its own servers and lets you borrow it over the internet. Anthropic (models called Claude), OpenAI (GPT), and Google (Gemini) are the three big ones. An **API** is just a door for that borrowing: a standard way for your program to send a request to their server and get an answer back. And an **API key** is the password that proves it's you — and, just as importantly, tells them whose account to bill.

So "implementing AI" collapses to one sentence: **call a provider's API from your app to power a feature** — smart form validation, a chatbot, recommendations. You send a request, you get text back, and that text becomes your feature. All three providers expose the same shape, which leaves three real decisions: **which provider, which model tier, and where the call runs.**

Here's the whole picture before I walk through it — your app, one request, three interchangeable doors:

```figure
<svg viewBox="0 0 740 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="One app sends the same shaped request to three interchangeable provider doors. Left box is Your app, sending one request bubble: POST messages plus a model name. Three arrows fan right to three doors: Anthropic (Claude, violet), OpenAI (GPT, green), Google (Gemini, blue). Each door returns the same shaped bubble: tokens back. A dashed note reads same shape, different provider.">
  <defs>
    <marker id="aiarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <!-- Your app -->
    <rect x="40" y="100" width="170" height="80" rx="10" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="125" y="130" font-size="14" font-weight="700" fill="#1e1b4b" text-anchor="middle">Your app</text>
    <text x="125" y="150" font-size="10.5" fill="#475569" text-anchor="middle">POST messages + model</text>
    <text x="125" y="166" font-size="10.5" fill="#475569" text-anchor="middle">→ tokens back</text>

    <!-- Three provider doors -->
    <rect x="500" y="36" width="200" height="56" rx="8" fill="#ede9fe" stroke="#7c3aed" stroke-width="1.5"/>
    <text x="600" y="60" font-size="13" font-weight="700" fill="#3b0764" text-anchor="middle">Anthropic — Claude</text>
    <text x="600" y="78" font-size="10" fill="#475569" text-anchor="middle">safety-focused · Constitutional AI</text>

    <rect x="500" y="112" width="200" height="56" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="600" y="136" font-size="13" font-weight="700" fill="#052e16" text-anchor="middle">OpenAI — GPT family</text>
    <text x="600" y="154" font-size="10" fill="#475569" text-anchor="middle">broad ecosystem · most SDKs</text>

    <rect x="500" y="188" width="200" height="56" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5"/>
    <text x="600" y="212" font-size="13" font-weight="700" fill="#0c1a3d" text-anchor="middle">Google — Gemini</text>
    <text x="600" y="230" font-size="10" fill="#475569" text-anchor="middle">multimodal · text, image, audio</text>

    <!-- arrows from app to each door -->
    <path d="M210,125 C350,125 400,64 498,64" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#aiarrow)"/>
    <path d="M210,140 L498,140" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#aiarrow)"/>
    <path d="M210,155 C350,155 400,216 498,216" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#aiarrow)"/>

    <text x="354" y="258" font-size="11" fill="#64748b" text-anchor="middle" font-style="italic">same shape — different provider, model tier, running location</text>
  </g>
</svg>
```

## The common shape, before the providers

The part I had to see clearly is that there isn't really an "AI integration skill" separate from "calling an HTTP API" [1]. Whatever the provider, the request is the same three pieces:

- **A messages array** — the conversation, written out as a list. It starts with a *system message* (the standing instructions, "you're a helpful assistant…"), then the back-and-forth of *user* and *assistant* turns. It's just the chat history, spelled out.
- **A model name** — a string saying which AI to use, like gpt-4o or a Claude model.
- **A couple of knobs** — like *temperature* (a dial controlling how adventurous the guessing is) and *max tokens* (a cap on how long the reply can be).

The response is the text the model generated, made of **tokens** — small chunks of text the model reads and writes, which is also the unit you're billed by. The reply comes back one of two ways: all at once (a *completion*), or piece by piece as it's produced (a *stream*). Streaming is what makes a chatbot feel live instead of making the user stare at a spinner for eight seconds.

That's the whole exchange. Each provider ships a **SDK** — a small helper library (Anthropic's @anthropic-ai/sdk, OpenAI's openai, Google's @google/genai) so you don't hand-write the request yourself — but the SDKs are thin convenience wrappers around that same exchange [2][3][4]. Knowing the shape is 80% of "implementing AI." The remaining 20% is three decisions, and the first one is which door to knock on.

## The three providers, in one line each

The three doors differ in flavor, not in shape. I don't need a deep dive on each — one line apiece covers the decision:

- **Anthropic (Claude)** — the safety-focused one, built around "Constitutional AI" (training guided by an explicit set of principles); my pick for careful long-context reading and reasoning [5].
- **OpenAI (GPT)** — the broad default; most tutorials, SDKs, and community examples assume it [3][6].
- **Google (Gemini)** — the multimodal one: "multimodal" means the model accepts more than text, so it takes images, audio, and video as one input stream [7].

You send the same shape at every door. The flavor only matters when a feature leans on a specific strength — and the one that shows up most often in practice is multimodality: if the feature reads an image or an audio clip, Gemini is the natural first choice.

## Picking a model tier within a provider

Each provider isn't one model — it's a ladder, and a **tier** is one rung on that ladder. There's usually a **flagship** (best reasoning, highest cost, slowest), a **balanced** mid-tier, and a **mini/flash/haiku** tier (cheap, fast, good enough for most UI work). The mistake I made early was reaching for the flagship for everything. A chat widget that answers FAQs does not need it; a mini model is a tenth of the cost and feels faster to the user. The question to ask is "what's the cheapest tier that still does this task acceptably?" — and to re-run the answer whenever a provider ships a new tier.

## The decision that actually matters: where the call runs

This is the one I got wrong first, and the one I now think about before provider or model. There are two places the API call can live, and they are not equally safe:

- **Server-side** — code that runs on a computer you control (a route handler, a server action, an API route). The API key lives in an **environment variable** — a secret stored on the server, kept out of the code the browser receives. The browser never sees it. This is the correct default [1][2].
- **Client-side** — code running in the user's browser. Any key shipped to the browser is retrievable by anyone who opens devtools (the browser's built-in inspector). For a public site this is a leaked key waiting to happen.

So the rule I hold to: the provider key never reaches the client. The browser calls my own endpoint; my endpoint calls the provider with the secret; the response (or stream) flows back. The provider choice is almost secondary to getting this one right, because a leaked key is a bill with my name on it regardless of which provider it belongs to.

The shape of a correct integration, then, is two hops:

```figure
<svg viewBox="0 0 700 200" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Two-hop request flow for key safety. Left: Browser sends a request to your own server route. Middle: Your server route holds the API key in an env var and calls the provider. Right: Provider returns tokens to the server, which streams them back to the browser. The API key never crosses into the browser.">
  <defs>
    <marker id="hop" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <!-- Browser -->
    <rect x="30" y="70" width="140" height="60" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="100" y="96" font-size="13" font-weight="700" fill="#422006" text-anchor="middle">Browser</text>
    <text x="100" y="114" font-size="10" fill="#475569" text-anchor="middle">no key, ever</text>

    <!-- Your server route -->
    <rect x="260" y="60" width="180" height="80" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="350" y="86" font-size="13" font-weight="700" fill="#052e16" text-anchor="middle">Your /api route</text>
    <text x="350" y="104" font-size="10" fill="#475569" text-anchor="middle">reads key from env</text>
    <text x="350" y="120" font-size="10" fill="#475569" text-anchor="middle">calls provider</text>

    <!-- Provider -->
    <rect x="530" y="70" width="140" height="60" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="600" y="96" font-size="13" font-weight="700" fill="#1e1b4b" text-anchor="middle">Provider</text>
    <text x="600" y="114" font-size="10" fill="#475569" text-anchor="middle">Anthropic / OpenAI / Gemini</text>

    <!-- hops -->
    <line x1="170" y1="92" x2="258" y2="92" stroke="#64748b" stroke-width="1.5" marker-end="url(#hop)"/>
    <text x="214" y="84" font-size="10" fill="#475569" text-anchor="middle">request</text>
    <line x1="258" y1="118" x2="170" y2="118" stroke="#64748b" stroke-width="1.5" marker-end="url(#hop)"/>
    <text x="214" y="132" font-size="10" fill="#475569" text-anchor="middle">stream / response</text>

    <line x1="440" y1="92" x2="528" y2="92" stroke="#64748b" stroke-width="1.5" marker-end="url(#hop)"/>
    <text x="484" y="84" font-size="10" fill="#475569" text-anchor="middle">+ key</text>
    <line x1="528" y1="118" x2="440" y2="118" stroke="#64748b" stroke-width="1.5" marker-end="url(#hop)"/>
    <text x="484" y="132" font-size="10" fill="#475569" text-anchor="middle">tokens</text>
  </g>
</svg>
```

## How I use this

The way of thinking leaves me with one triage whenever a feature needs AI. First, **where does the call run** — server, always, so the key is safe. Second, **which provider** — Anthropic for careful long-context reasoning, OpenAI for the broad default and easiest examples, Gemini for genuinely multimodal input. Third, **which tier** — start at the cheapest mini/flash/haiku that does the job, only climb when the output actually degrades. Get those three right and the integration itself is just the same shaped POST every time.

## References

[1] Anthropic, "Anthropic API overview," Anthropic Docs, 2024. [Online]. Available: [https://docs.anthropic.com/en/api/getting-started](https://docs.anthropic.com/en/api/getting-started)

[2] Anthropic, "Messages API," Anthropic Docs, 2024. [Online]. Available: [https://docs.anthropic.com/en/api/messages](https://docs.anthropic.com/en/api/messages)

[3] OpenAI, "API overview," OpenAI Platform Docs, 2024. [Online]. Available: [https://platform.openai.com/docs/overview](https://platform.openai.com/docs/overview)

[4] Google, "Gemini API overview," Google AI for Developers, 2024. [Online]. Available: [https://ai.google.dev/gemini-api/docs](https://ai.google.dev/gemini-api/docs)

[5] Anthropic, "Constitutional AI: Harmlessness from AI Feedback," Anthropic Research, 2022. [Online]. Available: [https://www.anthropic.com/research/constitutional-ai](https://www.anthropic.com/research/constitutional-ai)

[6] OpenAI, "Models," OpenAI Platform Docs, 2024. [Online]. Available: [https://platform.openai.com/docs/models](https://platform.openai.com/docs/models)

[7] Google, "Gemini — multimodal models," Google AI for Developers, 2024. [Online]. Available: [https://ai.google.dev/gemini-api/docs/models](https://ai.google.dev/gemini-api/docs/models)

```quiz
Q: "Implementing AI" in a frontend, at its core, is…
- training a model from scratch in the browser
- calling a model provider's API from your app to power a feature
- writing your own inference engine
correct: 1
explain: Every provider exposes an HTTP API: you POST messages plus a model name, you get tokens back. Implementing AI is integrating that exchange, not building or training the model.

Q: What do Anthropic, OpenAI, and Gemini's APIs have in common?
- Completely different request shapes; each is its own skill
- The same core shape — a messages array plus a model name, returning tokens
correct: 1
explain: The SDKs differ in details, but the exchange is identical across providers: messages + model in, streamed/completed tokens out. Once you know the shape, switching providers is a config change.

Q: Where should the provider API key live in a web app?
- In the browser, so the client can call the provider directly
- On the server, in an environment variable; the browser calls your own route
correct: 1
explain: Any key shipped to the browser is retrievable from devtools and effectively leaked. The safe pattern is a server-side route that holds the key and proxies the call.

Q: You're building a feature that reads an uploaded image and answers questions about it. Which provider's strength leans most naturally here?
- Google Gemini — multimodal input built in
- Any provider, identically — multimodal support is always equal
correct: 0
explain: Gemini is designed from the ground up as multimodal — text, image, audio, video in one context. Others can handle attached media through tooling, but Gemini treats it as a first-class input stream.

Q: When choosing a model tier within a provider, the default heuristic is…
- always use the flagship model for every feature
- start at the cheapest tier that does the job acceptably, climb only when output degrades
correct: 1
explain: Mini/flash/haiku tiers are a fraction of the cost and faster for UI work. Reserve the flagship for tasks that genuinely need its reasoning; most features don't.
```
