Building AI SaaS Product — AI Web

Membangun Produk AI SaaS Memadukan semua konsep menjadi produk yang bisa dijual. Architecture, pricing, scaling, dan bisnis model AI SaaS. Architecture…

AI SaaS adalah produk AI yang dijual sebagai layanan langganan ke banyak pelanggan sekaligus. Tiap pelanggan yang membayar disebut tenant. Di pelajaran ini Asisten Toko Buku dijual ke banyak toko: Toko Buku Pelangi di Bandung, Toko Buku Senja di Yogyakarta, dan seterusnya. Bayangin pengelola gedung apartemen. Satu gedung, banyak unit. Kunci unit A gak bisa membuka unit B. Tiap unit punya meteran listrik sendiri, jadi tagihannya sesuai pemakaian. Pengelola membayar listrik ke PLN, lalu menagih penghuni dengan harga yang sudah termasuk biaya pengelolaan. Dan daya tiap unit dibatasi, supaya satu unit gak membuat sekring seluruh gedung turun.

🧪 Tebakan cepat: Toko Buku Senja gak menjual Laskar Pelangi; Toko Buku Pelangi punya 12. Pelanggan Senja bertanya "Ada Laskar Pelangi?", dan query katalognya lupa WHERE toko_id = ?. Apa jawaban asistennya? Tebak dulu, jawabannya ada di bagian "Satu aplikasi, banyak toko: pisahkan datanya".

Satu aplikasi, banyak toko: pisahkan datanya

Multi-tenant artinya satu aplikasi dan satu database melayani banyak tenant, dan tiap baris data tahu milik siapa. Setiap tabel punya kolom toko_id, dan toko_id itu diambil dari API key yang sudah diverifikasi, bukan dari body request yang bisa diisi siapa saja.

// req.toko diisi middleware dari API key (bagian berikutnya)
const stok = await db.query(
  "SELECT judul, harga, stok FROM buku WHERE toko_id = $1 AND judul ILIKE $2",
  [req.toko.id, `%${judul}%`],
);
const mirip = await db.query(
  "SELECT judul, sinopsis FROM buku WHERE toko_id = $1 ORDER BY embedding <=> $2 LIMIT 5",
  [req.toko.id, vektorPertanyaan],
);
const kunciCache = `toko:${req.toko.id}:v${versiStok}:${normalkan(pertanyaan)}`;

Tanpa filter, asisten menjawab "Ada! Stok Laskar Pelangi 12, Rp89.000". Itu stok Toko Pelangi: jawabannya salah untuk pelanggan Senja, dan data toko lain bocor. Dengan filter: "Laskar Pelangi belum ada di katalog Toko Buku Senja." Itu jawaban tebakannya. Tambahkan kasus ini ke eval set supaya kebocoran ketahuan sebelum rilis.

API key per toko, disimpan sebagai hash

Tiap toko memanggil API-mu dengan kuncinya sendiri. Kunci OpenAI milikmu tetap satu, di .env server, dan gak pernah dilihat toko mana pun.

import { randomBytes, createHash } from "node:crypto";
const sha256 = (s) => createHash("sha256").update(s).digest("hex");

async function buatKunci(tokoId) {
  const kunci = "tb_live_" + randomBytes(24).toString("base64url"); // 32 karakter acak
  await db.insert("kunci_api", { toko_id: tokoId, prefix: kunci.slice(0, 12), hash: sha256(kunci), dicabut: false });
  return kunci; // ditampilkan SEKALI ke pemilik toko, lalu dilupakan
}

async function tokoDariKunci(req) {
  const kunci = (req.headers.authorization ?? "").replace("Bearer ", "");
  const baris = await db.first("SELECT toko_id FROM kunci_api WHERE hash = $1 AND dicabut = false", [sha256(kunci)]);
  return baris?.toko_id ?? null; // null: balas 401, model gak dipanggil
}

Streaming lewat backend-mu, sambil mencatat meteran

Browser pelanggan gak pernah bicara langsung ke OpenAI. Server meneruskan stream seperti di pelajaran Streaming Responses, dan event terakhir, response.completed, membawa usage yang jadi angka meteran:

await bacaSSE(upstream.body, async (event) => {
  if (event.type === "response.output_text.delta") res.write(`data: ${JSON.stringify({ teks: event.delta })}\n\n`);
  if (event.type === "response.completed") {
    const u = event.response.usage;
    await db.insert("pemakaian", {
      toko_id: req.toko.id, model: event.response.model, jenis: jenisPertanyaan,
      input_tokens: u.input_tokens, cached_tokens: u.input_tokens_details.cached_tokens, output_tokens: u.output_tokens,
    });
  }
});

Biaya dihitung dari tabel pemakaian dan daftar harga. Stream yang putus sebelum response.completed gak membawa usage, jadi cocokkan meteranmu dengan dasbor usage provider tiap bulan.

Token jadi biaya, biaya jadi harga

Asumsi dari pelajaran Cost Optimization: tiap pertanyaan 1.200 token input (1.100 dari prefix yang ter-cache), output 40 token untuk stok dan 200 untuk rekomendasi:

Meteran bulan Oktober, dengan kurs contoh Rp16.500 per dolar:

Toko Stok Rekomendasi Biaya AI
Pelangi 4.200 300 $0,87 = Rp14.276
Senja (klub buku) 1.500 2.900 $6,76 = Rp111.548

Paket Toko dengan harga rata, Rp149.000 untuk 5.000 pertanyaan apa saja: margin Pelangi 90,4%, Senja cuma 25,1%. Kalau satu toko memakai 5.000 rekomendasi, biayanya Rp190.575: rugi. Inilah tantangan bisnis terbesar produk AI SaaS: mengelola biaya AI per pengguna sambil menjaga margin, karena biayanya ikut naik dengan pemakaian, gak seperti SaaS biasa yang biaya per pengguna hampir tetap. (Margin = (pendapatan - biaya AI) / pendapatan; server, payment gateway, dan pajak belum dihitung.)

Jalan keluarnya pricing usage-based dengan tier dan sistem kredit, model yang paling umum untuk SaaS AI: stok = 1 kredit, rekomendasi = 50 kredit (mengikuti perbandingan biayanya). Paket Toko Rp149.000 berisi 50.000 kredit, lebihnya Rp3 per kredit. Biaya per kredit paling mahal Rp0,76, jadi tiap kredit yang terjual punya margin minimal 74%. Senja memakai 146.500 kredit: Rp149.000 + 96.500 × Rp3 = Rp438.500, margin 74,6%. Pelangi tetap Rp149.000 (19.200 kredit).

Observability: dasbor biaya per toko dan regresi eval

Hukum dan kepercayaan: dasar yang wajib

Checklist sebelum launch

⚠️ Jebakan yang sering ketemu

TL;DR: AI SaaS menjual asisten ke banyak tenant. Pisahkan data dengan toko_id dari API key yang terverifikasi, di SQL, vector search, dan kunci cache; tanpa itu pelanggan Senja dapat stok Toko Pelangi. API key per toko dibuat acak (tb_live_ + 24 byte), ditampilkan sekali, disimpan sebagai hash SHA-256; kunci provider tetap satu di server. Stream lewat backend-mu dan catat usage dari response.completed per toko. Biaya per pertanyaan: stok $0,000041, rekomendasi $0,00231 (56 kali). Tantangan bisnis terbesarnya mengelola biaya AI per pengguna sambil menjaga margin: harga rata membuat Senja cuma 25,1%, sedangkan usage-based dengan tier dan kredit (stok 1, rekomendasi 50, Rp149.000 untuk 50.000 kredit, lebihnya Rp3) menjaga margin di atas 74%. Free tier dibatasi per toko. Pantau biaya dan eval per toko, pakai store: false, baca DPA provider, patuhi UU PDP, dan beri tahu pengguna bahwa mereka bicara dengan AI.

Yang akan kamu pelajari