StokAPI — API Stok & Harga untuk Integrasi
API backend tanpa antarmuka yang berfungsi sebagai satu sumber kebenaran untuk data stok dan harga produk. Menyediakan endpoint untuk membaca daftar produk, memeriksa stok per SKU, memperbarui stok dari transaksi, dan mengirim webhook saat stok menipis atau harga berubah. Setiap aplikasi pemanggil diotentikasi dengan API key yang dapat dikelola dan memiliki batasan laju.
API Manajemen Stok & Harga Produk
API backend tanpa antarmuka yang berfungsi sebagai satu sumber kebenaran untuk data stok dan harga produk. Menyediakan endpoint untuk membaca daftar produk, memeriksa stok per SKU, memperbarui stok dari transaksi, dan mengirim webhook saat stok menipis atau harga berubah. Setiap aplikasi pemanggil diotentikasi dengan API key yang dapat dikelola dan memiliki batasan laju.
Problem
Pemilik usaha yang menjual produknya melalui berbagai kanal (aplikasi kasir, toko online, marketplace) sering mengalami selisih stok dan harga karena data yang tidak terpusat dan tidak sinkron secara real-time. Hal ini menyebabkan kerugian finansial dan pengalaman pelanggan yang buruk.
Goals
- Menyediakan satu sumber kebenaran data stok dan harga produk yang dapat diakses oleh berbagai aplikasi.
- Memastikan data stok diperbarui secara real-time dengan latensi P95 < 500ms untuk update stok.
- Mengurangi selisih stok antar kanal penjualan hingga 0% untuk produk yang dikelola via API.
- Memberikan notifikasi otomatis (webhook) saat stok produk menipis atau harga berubah.
- Memungkinkan pengelolaan API key dan batasan laju akses untuk setiap aplikasi klien.
Non-Goals
- Antarmuka pengguna (UI) untuk pengelolaan produk, stok, atau API key. Pengelolaan akan dilakukan via API atau langsung di database.
- Fitur manajemen inventaris yang kompleks seperti multi-lokasi gudang, transfer stok antar gudang, atau perhitungan biaya rata-rata.
- Integrasi langsung dengan platform e-commerce atau POS pihak ketiga.
- Fitur otentikasi pengguna berbasis email/password atau OAuth.
- Pelaporan atau analitik stok yang mendalam.
Target Users
- Pemilik Usaha — Memiliki data stok dan harga yang akurat dan terpusat untuk semua kanal penjualan.
- Developer Aplikasi Klien (Kasir/E-commerce) — API yang stabil, terdokumentasi dengan baik, dan mudah diintegrasikan untuk mendapatkan dan memperbarui data produk/stok.
User Stories
- must-haveSebagai Developer aplikasi kasir, saya ingin Mengambil daftar semua produk beserta stok dan harganya, agar Saya bisa menampilkan produk yang tersedia di aplikasi kasir..
- must-haveSebagai Developer aplikasi toko online, saya ingin Memperbarui jumlah stok suatu produk setelah ada transaksi penjualan, agar Stok di toko online selalu akurat dan mencegah penjualan produk yang sudah habis..
- must-haveSebagai Pemilik usaha, saya ingin Menerima notifikasi otomatis saat stok produk tertentu menipis, agar Saya bisa segera melakukan restock dan menghindari kehabisan barang..
- must-haveSebagai Developer aplikasi klien, saya ingin Menggunakan API key yang unik untuk aplikasi saya, agar Akses saya dapat diidentifikasi, dilacak, dan dibatasi lajunya..
User Flow
- Developer aplikasi klien mendapatkan API key dari pemilik usaha (secara manual atau via API key master).
- Developer mengintegrasikan API key ke aplikasi klien.
- Aplikasi klien memanggil endpoint GET /v1/products untuk menampilkan daftar produk.
- Saat transaksi penjualan terjadi, aplikasi klien memanggil PATCH /v1/products/:sku/stock untuk mengurangi stok.
- Jika stok produk menipis di bawah ambang batas, sistem mengirim webhook 'low_stock' ke URL yang terdaftar.
- Jika harga produk berubah, sistem mengirim webhook 'price_change' ke URL yang terdaftar.
Features
Manajemen Produk & Stok (API v1)
Menyediakan endpoint API untuk mengelola data produk (SKU, nama, harga, HPP) dan stok (kuantitas).
- API dapat mengembalikan daftar produk dengan paginasi, filter berdasarkan SKU atau nama, dan pengurutan.
- API dapat mengembalikan detail produk tunggal berdasarkan ID atau SKU.
- API dapat memperbarui stok produk berdasarkan SKU dengan operasi penambahan atau pengurangan.
- API dapat memperbarui harga jual dan HPP produk berdasarkan SKU.
- Setiap produk memiliki SKU unik yang tidak boleh duplikat (case-insensitive).
Kasus tepi yang ditangani
- Memperbarui stok produk yang tidak ditemukan: API harus mengembalikan error NOT_FOUND.
- Mengurangi stok hingga menjadi negatif: API harus menolak operasi dan mengembalikan error INSUFFICIENT_STOCK.
- Update stok ganda secara bersamaan (race condition): Sistem harus memastikan konsistensi stok akhir.
Manajemen API Key & Rate Limiting
Memungkinkan pembuatan, pencabutan, dan konfigurasi API key untuk setiap aplikasi klien, termasuk batasan laju panggilan API.
- Setiap API key memiliki nama klien, batasan laju (request per menit), dan status aktif/nonaktif.
- Setiap permintaan ke API produk/stok harus menyertakan API key yang valid di header `X-API-Key`.
- Permintaan dengan API key tidak valid atau tidak aktif akan ditolak dengan status 401 Unauthorized.
- Permintaan yang melebihi batasan laju API key akan ditolak dengan status 429 Too Many Requests dan header `Retry-After`.
- Sistem mencatat setiap penggunaan API key (timestamp, endpoint, status) untuk tujuan audit dan debugging.
Kasus tepi yang ditangani
- API key yang dicabut masih digunakan: Permintaan harus ditolak.
- Batasan laju terlampaui: Permintaan harus ditolak dengan pesan yang jelas.
Webhook Notifikasi Stok & Harga
Mengirim notifikasi otomatis ke URL yang terdaftar saat stok produk menipis di bawah ambang batas atau harga jual/HPP berubah.
- Pemilik dapat mendaftarkan URL webhook untuk menerima notifikasi.
- Webhook dapat dikonfigurasi untuk event 'stok_menipis' (low_stock) atau 'harga_berubah' (price_change).
- Saat stok produk mencapai atau di bawah ambang batas (misal: 5 unit), sistem akan mengirim payload JSON ke URL webhook yang terdaftar untuk event 'stok_menipis'.
- Saat harga jual atau HPP produk berubah, sistem akan mengirim payload JSON ke URL webhook yang terdaftar untuk event 'harga_berubah'.
- Payload webhook mencakup detail produk (SKU, nama, stok saat ini, harga baru/lama) dan jenis event.
- Sistem akan mencoba ulang pengiriman webhook jika gagal (misal: kode status non-2xx) hingga 3 kali dengan interval eksponensial.
Kasus tepi yang ditangani
- URL webhook tidak valid atau tidak merespons: Sistem harus mencatat kegagalan dan mencoba ulang.
- Perubahan stok yang sangat cepat memicu banyak webhook: Sistem harus mengelompokkan notifikasi atau memiliki mekanisme debounce (out of scope for MVP, but good to note).
Format Error API Seragam
Semua respons error API akan mengikuti format JSON yang konsisten untuk memudahkan penanganan oleh klien.
- Setiap respons error akan memiliki struktur JSON `{ "success": false, "message": "Pesan error deskriptif", "code": "KODE_ERROR_DOMAIN" }`.
- Kode error domain (misal: `PRODUCT_NOT_FOUND`, `INSUFFICIENT_STOCK`, `INVALID_API_KEY`, `RATE_LIMIT_EXCEEDED`) akan digunakan untuk error spesifik bisnis.
- Error validasi input akan mengembalikan status 400 Bad Request dengan detail field yang salah.
- Error server internal akan mengembalikan status 500 Internal Server Error dengan pesan generik.
Dokumentasi API OpenAPI
Menyediakan dokumentasi API interaktif menggunakan standar OpenAPI (Swagger UI) agar developer klien dapat dengan mudah memahami dan mengintegrasikan API.
- Dokumentasi API tersedia di path `/api-docs`.
- Dokumentasi mencakup semua endpoint yang tersedia, parameter, contoh request/response, dan skema error.
- Dokumentasi dapat diakses tanpa otentikasi.
- Contoh pemanggilan API (cURL) tersedia untuk setiap endpoint.
Tech Stack
Memilih Fastify untuk performa tinggi dan overhead rendah, cocok untuk API service. PostgreSQL sebagai database relasional yang robust. Prisma ORM untuk kemudahan interaksi database dan skema.
Architecture
Sistem ini adalah API backend monolitik yang dibangun dengan Node.js (Fastify) dan PostgreSQL. Logika bisnis diatur dalam service layer, berinteraksi dengan database melalui repository layer yang menggunakan Prisma ORM. Otentikasi dilakukan via API key dengan middleware Fastify. Webhook dikirim secara asinkron dan memiliki mekanisme retry.
Fastify Server
Menerima permintaan HTTP, routing, validasi input, otentikasi API key, dan mengirim respons.
Auth Service
Memvalidasi API key, menerapkan rate limiting, dan mencatat penggunaan API key.
Product Service
Mengelola logika bisnis terkait produk dan stok, termasuk memicu webhook.
Webhook Service
Mengirim notifikasi webhook ke URL terdaftar dan mengelola antrean percobaan ulang.
Repositories (Prisma)
Berinteraksi langsung dengan database PostgreSQL untuk operasi CRUD pada entitas.
PostgreSQL Database
Menyimpan semua data produk, stok, API key, langganan webhook, dan event webhook.
Background Job (Webhook Retry)
Secara berkala memproses dan mencoba ulang pengiriman webhook yang gagal.
Alur data
- Aplikasi klien mengirim permintaan HTTP ke Fastify Server dengan `X-API-Key`.
- Fastify Server memanggil Auth Service untuk memvalidasi API key dan memeriksa rate limit.
- Jika valid, permintaan diteruskan ke Product Service.
- Product Service memanggil Repository (Product/Stock) untuk berinteraksi dengan PostgreSQL.
- PostgreSQL mengembalikan data atau mengonfirmasi perubahan.
- Jika ada perubahan stok/harga yang memicu notifikasi, Product Service memanggil Webhook Service.
- Webhook Service mengirim payload ke URL webhook klien dan mencatat event di PostgreSQL.
- Fastify Server mengembalikan respons HTTP ke aplikasi klien.
- Background Job secara berkala mengambil event webhook yang gagal dari PostgreSQL dan mencoba mengirim ulang via Webhook Service.
Database Schema
Ini struktur data aplikasimu. Kamu tidak perlu paham semuanya — AI yang akan membuatnya. Kolom created_at/updated_at ditambahkan otomatis tiap tabel.
Relasi
- products.id → stocks.product_id (one-to-one)
- api_keys.id → webhook_subscriptions.api_key_id (one-to-many)
- webhook_subscriptions.id → webhook_events.subscription_id (one-to-many)
Struktur Folder
Struktur ini dikunci agar AI agent membangun dengan susunan file yang sama.
- src/
- src/server.ts — Inisialisasi Fastify app, plugin, dan error handler.
- src/config/ — Konfigurasi aplikasi.
- src/plugins/ — Plugin Fastify (misal: auth, swagger).
- src/routes/ — Definisi endpoint API.
- src/routes/v1/ — Endpoint API versi 1.
- src/routes/v1/products.routes.ts — Endpoint untuk produk dan stok.
- src/routes/v1/api-keys.routes.ts — Endpoint untuk API key.
- src/routes/v1/webhook-subscriptions.routes.ts — Endpoint untuk langganan webhook.
- src/services/ — Logika bisnis inti.
- src/services/auth.service.ts — Logika otentikasi dan rate limiting.
- src/services/product.service.ts — Logika bisnis produk dan stok.
- src/services/webhook.service.ts — Logika pengiriman dan retry webhook.
- src/repositories/ — Abstraksi akses database menggunakan Prisma.
- src/repositories/product.repository.ts
- src/repositories/stock.repository.ts
- src/repositories/api-key.repository.ts
- src/repositories/webhook.repository.ts
- src/utils/ — Fungsi utilitas umum.
- src/errors/ — Definisi custom error.
- src/jobs/ — Background jobs.
- src/jobs/webhook-retry.job.ts
- prisma/ — Skema dan migrasi Prisma.
- prisma/schema.prisma — Definisi skema database.
- tests/ — File pengujian.
API Endpoints
Enum
Environment Variables
Library Inti
Tasks (siap kirim ke AI agent)
Inisialisasi proyek Node.js dengan Fastify, Prisma, dan PostgreSQL. Pasang test runner (misal: Vitest) dan buat konfigurasi dasar untuk unit dan integrasi test.
Lihat isi perintah
Buat struktur proyek Node.js dengan Fastify, Prisma, dan PostgreSQL. Konfigurasi Vitest sebagai test runner. Pastikan ada file `src/server.ts` untuk Fastify app, `prisma/schema.prisma` untuk skema database, dan `vitest.config.ts`.
Definisikan skema database untuk `products`, `stocks`, `api_keys`, `webhook_subscriptions`, dan `webhook_events` menggunakan Prisma schema.
Lihat isi perintah
Buat file `prisma/schema.prisma` dengan definisi tabel `products`, `stocks`, `api_keys`, `webhook_subscriptions`, dan `webhook_events` sesuai `databaseSchema` di PRD. Terapkan relasi dan constraint yang diperlukan. Jalankan `prisma migrate dev` untuk membuat migrasi awal.
Buat modul repository untuk berinteraksi dengan tabel `products` menggunakan Prisma Client. Sertakan fungsi untuk mencari, mendapatkan detail, dan memperbarui produk.
Lihat isi perintah
Buat file `src/repositories/product.repository.ts` yang mengimplementasikan fungsi CRUD dasar untuk entitas `Product` menggunakan Prisma Client. Sertakan fungsi `findMany`, `findBySku`, `updatePrice`.
Buat modul repository untuk berinteraksi dengan tabel `stocks` menggunakan Prisma Client. Sertakan fungsi untuk mendapatkan stok dan memperbarui kuantitas.
Lihat isi perintah
Buat file `src/repositories/stock.repository.ts` yang mengimplementasikan fungsi untuk `findByProductId`, `updateQuantity` (dengan penambahan/pengurangan) menggunakan Prisma Client. Pastikan operasi update kuantitas aman dari race condition dengan `UPDATE ... SET quantity = quantity + X WHERE ...`.
Buat modul repository untuk mengelola API key, termasuk validasi dan pencatatan penggunaan.
Lihat isi perintah
Buat file `src/repositories/api-key.repository.ts` yang mengimplementasikan fungsi `findByKeyHash`, `create`, `updateStatus`, dan `recordUsage` untuk entitas `ApiKey` menggunakan Prisma Client.
Buat modul repository untuk mengelola langganan webhook dan event webhook.
Lihat isi perintah
Buat file `src/repositories/webhook.repository.ts` yang mengimplementasikan fungsi `findSubscriptionsByEvent`, `createSubscription`, `createEvent`, `updateEventStatus` untuk entitas `WebhookSubscription` dan `WebhookEvent` menggunakan Prisma Client.
Buat service untuk memvalidasi API key dan menerapkan batasan laju.
Lihat isi perintah
Buat file `src/services/auth.service.ts` yang berisi fungsi `validateApiKey(key: string)` yang mencari API key di database, memverifikasi keaktifannya, dan mencatat penggunaan. Implementasikan juga logika rate limiting menggunakan in-memory store (misal: `lru-cache` atau `rate-limiter-flexible`) per API key. Jika rate limit terlampaui, throw error `RATE_LIMIT_EXCEEDED`.
Buat plugin Fastify untuk otentikasi API key yang akan digunakan di semua endpoint terproteksi.
Lihat isi perintah
Buat file `src/plugins/auth.plugin.ts` sebagai plugin Fastify. Plugin ini harus mengekstrak `X-API-Key` dari header, memanggil `auth.service.ts#validateApiKey`, dan jika valid, mendekorasi request dengan objek `apiKey` yang ditemukan. Jika tidak valid atau rate limit terlampaui, kirim respons error 401/429 dengan format error seragam.
Buat endpoint untuk mengambil daftar produk dengan paginasi, filter, dan pengurutan.
Lihat isi perintah
Buat file `src/routes/v1/products.routes.ts` yang berisi endpoint `GET /v1/products`. Terapkan middleware otentikasi. Endpoint ini harus menerima query params `page`, `limit`, `search` (untuk nama/SKU), `sortBy`, `sortOrder`. Panggil `product.repository.ts#findMany` dan kembalikan respons paginasi.
Buat endpoint untuk mengambil detail produk berdasarkan SKU.
Lihat isi perintah
Tambahkan endpoint `GET /v1/products/:sku` ke `src/routes/v1/products.routes.ts`. Terapkan middleware otentikasi. Endpoint ini harus memanggil `product.repository.ts#findBySku`. Jika produk tidak ditemukan, kembalikan error 404 `PRODUCT_NOT_FOUND`.
Buat endpoint untuk memperbarui stok produk (menambah/mengurangi).
Lihat isi perintah
Tambahkan endpoint `PATCH /v1/products/:sku/stock` ke `src/routes/v1/products.routes.ts`. Terapkan middleware otentikasi. Endpoint ini harus menerima body `{ quantity_change: number }`. Panggil `stock.repository.ts#updateQuantity`. Jika stok menjadi negatif, kembalikan error 400 `INSUFFICIENT_STOCK`. Tangani race condition dengan update atomik di database.Buat endpoint untuk memperbarui harga jual dan HPP produk.
Lihat isi perintah
Tambahkan endpoint `PATCH /v1/products/:sku/price` ke `src/routes/v1/products.routes.ts`. Terapkan middleware otentikasi. Endpoint ini harus menerima body `{ price?: number, hpp?: number }`. Panggil `product.repository.ts#updatePrice`. Pastikan harga dan HPP tidak boleh negatif.Buat service untuk mengirim webhook dan mengelola antrean percobaan ulang.
Lihat isi perintah
Buat file `src/services/webhook.service.ts`. Implementasikan fungsi `sendWebhook(subscription: WebhookSubscription, eventPayload: object)` yang menggunakan `axios` untuk mengirim POST request. Jika gagal, simpan event ke `webhook_events` dengan status `PENDING` dan jadwalkan percobaan ulang. Buat juga fungsi `processPendingWebhooks()` yang akan dipanggil oleh background job.
Modifikasi service produk dan stok untuk memicu webhook saat kondisi terpenuhi.
Lihat isi perintah
Modifikasi `src/services/product.service.ts` dan `src/services/stock.service.ts` (jika ada, atau langsung di route handler) untuk memanggil `webhook.service.ts#sendWebhook` setelah update stok atau harga. Untuk stok, cek apakah kuantitas baru di bawah ambang batas (misal: 5). Untuk harga, selalu kirim notifikasi jika berubah. Ambil ambang batas stok dari `products.low_stock_threshold`.
Buat background job yang berjalan terjadwal untuk memproses webhook yang gagal dikirim.
Lihat isi perintah
Buat file `src/jobs/webhook-retry.job.ts`. Job ini harus memanggil `webhook.service.ts#processPendingWebhooks()`. Konfigurasi job ini agar berjalan setiap 5 menit menggunakan `node-cron` atau library serupa. Pastikan job ini diinisialisasi di `src/server.ts`.
Buat handler error global di Fastify untuk memastikan semua respons error mengikuti format yang ditentukan.
Lihat isi perintah
Modifikasi `src/server.ts` untuk menambahkan `setErrorHandler` Fastify. Handler ini harus menangkap semua error yang dilempar oleh route/service dan mengubahnya menjadi format JSON `{ success: false, message: string, code?: string }` dengan kode status HTTP yang sesuai (400, 401, 404, 429, 500). Definisikan custom error class untuk error domain seperti `ProductNotFound`, `InsufficientStock`, `InvalidApiKey`, `RateLimitExceeded`.Integrasikan Swagger UI ke Fastify dan buat definisi OpenAPI untuk semua endpoint.
Lihat isi perintah
Instal `fastify-swagger` dan `swagger-ui-dist`. Konfigurasi plugin di `src/server.ts` untuk melayani dokumentasi di `/api-docs`. Tambahkan skema OpenAPI (menggunakan JSDoc atau skema JSON/YAML) untuk semua endpoint di `src/routes/v1/products.routes.ts`, termasuk parameter, body request, respons sukses, dan respons error seragam. Sertakan contoh cURL untuk setiap endpoint.
Buat endpoint untuk membuat API key baru. Endpoint ini harus diakses secara manual atau dengan API key master.
Lihat isi perintah
Buat endpoint `POST /v1/api-keys` di `src/routes/v1/api-keys.routes.ts`. Endpoint ini harus menerima body `{ client_name: string, rate_limit_per_minute: number }`. Hasilkan API key (string acak), hash menggunakan bcrypt, simpan hash ke database, dan kembalikan API key mentah HANYA SEKALI di respons. Endpoint ini harus dilindungi oleh API key khusus 'master' atau diakses secara manual.Buat endpoint untuk memperbarui status atau rate limit API key.
Lihat isi perintah
Tambahkan endpoint `PATCH /v1/api-keys/:id` ke `src/routes/v1/api-keys.routes.ts`. Endpoint ini harus menerima body `{ is_active?: boolean, rate_limit_per_minute?: number }`. Hanya API key master yang boleh mengakses endpoint ini. Panggil `api-key.repository.ts#update`.Buat endpoint untuk mendaftarkan langganan webhook baru.
Lihat isi perintah
Buat endpoint `POST /v1/webhook-subscriptions` di `src/routes/v1/webhook-subscriptions.routes.ts`. Endpoint ini harus menerima body `{ event_type: 'low_stock' | 'price_change', callback_url: string }`. Panggil `webhook.repository.ts#createSubscription`. Pastikan `callback_url` adalah URL yang valid.Risks
- Race condition saat update stok: Jika tidak ditangani dengan benar, beberapa permintaan update stok secara bersamaan dapat menyebabkan data stok tidak konsisten. Mitigasi: Gunakan operasi update atomik di database (`UPDATE ... SET quantity = quantity + X`).
- Kegagalan pengiriman webhook: Jika server klien tidak merespons atau URL tidak valid, notifikasi penting bisa hilang. Mitigasi: Implementasi retry mechanism dengan backoff eksponensial dan pencatatan event.
- Keamanan API key: API key yang terekspos dapat disalahgunakan. Mitigasi: Simpan hash API key di database, bukan plainteks. Edukasi pengguna untuk menjaga kerahasiaan API key.
Open Questions
- Apakah perlu ada endpoint untuk membuat produk baru atau menghapus produk? Untuk MVP, diasumsikan produk di-seed atau dikelola secara manual di database.
- Bagaimana mekanisme untuk mengelola API key master yang dapat membuat API key lain? Untuk MVP, diasumsikan API key master di-seed langsung ke database atau dikelola secara manual.