Bỏ qua, đến nội dung

@digiforce-nc/sdk

Package@digiforce-nc/sdk
Depends onaxios, qs (không phụ thuộc core package nào)

1) Tổng quan

@digiforce-nc/sdkclient SDK để giao tiếp với Digiforce server API từ browser hoặc Node.js. Cung cấp APIClient cho HTTP communication, storage adapters cho token persistence, và Auth utilities cho xác thực.


2) APIClient API

Constructor

typescript
import { APIClient } from '@digiforce-nc/sdk';

const api = new APIClient({
  baseURL: 'http://localhost:13000/api',
  headers: {
    'X-App': 'my-app',
  },
  storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
});
OptionKiểuMô tả
baseURLstringURL gốc của API server
headersobjectHeaders mặc định cho mọi request
storageTypestringLoại storage cho token persistence

request(config)

Gửi HTTP request (wrapper quanh axios):

typescript
// GET request
const response = await api.request({
  url: 'users:list',
  method: 'GET',
  params: {
    pageSize: 20,
    page: 1,
    filter: { status: 'active' },
  },
});

// POST request
const result = await api.request({
  url: 'users:create',
  method: 'POST',
  data: {
    username: 'john',
    email: 'john@example.com',
  },
});

resource(name)

Tạo Resource instance cho CRUD operations:

typescript
const usersResource = api.resource('users');
const postsResource = api.resource('posts');

axios

Truy cập trực tiếp axios instance bên dưới:

typescript
api.axios.defaults.timeout = 5000;

interceptors

Đăng ký request/response interceptors:

typescript
api.axios.interceptors.request.use((config) => {
  config.headers['X-Request-Id'] = generateId();
  return config;
});

api.axios.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      redirectToLogin();
    }
    return Promise.reject(error);
  },
);

3) Resource API

Resource cung cấp CRUD interface thống nhất, map trực tiếp với server-side actions:

list(params)

typescript
const { data } = await api.resource('users').list({
  pageSize: 20,
  page: 1,
  sort: ['-createdAt'],
  filter: {
    status: { $eq: 'active' },
  },
  fields: ['id', 'username', 'email'],
  appends: ['roles'],
});

// data = { data: [...], meta: { count, page, pageSize, totalPage } }

get(params)

typescript
const { data } = await api.resource('users').get({
  filterByTk: 1,
  fields: ['id', 'username', 'email', 'profile'],
  appends: ['roles', 'departments'],
});

create(params)

typescript
const { data } = await api.resource('users').create({
  values: {
    username: 'jane',
    email: 'jane@example.com',
    roles: [{ id: 1 }],
  },
});

update(params)

typescript
await api.resource('users').update({
  filterByTk: 1,
  values: {
    email: 'new-email@example.com',
  },
});

destroy(params)

typescript
await api.resource('users').destroy({
  filterByTk: 1,
});

// Bulk delete
await api.resource('users').destroy({
  filter: { status: 'inactive' },
});

4) ActionParams type

Mọi resource action nhận ActionParams:

typescript
interface ActionParams {
  resource?: string;      // Tên resource
  action?: string;        // Tên action
  params?: Record<string, any>; // Query parameters / request body
}

Custom actions:

typescript
// Gọi custom action
await api.resource('users').call('activate', {
  filterByTk: 1,
  values: { reason: 'approved by admin' },
});

// Tương đương
await api.request({
  url: 'users:activate/1',
  method: 'POST',
  data: { reason: 'approved by admin' },
});

5) Auth class

Token management

typescript
// Sign in - nhận và lưu token
await api.auth.signIn({
  email: 'admin@example.com',
  password: 'secret',
});

// Token tự động được attach vào mọi request sau khi sign in
console.log(api.auth.getToken()); // 'eyJhbGciOi...'

// Sign out - xóa token
await api.auth.signOut();

Flow xác thực


6) Storage adapters

BaseStorage interface

typescript
interface BaseStorage {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}

Adapters có sẵn

AdapterPersistencePhạm viPhù hợp cho
LocalStorageVĩnh viễn (đến khi xóa)Cùng originWeb app thông thường
SessionStorageĐến khi đóng tabCùng tabSession ngắn, bảo mật cao hơn
MemoryStorageĐến khi refreshCùng runtimeNode.js, SSR, testing
typescript
// Dùng MemoryStorage cho Node.js
import { APIClient, MemoryStorage } from '@digiforce-nc/sdk';

const api = new APIClient({
  baseURL: 'http://localhost:13000/api',
  storageType: 'memory',
});

Node.js

Trong môi trường Node.js (không có window), SDK tự động fallback sang MemoryStorage. Không cần cấu hình thêm.


7) getSubAppName()

Resolve tên sub-application từ URL path hoặc headers:

typescript
import { getSubAppName } from '@digiforce-nc/sdk';

const subApp = getSubAppName();
// Đọc từ URL path: /apps/my-tenant/... → 'my-tenant'
// Hoặc từ header: X-App → giá trị header

Hữu ích trong multi-tenant setup nơi mỗi tenant chạy sub-application riêng.


8) Code example đầy đủ

typescript
import { APIClient } from '@digiforce-nc/sdk';

// Khởi tạo
const api = new APIClient({
  baseURL: 'https://app.digiforce.vn/api',
});

// Đăng nhập
await api.auth.signIn({
  email: 'admin@company.com',
  password: 'secure-password',
});

// CRUD operations
const { data: users } = await api.resource('users').list({
  page: 1,
  pageSize: 10,
  filter: { 'roles.name': { $includes: 'admin' } },
  appends: ['roles', 'departments'],
  sort: ['-createdAt'],
});

console.log(`Tìm thấy ${users.meta.count} users`);

for (const user of users.data) {
  console.log(`${user.username} - ${user.roles.map(r => r.name).join(', ')}`);
}

// Tạo record mới
const { data: newOrder } = await api.resource('orders').create({
  values: {
    customerId: 42,
    items: [
      { productId: 1, quantity: 2 },
      { productId: 5, quantity: 1 },
    ],
    status: 'pending',
  },
});

console.log(`Đơn hàng ${newOrder.data.id} đã được tạo`);

// Đăng xuất
await api.auth.signOut();

CORS

Khi gọi API từ browser khác domain, đảm bảo server đã cấu hình CORS cho phép origin của client. Xem cấu hình CORS_ORIGIN trong Server Application.