@datviet/nbp-sdk
v0.0.380
Published
TypeScript SDK cho NBP API (nbp.datviet.ai) — listings, clients, tasks, agent sessions.
Readme
@datviet/nbp-sdk
TypeScript SDK cho NBP API (https://nbp.datviet.ai) — listings, clients, contacts, tasks, tags, agent sessions.
Types sinh trực tiếp từ OpenAPI của server nên autocomplete luôn khớp API thật. Chạy được trên Bun, Node 22+, và trong trình duyệt.
npm i @datviet/nbp-sdkXác thực
Hai đường, chọn theo việc UI của bạn có nhiều người dùng riêng biệt hay không:
| Đường | Khi nào | Audit / RBAC |
| -------------------------------- | ---------------------------------------------------------------- | ----------------- |
| API key (sk-…) | prototype, script, dashboard nội bộ, hoặc backend của bạn gọi hộ | chung một account |
| Đăng nhập người dùng (OAuth) | app nhiều môi giới, mỗi người quyền khác nhau | theo từng người |
API key
Settings → Khoá API → Tạo khoá. Chuỗi khoá chỉ hiện một lần.
Khoá gắn chết vào một workspace — không cần gửi X-Workspace-ID, và không đổi được phạm vi từ phía client.
import { createOpencodeClient } from "@datviet/nbp-sdk/v2/client"
const client = createOpencodeClient({
baseUrl: "https://nbp.datviet.ai",
headers: { Authorization: `Bearer ${process.env.NBP_API_KEY}` },
throwOnError: true,
})
const listings = await client.nbp.listings.list({ limit: 20 })
const clients = await client.nbp.clients.list({ limit: 20 })Endpoint bất động sản nằm dưới namespace nbp (client.nbp.listings, client.nbp.clients,
client.nbp.tasks…); còn workspaces, session, agent nằm thẳng ở gốc.
Tham số đi phẳng — không bọc query / body. SDK tự xếp cái nào vào query, cái nào vào body.
Hai mức quyền, chọn lúc tạo khoá:
| Mức | Làm được | Dùng khi | | --------- | --------------------------- | ---------------------------------------------- | | Chỉ đọc | đọc listings / clients | khoá nằm trong app của đối tác, hoặc dashboard | | Đọc & ghi | thêm/sửa listings / clients | hệ thống bạn tự kiểm soát (backend của bạn) |
Khoá không mở được /map/* (dữ liệu quy hoạch bán theo lượt) — đường đó dùng Partner API.
Đăng nhập người dùng (OAuth 2.0 + PKCE)
@datviet/nbp-sdk/auth lo PKCE, đổi code, refresh, và retry-on-401. Không tự viết lại — access token sống 1 giờ nên UI thiếu refresh chạy tốt suốt buổi demo rồi mới chết lúc khách dùng thật.
import { createNbpAuth } from "@datviet/nbp-sdk/auth"
import { createOpencodeClient } from "@datviet/nbp-sdk/v2/client"
const auth = createNbpAuth({
clientID: "congty-a", // Datviet cấp — backend dùng chuỗi này để phân quyền
redirectURI: `${location.origin}/auth/callback`,
})
const client = createOpencodeClient({ baseUrl: "https://nbp.datviet.ai", fetch: auth.fetch })
// Nút đăng nhập
await auth.login()
// Route /auth/callback
const params = new URLSearchParams(location.search)
const ok = await auth.handleCallback(params.get("code")!, params.get("state") ?? undefined)
// Chọn workspace rồi mọi request sau đó tự mang X-Workspace-ID
const ws = await client.workspaces.list()
auth.setWorkspace(ws.data!.data[0]!.id)Ba điều bắt buộc biết:
redirectURIbị khoá theo domain:localhost/127.0.0.1(http được),*.datviet.ai,*.ankapong.com(https), và deep linknbp://auth/callback. Vibe-code ở local chạy ngay; deploy lên domain khác thì auth từ chối ở bước authorize — không phải lỗi SDK. Cách rẻ nhất: xin một subdomain<tên-bạn>.datviet.ai.- Người dùng phải là member của workspace, không thì mọi request trả 403
Not a member of this workspace. Mời họ qua Settings → Thành viên bằng đúng email họ đăng nhập — hệ thống tự nối lúc họ login lần đầu. clientIDphải do Datviet cấp. Backend dùng nó để phân quyền bề mặt tính tiền; chuỗi tự đặt sẽ bị chặn 403aud_not_allowedở các endpoint đó.
Chạy trong trình duyệt
CORS đã mở cho *.datviet.ai, *.ankapong.com và mọi http://localhost:<port> — vibe-code ở local là chạy được ngay. Deploy lên domain riêng thì gửi domain đó để thêm vào whitelist.
⚠️ Khoá sk-… đặt trong frontend là coi như đã lộ — ai mở devtools cũng đọc được. Với web app thật:
- khoá read-only, hoặc
- proxy qua backend của bạn (khoá nằm ở server), hoặc
- dùng JWT của người dùng qua
auth.datviet.ai.
Ví dụ hay dùng
// Tin mới nhất, có ảnh, nhà phố dưới 10 tỷ ở Quận 2
const res = await client.nbp.listings.list({
limit: 12,
sortBy: "time_created",
sortDirection: "desc",
hasPhotos: "true",
property_type: "house",
maxPrice: 10_000_000_000,
district: "Quận 2",
})
// Tạo tin (khoá phải là "Đọc & ghi")
await client.nbp.listings.create({
price: 8_500_000_000,
tags: { category: "sale", property_type: "house", district: "Quận 2", area: 80 },
})
// Quản lý khoá API — chỉ JWT của owner/admin gọi được, khoá `sk-` bị chặn 403
await client.workspaces.apiKeys.list({ id: workspaceID })Tag lưu trong DB bằng key tiếng Anh ("house"), UI hiện nhãn Việt ("Nhà phố") — nhớ gửi key.
Bản v2 vs bản gốc
@datviet/nbp-sdk/v2/client là bản đang dùng (client theo class, client.nbp.listings.list()). Export ở root (@datviet/nbp-sdk) là bản cũ kế thừa từ opencode, giữ cho tương thích — code mới dùng /v2/client.
