Giao diện
@digiforce-nc/resourcer
| npm | @digiforce-nc/resourcer |
| Import | import { ResourceManager, Resource, Action } from '@digiforce-nc/resourcer' |
| Runtime | Node.js (Koa middleware) |
Tổng quan
@digiforce-nc/resourcer là cầu nối giữa HTTP URL và action handler. Thay vì viết route thủ công cho mỗi endpoint, Resourcer tự động map URL theo convention:
/api/users→ resourceusers, actionlist(GET) hoặccreate(POST)/api/users/1→ resourceusers, actionget(GET) hoặcupdate(PUT)/api/users/1/posts→ associationpostscủa resourceusers
Resourcer xử lý:
- URL parsing - chuyển URL thành
{ resourceName, actionName, params } - Action dispatch - tìm handler tương ứng và chạy
- Middleware pipeline - global middleware + per-resource middleware
- Param merging - gộp params từ URL, query string, body theo chiến lược
Class diagram
| Class | Trách nhiệm |
|---|---|
ResourceManager | Map resources, global action handlers, middleware graph (topo sort), pre-action handlers |
Resource | Đại diện một resource, map action instances, only/except filtering |
Action | Chứa handler, params, middlewares; merge params bằng chiến lược đặc thù |
ResourceManager API
Resource management
| Method | Mô tả |
|---|---|
define(options) | Tạo resource mới. Options: name, actions, only, except, middlewares. |
isDefined(name) | Kiểm tra resource đã được define chưa. |
getResource(name) | Lấy Resource instance. Trả về undefined nếu không tồn tại. |
typescript
resourceManager.define({
name: 'tasks',
actions: {
complete: async (ctx, next) => {
// custom action logic
await next();
},
},
only: ['list', 'get', 'create', 'update', 'destroy', 'complete'],
});Action handlers
| Method | Mô tả |
|---|---|
registerActionHandler(name, handler) | Đăng ký global action handler. Áp dụng cho tất cả resource có action cùng tên. |
registerPreActionHandler(name, handler, options?) | Đăng ký pre-handler cho action. Chạy trước action handler chính. Options cho topo sort. |
typescript
// Global handler cho action "list" trên mọi resource
resourceManager.registerActionHandler('list', async (ctx, next) => {
const repo = ctx.db.getRepository(ctx.action.resourceName);
ctx.body = await repo.find(ctx.action.params);
await next();
});
// Pre-handler: validate trước khi create
resourceManager.registerPreActionHandler('create', async (ctx, next) => {
// validation logic
await next();
}, { tag: 'validate', before: 'create' });Middleware
| Method | Mô tả |
|---|---|
use(middleware, options?) | Đăng ký middleware vào pipeline. Options: tag, before, after, group. |
middleware() | Trả về Koa middleware function - entry point để mount vào Koa app. |
typescript
// Đăng ký middleware
resourceManager.use(async (ctx, next) => {
console.log(`${ctx.action.resourceName}:${ctx.action.actionName}`);
await next();
}, { tag: 'logger', before: 'handler' });
// Mount vào Koa app
app.use(resourceManager.middleware());Resource API
| Method | Mô tả |
|---|---|
addAction(name, handler) | Thêm action vào resource. |
getAction(name) | Lấy Action instance. |
typescript
const resource = resourceManager.getResource('tasks');
resource.addAction('archive', async (ctx, next) => {
// archive logic
await next();
});only và except
typescript
// Chỉ cho phép list và get
resourceManager.define({
name: 'logs',
only: ['list', 'get'],
});
// Cho phép tất cả trừ destroy
resourceManager.define({
name: 'configs',
except: ['destroy'],
});Action API
| Method | Mô tả |
|---|---|
mergeParams(params, strategies?) | Gộp params mới vào params hiện có. Dùng merge strategy cho từng field. |
getHandler() | Lấy handler function chính. |
getHandlers() | Lấy toàn bộ middleware chain (pre-handlers + middlewares + handler). |
Merge strategies
Khi nhiều nguồn cung cấp params (URL, query, body, middleware), Action merge chúng theo chiến lược:
| Param | Strategy mặc định | Ý nghĩa |
|---|---|---|
filter | andMerge | Gộp bằng $and - cả hai điều kiện đều phải thỏa |
fields | intersect | Chỉ giữ fields xuất hiện trong cả hai |
except | union | Gộp tất cả fields cần loại trừ |
appends | union | Gộp tất cả relations cần load |
sort | overwrite | Nguồn sau ghi đè nguồn trước |
values | deepMerge | Deep merge object values |
Lưu ý merge filter
Khi ACL thêm filter (restrict quyền truy cập) và user gửi filter riêng, cả hai được gộp bằng $and. Điều này đảm bảo ACL filter không bao giờ bị user filter ghi đè.
URL parsing patterns
Pattern 1: Explicit action
/api/<resource>:<action>| URL | Resource | Action |
|---|---|---|
/api/users:list | users | list |
/api/users:create | users | create |
/api/tasks:complete | tasks | complete |
Pattern 2: REST-like
/api/<resource>
/api/<resource>/<resourceIndex>| Method | URL | Resource | Action |
|---|---|---|---|
GET | /api/users | users | list |
GET | /api/users/1 | users | get |
POST | /api/users | users | create |
PUT | /api/users/1 | users | update |
DELETE | /api/users/1 | users | destroy |
Pattern 3: Association
/api/<associatedName>/<associatedIndex>/<resourceName>
/api/<associatedName>/<associatedIndex>/<resourceName>:<action>| Method | URL | Source | Resource | Action |
|---|---|---|---|---|
GET | /api/users/1/posts | users(1) | posts | list |
POST | /api/users/1/posts | users(1) | posts | create |
GET | /api/users/1/posts/5 | users(1) | posts | get |
POST | /api/users/1/roles:add | users(1) | roles | add |
POST | /api/users/1/roles:remove | users(1) | roles | remove |
Default action names
| Action | Mô tả | HTTP mặc định |
|---|---|---|
list | Lấy danh sách records | GET /resource |
get | Lấy một record | GET /resource/:id |
create | Tạo record | POST /resource |
update | Cập nhật record | PUT /resource/:id |
destroy | Xóa record | DELETE /resource/:id |
add | Thêm association (M:N) | POST /source/:id/resource:add |
remove | Gỡ association (M:N) | POST /source/:id/resource:remove |
set | Set association (1:1, 1:N) | POST /source/:id/resource:set |
toggle | Toggle association | POST /source/:id/resource:toggle |
move | Thay đổi sort order | POST /resource:move |
query | Custom query | Tùy context |
firstOrCreate | Tìm hoặc tạo | POST /resource:firstOrCreate |
updateOrCreate | Tìm cập nhật hoặc tạo | POST /resource:updateOrCreate |
Middleware chain
Khi một request được dispatch, middleware chạy theo thứ tự:
Toàn bộ middleware chain sử dụng topological sort (Toposort), cho phép:
- Đặt middleware
beforehoặcaftermiddleware khác - Nhóm middleware bằng
group - Đặt
tagđể identify middleware
typescript
resourceManager.use(aclMiddleware, {
tag: 'acl',
after: ['auth'],
group: 'access-control',
});
resourceManager.use(validateMiddleware, {
tag: 'validate',
before: ['handler'],
after: ['acl'],
});Ví dụ tổng hợp
typescript
import { ResourceManager } from '@digiforce-nc/resourcer';
const resourceManager = new ResourceManager();
// Đăng ký global CRUD handlers
resourceManager.registerActionHandler('list', async (ctx, next) => {
const repo = ctx.db.getRepository(ctx.action.resourceName);
ctx.body = await repo.find(ctx.action.params);
await next();
});
resourceManager.registerActionHandler('create', async (ctx, next) => {
const repo = ctx.db.getRepository(ctx.action.resourceName);
ctx.body = await repo.create({ values: ctx.action.params.values });
await next();
});
// Định nghĩa resource với custom action
resourceManager.define({
name: 'orders',
actions: {
cancel: async (ctx, next) => {
const { filterByTk } = ctx.action.params;
await ctx.db.getRepository('orders').update({
filterByTk,
values: { status: 'cancelled' },
});
ctx.body = { success: true };
await next();
},
},
});
// Middleware: log mọi action
resourceManager.use(async (ctx, next) => {
const start = Date.now();
await next();
console.log(
`${ctx.action.resourceName}:${ctx.action.actionName} - ${Date.now() - start}ms`
);
}, { tag: 'timer' });Xem thêm
- Resourcer & Actions - phân tích code-level chi tiết parse, dispatch, mergeParams
- Data & Request Flow - luồng request từ HTTP đến action
- FAQ - câu hỏi thường gặp
- Mã nguồn