Since I last wrote Building AI-powered e-commerce applications using Angular & Firebase AI Logic (formerly Vertex AI in Firebase) in 2025, so much has changed that this article may look like a complete rewrite.
Firebase AI Logic is the successor to Vertex AI in Firebase (May 2025). This post covers the Angular setup only. For the full rename, new APIs, and migration from firebase/vertexai, see Vertex AI in Firebase is now Firebase AI Logic — What Actually Changed.
Official Firebase docs cover each piece, but the order matters. This guide follows the sequence that actually works. We use the modular firebase JS SDK + Angular DI, so this works on Angular 18+.
Why Firebase AI Logic in web apps
Most Gemini tutorials show either AI Studio + a raw API key or a custom Node/Python proxy you host and secure yourself. Firebase AI Logic sits in between: Gemini runs from your Angular app in the browser, but requests go through Firebase's managed path and not a DIY backend you maintain.
What you get on web:
| Benefit | Why it matters |
|---|---|
| No custom AI backend | Call generateContent() from a service; skip building Express/Cloud Functions just to hide an API key |
| App Check attestation | Proves traffic comes from your real app (reCAPTCHA Enterprise in prod, debug tokens on localhost) — required for production AI Logic on web |
| One Firebase project | Same Console for Hosting, Auth, Firestore, Remote Config, and AI monitoring |
| Gemini Developer API path |
GoogleAIBackend(): fast onboarding, Spark plan for prototypes, Console wizard provisions APIs for you |
| Upgrade path | Switch to AgentPlatformBackend() later if you need enterprise Vertex/Agent Platform features; often a one-line backend change |
Good fit for web apps:
- Shopping assistants, support chat, on-page copilots
- Multi-turn chat and function calling (tools that call your existing Angular services)
- Features where latency matters; inference starts from the client without an extra hop through your server
When to use something else:
- Sensitive prompts or secrets: keep those on the server (Genkit, Cloud Functions)
- Heavy RAG over private corpora: server-side retrieval is usually safer and more flexible
- Billing/abuse control at the edge: combine App Check enforcement with Firebase Console monitoring; add server gates for high-risk actions
For ByteWise, the agent reads inventory and updates the cart via function calling; still client-side AI Logic, with business logic in Angular services. This guide stops at your first generateContent(); the ByteWise repo goes further.
What you are building
Firebase Console (AI Logic + App Check + API keys)
↓
firebase.config.ts ← credentials
firebase-ai.ts ← initializeApp + App Check + getAI (providers)
app.config.ts ← register providers once
ai.service.ts ← inject FIREBASE_AI → generateContent()
You only create two new Firebase-specific files beyond a standard Angular app:
| File | Purpose |
|---|---|
src/environments/firebase.config.ts |
firebaseConfig + reCAPTCHA site key |
src/app/firebase/firebase-ai.ts |
Bootstrap: tokens, App Check, provideFirebaseAI()
|
Everything else is small edits to files you already have (app.config.ts, index.html, one service).
Prerequisites
- Angular 18+ (standalone,
ApplicationConfigstyle) - Node.js LTS
- A Google account and a Firebase project
-
firebase>= 12.19.0 (required for current Gemini + App Check behaviour)
npm install firebase@^12.19.0
Part 1: Firebase Console (do this before writing any code)
1. Create a project and web app
- Firebase Console → Create a new Firebase project
-
Add app → Web (
</>) - Copy the
firebaseConfigobject — you will paste it in Part 2
2. Enable Firebase AI Logic (early)
- AI Services → AI Logic → Get started
- Choose Gemini Developer API and finish the setup wizard. Ensure to enable the APIs recommended and AI monitoring.
Wait 5–10 minutes, then in Google Cloud console (same project ID as Firebase): APIs & Services → Enabled APIs & services → scroll past the charts to the API table → use Filter to confirm Firebase AI Logic API and Gemini API are listed. Firebase's wizard labels the second one Gemini Developer API; GCP now shows Gemini API (generativelanguage.googleapis.com). You may also see Gemini for Google Cloud API when filtering Gemini — that is for Gemini inside the Cloud Console and is not required for Firebase AI Logic in your Angular app.
Skipping this step is the most common cause of 403 "The caller does not have permission" later.
3. reCAPTCHA Enterprise + App Check
3a. Create a reCAPTCHA Enterprise key
- Google Cloud (same project) → Security → Fraud Defense → Enable reCAPTCHA Enterprise API
- Select Keys on the tab → Create key → Input any Display name of your choice → Website
-
Domains: add domains that production only. Do not add
localhost. For example:<project-id>.web.app<project-id>.firebaseapp.com- Any custom domain you use later
- Click on Create Key
- After creation, copy the site key. This key starts with something like
6L...
3b. Register App Check
- Firebase Console → Security → App Check → Click on the Apps tab → Click on your web app → reCAPTCHA Enterprise → paste site key (from Step 3a). You can leave the default configurations as they are.
-
APIs tab → Firebase AI Logic. You will find Monitoring and Baseline Protection have already been enabled (
Basic - Enforced). However, you can set up additional checks if you want such as Replay Protection.
4. Browser API key restrictions
- Google Cloud → APIs & Services → Credentials
- Open the Browser key matching your
firebaseConfig.apiKey - API restrictions: You will find about 25 APIs already allowed on that project and ensure you allow Firebase AI Logic API and Firebase App Check API if not already allowed.
-
Application restrictions: None while developing on
localhost
Part 2: Angular configuration
Step 1: Generate Angular environment variables files
You can generate your Angular environment files using the command:
ng generate environments
Step 2: src/environments/firebase.config.ts
Inside the environment folder, create a file with the name firebase.config.ts and copy the code snippet below. Replace the YOUR_ values with the actual values from your Firebase project Settings (scroll all the way down to the SDK setup and configuration section).
The recaptchaEnterpriseSiteKey is the same as the one in Part 1: Step 3a: Google Cloud (same project) → Security → Fraud Defense → Keys → Copy the already created key.
import type { FirebaseOptions } from 'firebase/app';
export const firebaseConfig: FirebaseOptions = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_PROJECT.firebaseapp.com',
projectId: 'YOUR_PROJECT_ID',
storageBucket: 'YOUR_PROJECT.firebasestorage.app',
messagingSenderId: 'YOUR_SENDER_ID',
appId: 'YOUR_APP_ID',
};
export const recaptchaEnterpriseSiteKey = 'YOUR_RECAPTCHA_SITE_KEY';
Step 3: src/environments/environment.model.ts
Create environment.model.ts — one shared type for production and development. appCheckDebugToken is optional so production omits it, while firebase-ai.ts can still reference environment.appCheckDebugToken without IDE TS2339 errors (your editor type-checks environment.ts, not the dev file Angular swaps in at build time).
import type { FirebaseOptions } from 'firebase/app';
export interface AppEnvironment {
production: boolean;
firebaseConfig: FirebaseOptions;
recaptchaEnterpriseSiteKey: string;
/** Dev/CI only — set in `environment.development.ts`, omit in `environment.ts` */
appCheckDebugToken?: boolean | string;
}
Step 4: environment.ts and environment.development.ts
src/environments/environment.ts (production — used by ng build):
import { firebaseConfig, recaptchaEnterpriseSiteKey } from './firebase.config';
import type { AppEnvironment } from './environment.model';
export const environment: AppEnvironment = {
production: true,
firebaseConfig,
recaptchaEnterpriseSiteKey,
};
src/environments/environment.development.ts (local dev — swapped in by ng serve):
import { firebaseConfig, recaptchaEnterpriseSiteKey } from './firebase.config';
import type { AppEnvironment } from './environment.model';
export const environment: AppEnvironment = {
production: false,
firebaseConfig,
recaptchaEnterpriseSiteKey,
appCheckDebugToken: true,
};
Confirm angular.json has fileReplacements under the development build configuration ( ng generate environments usually adds this):
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
Always import the alias path in app code — never environment.development directly:
import { environment } from '../../environments/environment';
NOTE: On
ng serve, the CLI substitutesenvironment.development.tswherever you importenvironment.ts. Production builds use the realenvironment.tswith no debug token.
Step 5: src/app/firebase/firebase-ai.ts
Create a file with the name firebase-ai.ts under a folder firebase.
import {
inject,
InjectionToken,
makeEnvironmentProviders,
PLATFORM_ID,
provideAppInitializer,
type EnvironmentProviders,
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { getAI, GoogleAIBackend, type AI } from 'firebase/ai';
import { initializeApp, type FirebaseApp } from 'firebase/app';
import {
CustomProvider,
initializeAppCheck,
ReCaptchaEnterpriseProvider,
type AppCheck,
} from 'firebase/app-check';
import { environment } from '../../environments/environment';
export const FIREBASE_AI = new InjectionToken<AI>('FIREBASE_AI');
const FIREBASE_APP_CHECK = new InjectionToken<AppCheck>('FIREBASE_APP_CHECK');
const firebaseApp = initializeApp(environment.firebaseConfig);
function initAppCheck(firebaseApp: FirebaseApp, platformId: object): AppCheck {
const isBrowser = isPlatformBrowser(platformId);
if (isBrowser && !environment.production) {
(globalThis as typeof globalThis & { FIREBASE_APPCHECK_DEBUG_TOKEN?: boolean | string })
.FIREBASE_APPCHECK_DEBUG_TOKEN ??= environment.appCheckDebugToken ?? true;
}
if (!isBrowser) {
return initializeAppCheck(firebaseApp, {
provider: new CustomProvider({
getToken: async () => ({
token: 'ssr-placeholder',
expireTimeMillis: Date.now() + 60 * 60 * 1000,
}),
}),
isTokenAutoRefreshEnabled: false,
});
}
return initializeAppCheck(firebaseApp, {
provider: new ReCaptchaEnterpriseProvider(environment.recaptchaEnterpriseSiteKey),
isTokenAutoRefreshEnabled: true,
});
}
export function provideFirebaseAI(): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: FIREBASE_APP_CHECK,
useFactory: (platformId: object) => initAppCheck(firebaseApp, platformId),
deps: [PLATFORM_ID],
},
{
provide: FIREBASE_AI,
useFactory: (_appCheck: AppCheck): AI =>
getAI(firebaseApp, {
backend: new GoogleAIBackend(),
useLimitedUseAppCheckTokens: environment.production,
}),
deps: [FIREBASE_APP_CHECK],
},
// Eager-init App Check on startup — prints the debug token in DevTools
// immediately so you can register it in Firebase Console before Part 3.
provideAppInitializer(() => {
inject(FIREBASE_APP_CHECK);
}),
]);
}
Step 6: src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideFirebaseAI } from './firebase/firebase-ai';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideFirebaseAI(),
],
};
Step 7: Localhost debug token — src/index.html
Add this inside the <body> element, right before <app-root>.
<script>
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
</script>
Step 8: Run locally and capture the debug token
ng serve
Open http://localhost:4200 (the URL the Angular CLI prints in the terminal).
Where the token appears: the Firebase SDK logs it when initializeAppCheck() runs with the debug provider active. Open DevTools → Console:
Search/filter for AppCheck debug token or debug token
You should see a line similar to:
Firebase App Check debug token: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Copy the UUID (without quotes), then register it:
Firebase Console → App Check → Apps → your web app → ⋮ menu → Manage debug tokens → Add debug token → Give the debug token any name of your choice → paste → save → hard-refresh the app (Ctrl+Shift+R).
NOTE: The debug token is not in
environment.ts. It's minted by the SDK and printed in the Console once App Check initializes on the client. The debug token is ONLY for use during development. Production must use reCAPTCHA, not debug tokens.
Part 3: Your first generateContent
Step 1: src/app/services/ai.service.ts
import { inject, Injectable } from '@angular/core';
import { getGenerativeModel } from 'firebase/ai';
import { FIREBASE_AI } from '../firebase/firebase-ai';
@Injectable({ providedIn: 'root' })
export class AiService {
private readonly ai = inject(FIREBASE_AI);
async ask(prompt: string): Promise<string> {
const model = getGenerativeModel(this.ai, { model: 'gemini-3.5-flash' });
const result = await model.generateContent(prompt);
return result.response.text();
}
}
Step 2: Call it from a component
Generate a dedicated AI Demo component with the Angular CLI, or wire the call into your root App component.
ng generate component ai-demo --inline-template --skip-tests
import { Component, inject, signal } from '@angular/core';
import { AiService } from '../services/ai.service';
@Component({
selector: 'app-ai-demo',
template: `
<button (click)="run()" [disabled]="loading()">
{{ loading() ? 'Please wait…' : 'Ask Gemini' }}
</button>
@if (error()) {
<p role="alert">{{ error() }}</p>
}
@if (reply()) {
<pre>{{ reply() }}</pre>
}
`,
styleUrl: './ai-demo.scss',
})
export class AiDemo {
private readonly ai = inject(AiService);
readonly loading = signal(false);
readonly reply = signal('');
readonly error = signal('');
async run(): Promise<void> {
this.loading.set(true);
this.error.set('');
this.reply.set('');
try {
this.reply.set(await this.ai.ask('Say hello in one sentence.'));
} catch (err) {
this.error.set(
err instanceof Error ? err.message : 'Something went wrong. Check the console.',
);
} finally {
this.loading.set(false);
}
}
}
Render it from src/app/app.html:
<app-ai-demo />
Import AiDemo in src/app/app.ts — Angular components are standalone by default and must be listed in the parent imports array:
import { Component } from '@angular/core';
import { AiDemo } from './ai-demo/ai-demo';
@Component({
selector: 'app-root',
imports: [AiDemo],
templateUrl: './app.html',
styleUrl: './app.scss',
})
export class App {}
Without that import, <app-ai-demo /> will fail at compile time with an unknown element error.
Run ng serve and click on the Ask Gemini button in the rendered UI on http://localhost:4200
Part 4: Verify it worked
Click Ask Gemini. If you get text back — not a 403 — your setup works. That is the pass/fail check.
On first load, the console should show no App Check token fetch failed, and you should have registered the debug token from Part 2, Step 8. Optional: in DevTools → Network, filter firebasevertexai and confirm a POST to :generateContent returns 200. Do not worry if you cannot spot X-Firebase-AppCheck — the header is easy to miss, and Gemini succeeding is what matters.
403 The caller does not have permission? Re-run AI Logic → Get started, confirm the debug token is registered, confirm the Browser key allowlist includes Firebase AI Logic API + Firebase App Check API, then wait a few minutes and hard-refresh.
Part 5: What you can add next
| Feature | SDK entry point |
|---|---|
| Multi-turn chat |
startChat() on a generative model |
| Function calling |
tools + FunctionCallingConfig on the model |
| Streaming | generateContentStream() |
| System instructions |
systemInstruction in model config |
For a full function-calling example, see ByteWise ai.service.ts.
Conclusion
We are at an inflection point where developers can build intelligent web experiences, powered by Large Language Models. Firebase AI Logic provides this infrastructure with a free tier on the Gemini Developer API and Agent Platform Gemini API for enterprise-level use cases.
If you want these capabilities where latency matters, Firebase AI Logic will be your best bet, while offering you security and abuse prevention for your production apps.
To see Firebase AI Logic in action in e-commerce applications, check out Bytewise Shop, a fictional tech gadget shop I built to demonstrate the use of tool calling on actions such as add to cart, check inventory, clearing cart, all using natural language.
File checklist
- [ ]
src/environments/firebase.config.ts— new - [ ]
src/environments/environment.model.ts— new - [ ]
src/environments/environment.ts— production values - [ ]
src/environments/environment.development.ts— addappCheckDebugToken - [ ]
src/app/firebase/firebase-ai.ts— new - [ ]
src/app/app.config.ts— addprovideFirebaseAI() - [ ]
src/index.html— localhost debug script - [ ]
src/app/services/ai.service.ts— new - [ ]
AiDemocomponent + import inapp.ts
Related links
- Vertex AI in Firebase is now Firebase AI Logic — What Actually Changed — companion article (rename, migration, what's new)
- Firebase AI Logic overview
- App Check with AI Logic (web)
Top comments (0)