Bỏ qua, đến nội dung

@digiforce-nc/resourcer

npm@digiforce-nc/resourcer
Importimport { ResourceManager, Resource, Action } from '@digiforce-nc/resourcer'
RuntimeNode.js (Koa middleware)

Tổng quan

@digiforce-nc/resourcercầ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 → resource users, action list (GET) hoặc create (POST)
  • /api/users/1 → resource users, action get (GET) hoặc update (PUT)
  • /api/users/1/posts → association posts của resource users

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

ClassTrách nhiệm
ResourceManagerMap resources, global action handlers, middleware graph (topo sort), pre-action handlers
ResourceĐại diện một resource, map action instances, only/except filtering
ActionChứa handler, params, middlewares; merge params bằng chiến lược đặc thù

ResourceManager API

Resource management

MethodMô 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

MethodMô 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

MethodMô 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

MethodMô 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();
});

onlyexcept

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

MethodMô 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:

ParamStrategy mặc địnhÝ nghĩa
filterandMergeGộp bằng $and - cả hai điều kiện đều phải thỏa
fieldsintersectChỉ giữ fields xuất hiện trong cả hai
exceptunionGộp tất cả fields cần loại trừ
appendsunionGộp tất cả relations cần load
sortoverwriteNguồn sau ghi đè nguồn trước
valuesdeepMergeDeep 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>
URLResourceAction
/api/users:listuserslist
/api/users:createuserscreate
/api/tasks:completetaskscomplete

Pattern 2: REST-like

/api/<resource>
/api/<resource>/<resourceIndex>
MethodURLResourceAction
GET/api/usersuserslist
GET/api/users/1usersget
POST/api/usersuserscreate
PUT/api/users/1usersupdate
DELETE/api/users/1usersdestroy

Pattern 3: Association

/api/<associatedName>/<associatedIndex>/<resourceName>
/api/<associatedName>/<associatedIndex>/<resourceName>:<action>
MethodURLSourceResourceAction
GET/api/users/1/postsusers(1)postslist
POST/api/users/1/postsusers(1)postscreate
GET/api/users/1/posts/5users(1)postsget
POST/api/users/1/roles:addusers(1)rolesadd
POST/api/users/1/roles:removeusers(1)rolesremove

Default action names

ActionMô tảHTTP mặc định
listLấy danh sách recordsGET /resource
getLấy một recordGET /resource/:id
createTạo recordPOST /resource
updateCập nhật recordPUT /resource/:id
destroyXóa recordDELETE /resource/:id
addThêm association (M:N)POST /source/:id/resource:add
removeGỡ association (M:N)POST /source/:id/resource:remove
setSet association (1:1, 1:N)POST /source/:id/resource:set
toggleToggle associationPOST /source/:id/resource:toggle
moveThay đổi sort orderPOST /resource:move
queryCustom queryTùy context
firstOrCreateTìm hoặc tạoPOST /resource:firstOrCreate
updateOrCreateTìm cập nhật hoặc tạoPOST /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 before hoặc after middleware 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