Materi pendamping video. Targetnya satu respons model nyata melalui chat Toko Nusa, dengan API key tetap di server. UI, endpoint POST /api/chat, dan respons demo sudah tersedia di starter.
Mulai dari branch starter
Clone repository latihan pada branch eps-01-starter. Gunakan Node.js 22.12+.
git clone --branch eps-01-starter --single-branch https://github.com/mujibur-rohman/ai-engineer-javascript.git
cd ai-engineer-javascript
npm ci
npm run dev
Buka http://localhost:5173. Balasan awal berlabel DEMO LOKAL · BUKAN AI karena server/ai.js masih menyusun teks sendiri. Kontrak endpoint sudah { message } masuk dan { reply, source } keluar. Kita hanya mengganti fungsi generateReply(message).
Hubungkan model di server
Hentikan server, lalu pasang Google Gen AI SDK dengan versi tetap:
npm install --save-exact @google/genai@2.27.0
cp -n .env.example .env
Isi .env di folder hasil clone:
GEMINI_API_KEY=GANTI_DENGAN_KEY_MILIKMU
GEMINI_MODEL=gemini-3.5-flash-lite
Buat key di Google AI Studio. Key dibaca oleh proses Node melalui process.env, bukan dikirim ke React. Jangan memakai awalan VITE_ untuk key karena variabel tersebut dapat terekspos ke frontend (dokumentasi Vite). Script server starter sudah memuat .env; restart server setelah mengubahnya.
.env.example pada branch starter masih memakai gemini-2.5-flash. Saat materi ini diperiksa pada 7 Oktober 2026, daftar model Google menyebut akses keluarga 2.5 dibatasi untuk pengguna lama dan menyarankan model baru bagi proyek baru. Karena itu contoh di atas memakai gemini-3.5-flash-lite. Ketersediaan dan kuota tetap bergantung pada akunmu.
Ganti seluruh isi server/ai.js dengan kode ini. Komentar hanya menandai bagian yang menentukan perilaku integrasi AI:
import { GoogleGenAI } from "@google/genai";
export async function generateReply(message) {
// Konfigurasi wajib ada sebelum request ke provider dibuat.
const apiKey = process.env.GEMINI_API_KEY?.trim();
const model = process.env.GEMINI_MODEL?.trim();
if (!apiKey) throw new Error("GEMINI_API_KEY belum diisi di server.");
if (!model) throw new Error("GEMINI_MODEL belum diisi di server.");
const ai = new GoogleGenAI({ apiKey });
const response = await ai.models.generateContent({
model,
contents: message, // Episode ini mengirim satu pesan terbaru.
config: {
httpOptions: {
timeout: 20000, // Batas request provider: 20 detik.
retryOptions: { attempts: 1 }, // Tidak ada retry otomatis.
},
},
});
// Respons HTTP sukses belum tentu membawa teks yang bisa ditampilkan.
const reply = response.text?.trim();
if (!reply) throw new Error("Model tidak mengembalikan teks jawaban.");
return { reply, source: "ai" };
}
models.generateContent mengirim message ke model yang dipilih (referensi SDK). response.text adalah getter teks dari kandidat pertama; nilainya bisa tidak tersedia (referensi respons). Karena itu adapter menolak hasil kosong sebelum memberi label source: "ai".
Timeout SDK dipasang 20 detik, lebih pendek dari timeout UI starter 30 detik. attempts: 1 berarti satu percobaan total, sehingga hasil demo tidak tertunda retry otomatis (opsi HTTP, opsi retry). Jika SDK melempar error, server/app.js menangkapnya dan mengirim HTTP 502 dengan pesan umum. Pemetaan error yang lebih rinci dibahas pada episode 6.
Setelah integrasi, perbarui teks pemberitahuan di client/src/App.jsx yang masih menyebut respons demo lokal. Label pada gelembung pesan sudah mengikuti source dari server.
Buktikan jalurnya
Restart dengan npm run dev, lalu kirim pertanyaan umum seperti “Jelaskan apa itu API dalam satu kalimat.” Di Network tab browser, POST /api/chat harus menghasilkan status 200, reply terisi, dan source: "ai". UI menampilkan label MODEL AI. Isi jawaban bisa berbeda pada tiap panggilan; contoh ini belum menguji akurasi pengetahuan toko.
Uji jalur gagal dengan mengosongkan GEMINI_API_KEY di .env, restart, lalu kirim pertanyaan lagi. Endpoint seharusnya mengembalikan 502 dan UI menampilkan error tanpa jawaban baru berlabel MODEL AI. Jika key pernah diatur langsung di terminal, hapus variabel itu juga karena dapat mengalahkan nilai dari .env. Isi kembali key setelah uji.
Adapter ini sudah diperiksa dengan SDK versi di atas dan respons provider simulasi untuk jalur sukses, key kosong, output kosong, error provider, dan timeout. Panggilan Gemini sungguhan memerlukan key serta kuota akunmu.
Batas episode ini
Setiap request hanya mengirim contents: message; riwayat yang tampak di UI belum diteruskan ke model. Model juga belum menerima data produk, pesanan, atau kebijakan Toko Nusa. Persona customer support masuk episode 2, riwayat percakapan episode 4, dan pengetahuan bersumber mulai level 2.