# Admin SDK: Database của Project

Python và Node có toàn quyền dữ liệu, cấu trúc Database trong Project; client SDK tiếp tục áp dụng policy người dùng.

Nguồn: https://whypik.com/docs/data-admin
Nhóm: Lập trình viên
Phiên bản tài liệu: 2026-10-14.10
Ngôn ngữ: vi

## Cấp khóa cho máy chủ tin cậy

Liên kết mục: https://whypik.com/docs/data-admin#section-0

Mở Project → Database → API & SDK → + Tạo khóa Data API → Loại khóa → Project Admin. Chỉ chủ tài khoản tạo hoặc thu hồi khóa Admin bằng phiên đăng nhập. Sao chép khóa một lần vào secret của máy chủ hoặc .env local; không đưa vào frontend, Git, ZIP, log hoặc ảnh chụp. Gỡ khóa khi không còn dùng.

Khóa wpk_admin_ chỉ dùng cho Database của Project ghi trên khóa. Nó bỏ qua policy bảng/dòng/cột và server:read/server:write; vẫn chịu quota, ràng buộc dữ liệu, khóa trạng thái, kiểm revision và bảo vệ thay đổi phá hủy. Không có quyền tài khoản, Project khác, Auth nội bộ, hệ điều hành hoặc SQL tùy ý.

Public key và Server key cũ giữ nguyên policy; chúng không tự thành Admin. Khóa CI chỉ deploy không tự có quyền Database. Ai có quyền deploy Mini Server có thể viết handler sử dụng quyền Admin dữ liệu và Variables của backend, nên chỉ cấp quyền đó cho người tin cậy.

Mini Server dùng danh tính backend để có Admin CRUD sẵn, không cần dán khóa Admin vào Variables. Handler phải kiểm request.user trước thao tác cần bảo vệ. API thay đổi cấu trúc dùng AdminClient/WhyPikAdmin bên ngoài runtime; broker db trong handler chỉ có CRUD.


## Python trên local hoặc máy chủ khác

Liên kết mục: https://whypik.com/docs/data-admin#section-1

Cài WhyPik CLI/Python SDK từ bài Mini Server. Đặt WHYPIK_PROJECT_UUID và WHYPIK_ADMIN_KEY trong môi trường của chương trình. Đã có bảng products với cột id/name thì ví dụ dưới đọc tối đa 20 dòng. Script standalone dùng os.environ; CLI run mới tự nạp .env.

AdminClient(*, project, key, endpoint="https://app.whypik.com", timeout=120) cố định Project UUID và HTTPS origin. timeout từ 0,1 đến 300 giây cho I/O của transport; hết chờ không chứng minh ghi đã rollback. table(name) hỗ trợ cùng filter/order_by/limit/cursor/include/select/first/page/insert/update/delete với db trong Mini. Không tự retry hoặc tải mọi trang.

```
import asyncio
import os
from whypik.admin import AdminClient

async def main():
    admin = AdminClient(
        project=os.environ["WHYPIK_PROJECT_UUID"],
        key=os.environ["WHYPIK_ADMIN_KEY"],
    )
    rows = await admin.table("products").limit(20).select("id", "name")
    print({"rows": len(rows)})

asyncio.run(main())
```


## Node trên máy chủ

Liên kết mục: https://whypik.com/docs/data-admin#section-2

Cài bằng npm install https://app.whypik.com/api/v1/public/sdk/javascript; package tải về tên @whypik/js, chưa phát hành trên npm registry. Dùng Node 24 trở lên. WhyPikAdmin chỉ xuất từ @whypik/js/server, không có trong entry trình duyệt. Không tạo client Admin trong bundle frontend.

WhyPikAdmin({project,key,backendOrigin,timeoutMs?}) có from(table) với API truy vấn giống client. schema(), applySchema(change,idempotencyKey,signal?), operation(id,signal?) trả envelope như Python. destroy() hủy request và đóng client.

```
import { WhyPikAdmin } from "@whypik/js/server";

const admin = new WhyPikAdmin({
  project: process.env.WHYPIK_PROJECT_UUID,
  key: process.env.WHYPIK_ADMIN_KEY,
  backendOrigin: "https://app.whypik.com",
});
try {
  const rows = await admin.from("products").limit(20).select("id", "name");
  console.log({ rows: rows.length });
} finally {
  admin.destroy();
}
```


## Đọc và thay đổi cấu trúc có revision

Liên kết mục: https://whypik.com/docs/data-admin#section-3

Phần này dành cho Database ở mode manual. Với mode migration, dùng kế hoạch và chuỗi migration; endpoint thay schema trực tiếp bị từ chối để không lệch lịch sử.

await admin.schema() trả {database:{id,revision,definition,status,...}}. Python apply_schema(revision=...,definition=...,idempotency_key=...,changes=None,recovery=None); Node applySchema({revision,definition,changes?,recovery?},idempotencyKey). Cả hai trả {operation:{id,state,...}}, không đồng nghĩa DDL đã hoàn thành. Dùng operation(id) tới state completed hoặc trạng thái lỗi terminal; giữ receipt trước khi kết thúc tiến trình.

Definition dùng cùng định dạng Database UI. changes mô tả đổi tên/xóa theo hợp đồng cấu trúc hiện có. Thay đổi phá hủy cần backup hợp lệ và recovery={backup_id,confirm_project}; confirm_project là ID ngắn chính xác. Khóa Admin không bỏ kiểm backup. Thay đổi revision cũ bị từ chối, không tự ghi đè sửa đổi của người khác.

Ví dụ chỉ thêm cột nullable note vào bảng đầu tiên của Database đã có. Chạy có chủ đích trên Project thử; lưu UUID yêu cầu và JSON trước khi gửi. Nếu mất phản hồi, gửi lại đúng JSON/UUID; không tạo UUID mới hoặc tự thay revision.

```
import asyncio
import copy
import json
import os
import uuid
from pathlib import Path
from whypik.admin import AdminClient

async def main():
    admin = AdminClient(project=os.environ["WHYPIK_PROJECT_UUID"], key=os.environ["WHYPIK_ADMIN_KEY"])
    receipt = Path("schema-request.json")
    if receipt.exists():
        request = json.loads(receipt.read_text(encoding="utf-8"))
    else:
        current = (await admin.schema())["database"]
        definition = copy.deepcopy(current["definition"])
        table = definition["tables"][0]  # Cần có ít nhất một bảng.
        if any(column["name"] == "note" for column in table["columns"]):
            raise ValueError("Cột note đã tồn tại")
        table["columns"].append({"name": "note", "type": "text", "nullable": True, "primary": False, "unique": False})
        request = {"revision": current["revision"], "definition": definition, "idempotency_key": str(uuid.uuid4())}
        with receipt.open("x", encoding="utf-8") as file:
            json.dump(request, file, ensure_ascii=False)
    operation = (await admin.apply_schema(**request))["operation"]
    print({"operation": operation["id"], "state": operation["state"]})
    for _ in range(60):
        operation = (await admin.operation(operation["id"]))["operation"]
        if operation["state"] not in {"pending", "applying"}:
            if operation["state"] != "completed":
                raise RuntimeError(operation["error_code"])
            return
        await asyncio.sleep(1)
    raise TimeoutError("Chưa có kết quả cuối; giữ receipt và kiểm tra lại operation")

asyncio.run(main())
```


## HTTP và kết quả chưa xác định

Liên kết mục: https://whypik.com/docs/data-admin#section-4

Dùng X-WhyPik-Key với khóa Admin, HTTPS, không Origin, Sec-Fetch-*, cookie hoặc Authorization token người dùng. Request cấu trúc tối đa 256 KiB JSON; SDK đọc response tối đa 5 MiB và không đi theo redirect. HTTP 202 chỉ xác nhận đã nhận yêu cầu.

Lỗi ghi dữ liệu data_outcome_unknown hoặc schema_outcome_unknown có MiniError.outcome_unknown (Python) / WhyPikError.outcomeUnknown (Node) bằng true. Dừng retry mù: dữ liệu kiểm theo khóa nghiệp vụ; cấu trúc giữ request/UUID gốc để đối chiếu receipt. Hủy chờ hoặc timeout không hủy thao tác đã được máy chủ nhận. Các lệnh CRUD là giao dịch riêng, không có transaction xuyên nhiều await.

| Thao tác | Endpoint / kết quả |
| --- | --- |
| CRUD | POST /api/v1/data/PROJECT_UUID/TABLE/select\|insert\|update\|delete |
| Đọc cấu trúc | GET /api/v1/admin/db/PROJECT_UUID/schema → {database} |
| Gửi cấu trúc | POST cùng /schema; JSON {revision,definition,changes?,recovery?}; Idempotency-Key UUID → 202 {operation} |
| Kiểm tra thao tác | GET /api/v1/admin/db/PROJECT_UUID/operations/OPERATION_UUID → {operation} |


## Local Database và Migration có lịch sử

Liên kết mục: https://whypik.com/docs/data-admin#section-5

Bản CLI có nhóm db hỗ trợ stack local riêng và chuỗi migration JSON; tính năng hosted được bật theo môi trường, không tự chuyển Database đang có. Làm theo bài Bắt đầu với Database Migration trước khi thử. Hiện local cần Docker Linux/Compose và source bundle tương thích, chưa tự tải image phát hành.

whypik run --db-env local chọn Database local; --env-file chọn Variables, không chọn Database. Local unavailable thì dừng, không fallback hosted. Workspace chưa cấu hình Database vẫn có thể gọi hosted bằng WHYPIK_ADMIN_KEY như trước.

Mode migration khóa apply_schema/applySchema trực tiếp, editor schema/policy và import cấu trúc. Dùng migration_plan/migration_apply hoặc CLI để thay đổi cấu trúc; CRUD và đọc schema giữ quyền Admin hiện có. Không chạy raw ALTER/DROP để vượt lịch sử.

- [Bắt đầu với Migration](https://whypik.com/docs/data-migrations)
- [Hợp đồng SDK và phục hồi](https://whypik.com/docs/data-migrations-reference)

## Đọc tiếp

- [Data API: chọn cột, lọc và phân trang](https://whypik.com/docs/data-queries)
- [Thêm, sửa, xóa bằng SDK](https://whypik.com/docs/data-writes)
- [CI/CD với GitHub, GitLab và Bitbucket](https://whypik.com/docs/cicd)
- [Variables và môi trường local](https://whypik.com/docs/mini-server-variables)
- [Mini Server: API và SDK Python](https://whypik.com/docs/mini-server-sdk)
- [Bắt đầu với Database Migration](https://whypik.com/docs/data-migrations)
- [Migration: hợp đồng, baseline và phục hồi](https://whypik.com/docs/data-migrations-reference)
