คู่มือ aWhere
พัฒนาAPI

คู่มือ API

เรียก API ของแพลตฟอร์มด้วย bearer token หรือ API key รู้ว่าแต่ละ endpoint อยู่ที่ path ไหน ข้อผิดพลาดหน้าตาอย่างไร และแบ่งหน้าอย่างไร

API ทั้งหมดของแพลตฟอร์มอยู่ที่ <site>/api/... และทุกคำขอต้องมี bearer token หรือ API key <site> ในหน้านี้คือที่อยู่ของแพลตฟอร์มคุณ เช่น https://platform.example.com

ลองเรียกดู

curl -H "X-API-Key: $KEY" https://platform.example.com/api/profile

ถ้าได้โปรไฟล์ของเจ้าของคีย์กลับมา คีย์ใช้ได้

ที่อยู่

แต่ละ path ขึ้นต้นเป็นของโหนดหนึ่ง ตารางของแต่ละโหนดมีทุก endpoint

pathโหนด
/api/authauthorization-server ออก token ดู bearer token จาก OAuth client
/api/itemitem-services ทำงานกับรายการและระเบียน และ tile-services ส่ง tile ใต้ /api/item/:id/actions/getTile
/api/filefile-services ส่งไฟล์
/api/realtimerealtime-services ส่งเหตุการณ์ผ่าน socket.io
/api ที่เหลือcore-services ผู้ใช้งาน กลุ่ม Drive โปรไฟล์ และการจัดการ

item-services ส่งเอกสาร OpenAPI ที่สร้างจากโค้ดให้ที่ /api/item/openapi/openapi.json และหน้าดูที่ /api/item/openapi/ui สองตัวนี้ไม่ต้องลงชื่อเข้าใช้

การยืนยันตัวตน

ส่งข้อมูลยืนยันตัวตนใน header เสมอ ไม่ใส่ใน URL เพราะ URL ถูกบันทึกไว้ในหลายที่ ไม่มีวิธีอื่น: endpoint ไม่รับชื่อผู้ใช้และรหัสผ่าน และไม่เชื่อ header x-auth-*

วิธีheaderใช้เมื่อ
bearer tokenAuthorization: Bearer <token>แอปที่ให้คนลงชื่อเข้าใช้ และทำงานแทนเขา
API keyX-API-Key: <key>โปรแกรมที่ทำงานเอง ไม่มีคนลงชื่อเข้าใช้
ลิงก์ที่เซ็นชื่อไม่มี มี sig ใน URLแสดงหรือดาวน์โหลดไฟล์ใน <img> หรือลิงก์

bearer token จาก OAuth client

คุณต้องมี OAuth client ก่อน สร้างได้ใน Account (OAuth client) หรือผู้ดูแลระบบสร้างให้ (OAuth client ของแพลตฟอร์ม) จดค่า Client ID และ redirect URI ที่ลงทะเบียนไว้ แล้วทำตามนี้ โดยใช้ PKCE

  1. สร้างค่า code_verifier แบบสุ่ม และคำนวณ code_challenge จากมัน

    verifier=$(openssl rand -base64 48 | tr -d '=+/' | cut -c1-64)
    challenge=$(printf %s "$verifier" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
  2. ส่งผู้ใช้งานไปที่หน้าลงชื่อเข้าใช้ redirect_uri ต้องขึ้นต้นด้วยค่าที่ลงทะเบียนไว้ใน client

    https://platform.example.com/api/auth/oauth2/authorize
      ?response_type=code
      &client_id=my-app
      &redirect_uri=https://app.example.com/callback
      &code_challenge=<challenge>
      &code_challenge_method=sha256
      &state=<ค่าสุ่ม>
  3. หลังลงชื่อเข้าใช้ เบราว์เซอร์กลับมาที่ redirect_uri พร้อม code และ state ให้ตรวจ state ว่าเป็นค่าที่คุณส่งไป

  4. แลก code เป็น token

    curl -s https://platform.example.com/api/auth/oauth2/token \
      -d grant_type=authorization_code \
      -d client_id=my-app \
      -d code="$CODE" \
      -d code_verifier="$verifier" \
      -d redirect_uri=https://app.example.com/callback
    { "access_token": "…", "refresh_token": "…", "expires_in": 86400, "token_type": "Bearer" }
  5. เรียก API ด้วย Authorization: Bearer <access_token>

access_token อยู่ได้ประมาณหนึ่งวัน (expires_in เป็นวินาที) refresh_token อยู่ได้ประมาณ 30 วัน และใช้ได้ครั้งเดียว ต่ออายุด้วย

curl -s https://platform.example.com/api/auth/oauth2/token \
  -d grant_type=refresh_token -d client_id=my-app \
  -d refresh_token="$REFRESH" -d code_verifier="$verifier"

ถ้าฝั่งเซิร์ฟเวอร์เก็บความลับได้ ให้ส่ง client_secret แทน code_verifier ใน body หรือใช้ HTTP Basic (client_id:client_secret) กับ endpoint /oauth2/token เท่านั้น ถอน token ด้วย POST /api/auth/oauth2/revoke และ body token=<access_token>

API key

สร้างใน Account (API key ส่วนตัว) หรือผู้ดูแลระบบสร้างให้ (API key ของแพลตฟอร์ม) คีย์ทำงานแทนเจ้าของคีย์ และทำได้เท่าที่ scope ของคีย์ให้

scopeทำได้
public:readerอ่านเนื้อหาสาธารณะ
items:readerอ่านรายการที่เป็นของเจ้าของคีย์หรือแชร์ให้เขา
items:organizerจัดการรายการที่เป็นของเจ้าของคีย์
groups:reader, groups:organizerอ่าน หรือจัดการ กลุ่มของเจ้าของคีย์
admin:reader, admin:organizerอ่าน หรือจัดการ ข้อมูลของผู้ดูแลระบบ เช่น ผู้ใช้งาน บทบาท client และ API key

GET /api/credential/scope แสดงรายการ scope ทั้งหมดของเวอร์ชันนี้ endpoint ใต้ /api/admin ต้องการคีย์ที่มี scope ขึ้นต้นด้วย admin และเจ้าของคีย์ต้องมีสิทธิ์ผู้ดูแลระบบ

ไฟล์ใน <img> หรือลิงก์ดาวน์โหลดส่ง header ไม่ได้ ให้ขอลิงก์จาก file-services ด้วย token หรือ API key ของคุณ

curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"files":[{"id":"<id ของไฟล์>","use":"content"}]}' \
  https://platform.example.com/api/file/links

use เป็น content (แสดงในหน้า) download (บันทึก) หรือ thumbnail พร้อม width เป็นหนึ่งใน 160, 320, 640, 1280 และ 2048 คำตอบมี links ของแต่ละไฟล์ และ denied ซึ่งเป็น id ของไฟล์ที่คุณอ่านไม่ได้ ลิงก์เป็น path ที่ขึ้นต้นด้วย /api/file/ และมี sig ใช้กับ GET ธรรมดาได้เลย ไม่ต้องมี header จนถึงสิ้นชั่วโมงถัดไป (ระหว่างหนึ่งถึงสองชั่วโมง) คีย์ของระบบขอลิงก์ไม่ได้ เพราะลิงก์ต้องผูกกับผู้ใช้งานคนหนึ่ง

ระดับในรายการ

endpoint ใต้ /api/item/:id ดูว่าคุณมีระดับอะไรในรายการนั้น ตารางใน item-services เขียนระดับต่ำสุดที่ต้องมี และระดับที่สูงกว่าทำได้ด้วย

ระดับในตารางระดับในแอป
readerผู้อ่าน
writerผู้เขียน
itemOrganizerคนจัดการรายการ

ระดับเหล่านี้ตั้งตอนแชร์ ดู แชร์ คีย์ที่ไม่มี scope items:organizer ทำอะไรที่ต้องการระดับสูงกว่า reader ไม่ได้

ข้อผิดพลาด

ทุกโหนดตอบด้วยรหัส HTTP และ body แบบ JSON ที่ต่างกันเล็กน้อย

โหนดbody
core-services{ "errors": [ { "code": "FORBIDDEN", "message": "Forbidden." }, { "message": "INSUFFICIENT_PERMISSION" } ] } รายการแรกคือชนิด รายการหลังบอกเหตุผล
item-services{ "message": "Not found", "code": 404, "type": "NOT_FOUND_ITEM" } code คือรหัส HTTP และ type บอกเหตุผล
file-services{ "message": "Not found", "code": 404, "type": "NOT_FOUND_FILE" } ถ้าเป็น 5xx type เป็น INTERNAL_ERROR เท่านั้น

รหัสที่พบบ่อย

รหัสเมื่อเหตุผลที่พบ
401ไม่มีข้อมูลยืนยันตัวตน หรือใช้ไม่ได้INVALID_TOKEN, EXPIRED_TOKEN, INVALID_API_KEY, EXPIRED_API_KEY, INVALID_AUTH_TYPE
403ยืนยันตัวตนแล้ว แต่ไม่มีสิทธิ์INSUFFICIENT_SCOPE, INSUFFICIENT_PERMISSION
404ไม่พบสิ่งที่ขอ หรือคุณเห็นไม่ได้NOT_FOUND_ITEM, NOT_FOUND_FILE
405ใช้เมธอดที่ path นั้นไม่รับ

/api/file ตอบ 404 เหมือนกันทั้งไฟล์ที่ไม่มีและไฟล์ที่คุณอ่านไม่ได้ เพื่อไม่ให้ใครเดา id

การแบ่งหน้า

endpoint ที่แสดงรายการส่วนใหญ่รับ skip และ limit ทาง query string ของ GET หรือใน body ของ POST ค่าเริ่มต้นคือ limit 10 และ limit=0 คือไม่จำกัด รับ keyword สำหรับค้นหา sort เป็น JSON เช่น {"name":1} และ query เป็น JSON ของเงื่อนไข

curl -H "X-API-Key: $KEY" \
  "https://platform.example.com/api/admin/user?keyword=somchai&skip=20&limit=10"

คำตอบของ core-services และ item-services มี total (จำนวนทั้งหมดที่ตรงเงื่อนไข) และ data (หน้านี้)

{ "total": 134, "data": [ … ] }

ในหน้านี้