Query Gateway (/query/list, /query/limit, /query/batch, /query/exec) ทำงานอย่างไร
ตอบสั้น: เป็นประตูที่หน้าเว็บส่ง SQL ข้อความมา backend ตรวจรูปแบบแล้วรันบนฐานข้อมูลบริษัทที่อยู่ใน token (ไม่รับชื่อฐานจาก body) ฝั่งอ่านรับเฉพาะ SELECT/WITH ฝั่งเขียนรับเฉพาะ INSERT/UPDATE/DELETE และถูกล็อกไม่ให้แตะตารางเงิน/สต็อก/GL (ทำไมเขียนข้อมูลผ่าน /query/exec ไม่ได้ (error 403 ตารางเงิน/สต็อก/GL))
รายละเอียด
ทุก route อยู่ใต้ /query ต้องมี Authorization: Bearer (router.go:90-110) หน้าเว็บเรียกผ่านฟังก์ชันใน frontend/src/lib/query-client.ts ซึ่งยิง /api/query/... ของ Next แล้วส่งต่อมา backend (ตัวเรียก API และตัวประกอบ SQL ฝั่งเบราว์เซอร์ (api-client, query-client, sml-query-builder))
| Endpoint | body | ผล | ข้อจำกัดในโค้ด |
|---|---|---|---|
/query/list |
{sql} |
คืนทุกแถว | ไม่เกิน MaxListRows = 50,000 แถว |
/query/limit |
{sql, offset, limit, includeTotal} |
คืน {totalRecords, rows} |
limit บีบอยู่ใน (0, 1000], offset < 0 เป็น 0 (NewPagination) |
/query/batch |
{sql: [...]} |
SELECT หลายคำสั่งใน read-only tx เดียว | ไม่เกิน 20 คำสั่ง |
/query/exec |
{sql} |
DML เดียว (มี RETURNING ได้) → rowsAffected, rows |
ผ่านสิทธิ์เมนู + write policy |
/query/execBatch |
{sql: [...]} |
DML หลายคำสั่งใน transaction เดียว ล้มคำสั่งไหน rollback ทั้งชุด | ไม่เกิน 1000 คำสั่ง และรวมไม่เกิน 2 MB |
ขั้นตอนฝั่งอ่าน:
NewStatement(query.go:290) ตัดคอมเมนต์/ตรวจ string literal ด้วยscanSQLต้องขึ้นต้นด้วยSELECTหรือWITHมิฉะนั้นได้ErrNotSelectOnly(400)/query/limitห่อ SQL เป็นSELECT * FROM (...) AS _q OFFSET n LIMIT mและนับรวมด้วยSELECT count(*) FROM (...) AS _query_countผู้เรียกไม่ต้องเขียน count เองGateway.Query(gateway.go:92) เปิด transaction แบบReadOnly+RepeatableRead- ทุก use case มี timeout 30 วินาที (
service.go:16) และทั้ง request มี timeout 60 วินาที (router.go:64)
ฝั่งเขียน: Service.Exec (service.go:95) ตรวจ DML-only → ValidateUserLevel (ตาราง sml_user_list, sml_database_list*, erp_option ต้อง level >= 9) → ถ้า ClientRequest (ตั้งเป็น true เฉพาะ handler ของ /query/exec และ /query/execBatch ที่ handler.go:245,299) ผ่าน ValidateClientPolicy อีกชั้น
ข้อควรระวัง
- งานที่มีกฎบัญชี (บันทึกเอกสาร คำนวณยอด รายงาน) ไม่ผ่านประตูนี้ แต่ผ่าน
/process(Process engine (/process) เรียก action อย่างไร และเพิ่ม action ใหม่ต้องแก้ที่ไหนบ้าง)
ดูเพิ่ม
- Query Gateway: /query/list, limit, batch, exec, execBatch
- API ประตูอ่านข้อมูล /query — list, limit, batch (Query gateway read endpoints)
- API ประตูเขียนข้อมูล /query/exec และ /query/execBatch (Query exec endpoints)
- ทำไมเขียนข้อมูลผ่าน /query/exec ไม่ได้ (error 403 ตารางเงิน/สต็อก/GL)
- ADR-0005 Query Gateway เริ่มแบบอ่านอย่างเดียว + สัญญา wire ใหม่
- ADR-0008 /query/batch รัน SELECT หลายตัวในคำขอเดียว
ที่มาในโค้ด
backend/internal/adapter/inbound/http/router.go:90route/query/*และqueryWriteGuard(บรรทัด 85-87)backend/internal/domain/query/query.go:290NewStatement(SELECT/WITH only) และค่าคงที่MaxListRows/MaxBatchStatements/MaxExecBatchStatements(บรรทัด 69-81)backend/internal/domain/query/query.go:455NewPaginationbackend/internal/adapter/outbound/dynamicsql/gateway.go:92read-only + RepeatableReadbackend/internal/application/query/service.go:95Exec(ขั้นตรวจ DML, level, client policy)backend/internal/adapter/inbound/http/query/handler.go:245ClientRequest: truefrontend/src/lib/query-client.ts:16queryList(และqueryLimit25,queryExec44,queryBatch54,queryExecBatch63)