@libs-ui/services-http-request
v0.2.357-2
Published
> Service trung gian gọi HTTP request qua config object, hỗ trợ caching 3 tầng và bộ API Mockup để phát triển frontend không cần backend.
Readme
@libs-ui/services-http-request
Service trung gian gọi HTTP request qua config object, hỗ trợ caching 3 tầng và bộ API Mockup để phát triển frontend không cần backend.
Giới thiệu
LibsUiHttpRequestService là service Angular cung cấp cơ chế gọi bất kỳ method nào của service khác thông qua một config object thay vì inject trực tiếp. Service tự động detect Observable/Promise, hỗ trợ caching 3 tầng (memory / localStorage / IndexedDB), và cung cấp bộ helper returnListObject / returnDetailObject để mock data trong quá trình phát triển.
Tính năng
- ✅
callApi()— gọi method bất kỳ của service qua config object, không cần inject trực tiếp - ✅ Caching 3 tầng:
service(memory Map) /local-store(localStorage) /indexDB(persist + TTL tính theo phút) - ✅ Auto-detect Observable/Promise — tự động wrap bằng
lastValueFrom()nếu cần - ✅
convertResponseData— transform response trước khi trả về (cache vẫn lưu raw response) - ✅
updateArguments()— tự động cập nhật page params cho infinite scroll / load more - ✅
returnListObject— mock list API từ mảng tĩnh hoặc dynamic function, có phân trang - ✅
returnDetailObject— mock detail API, lookup theo key từ mảng hoặc async function - ✅
getConfigListDataDemo— tạo sẵn 3015 items mock với{ list, detailByData } - ✅
pagingToList— phân trang mảng dữ liệu, trả về response chuẩn có paging info
Khi nào sử dụng
- Feature component cần gọi API qua config object và caching mà không muốn tự quản lý cache logic
- Cần load more / infinite scroll — dùng
updateArguments()để tự động cập nhật page params - Phát triển frontend trước khi có API backend — dùng
returnListObject/returnDetailObjectđể mock data - Cần resolve service động tại runtime từ
serviceClass— service dùngInjector.get()nội bộ
Cài đặt
npm install @libs-ui/services-http-requestImport
// Service chính
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
// API Mockup (dùng khi phát triển / test)
import {
returnListObject,
returnDetailObject,
getConfigListDataDemo,
pagingToList,
} from '@libs-ui/services-http-request';
// Interfaces
import {
IHttpRequestConfig,
IGuideAutoUpdateArgumentsValue,
IGuideAutoUpdateArgumentsValueConfig,
} from '@libs-ui/services-http-request';Ví dụ sử dụng
1. callApi() cơ bản — gọi method qua serviceClass
import { Component, inject } from '@angular/core';
import { Injectable } from '@angular/core';
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
@Injectable({ providedIn: 'root' })
class UserService {
getUsers(page = 1) {
return this.http.get('/api/users', { params: { page } });
}
}
@Component({
standalone: true,
selector: 'app-user-list',
template: `...`,
})
export class UserListComponent {
private readonly httpReq = inject(LibsUiHttpRequestService);
async loadUsers(page = 1) {
const result = await this.httpReq.callApi<{ code: number; data: User[] }>({
serviceClass: UserService, // Angular DI resolve tự động
functionName: 'getUsers', // tên method cần gọi
argumentsValue: [page], // arguments truyền vào method
});
console.log(result.data); // User[]
}
}2. callApi() với objectInstance (không qua DI)
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
// Dùng objectInstance khi service không phải DI injectable
const myInstance = new MyCustomService();
const result = await this.httpReq.callApi({
objectInstance: myInstance,
functionName: 'fetchData',
argumentsValue: ['search-term', { page: 1 }],
});3. callApi() với caching 3 tầng
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
// Cache 'service' — memory Map, nhanh nhất, reset khi service bị GC
const result = await this.httpReq.callApi({
serviceClass: UserService,
functionName: 'getUsers',
argumentsValue: [],
keyCache: 'USER_LIST_UNIQUE_KEY', // UUID cố định — KHÔNG random mỗi lần
cacheType: 'service',
clearCache: false, // true = xóa cache trước khi gọi
});
// Cache 'local-store' — localStorage, persist qua session
const result2 = await this.httpReq.callApi({
serviceClass: ConfigService,
functionName: 'getAppConfig',
argumentsValue: [],
keyCache: 'APP_CONFIG_KEY',
cacheType: 'local-store',
});
// Cache 'indexDB' — persist qua refresh + TTL tính theo phút
const result3 = await this.httpReq.callApi({
serviceClass: DataService,
functionName: 'getMasterData',
argumentsValue: [],
keyCache: 'MASTER_DATA_KEY',
cacheType: 'indexDB',
timeCache: 30, // 30 phút
});4. callApi() với convertResponseData
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
// convertResponseData — transform response trước khi trả về
// Cache vẫn lưu raw response, mỗi lần đọc cache cũng qua convertResponseData
const users = await this.httpReq.callApi<User[]>({
serviceClass: UserService,
functionName: 'getUsers',
argumentsValue: [],
keyCache: 'USERS_KEY',
cacheType: 'service',
convertResponseData: (response) => response?.data || [],
});
// users là User[] đã extract từ IHttpResponse wrapper5. Infinite scroll với updateArguments()
import { HttpParams } from '@angular/common/http';
import { LibsUiHttpRequestService } from '@libs-ui/services-http-request';
import { IPaging } from '@libs-ui/interfaces-types';
@Component({ standalone: true, selector: 'app-infinite-list', template: `...` })
export class InfiniteListComponent {
private readonly httpReq = inject(LibsUiHttpRequestService);
private argumentsValue = [new HttpParams().set('page', '1').set('per_page', '20')];
private pagingStore: IPaging | undefined;
private isLoadedAll = false;
async loadMore() {
if (this.isLoadedAll) return;
this.isLoadedAll = this.httpReq.updateArguments(
this.argumentsValue,
{ paging: this.pagingStore },
this.pagingStore!,
'', // keySearch
false, // searchOnline
this.isLoadedAll,
);
if (!this.isLoadedAll) {
const res = await this.httpReq.callApi({
serviceClass: ItemService,
functionName: 'getItems',
argumentsValue: this.argumentsValue,
});
this.pagingStore = res.paging;
}
}
}6. API Mockup — returnListObject
import { returnListObject } from '@libs-ui/services-http-request';
import { UtilsHttpParamsRequest } from '@libs-ui/utils';
const mockData = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' },
{ id: 3, name: 'Carol', email: '[email protected]' },
];
const listService = returnListObject(mockData, undefined, { delay: 300 });
// Lấy tất cả (không phân trang)
const allItems = await listService.list();
// → { code: 200, data: [...mockData], paging: {} }
// Lấy dưới dạng Observable
const items$ = listService.listObservable();
// Phân trang
const params = new UtilsHttpParamsRequest({ fromObject: { page: '1', per_page: '2' } });
const paged = await listService.listPaging(params);
// → { code: 200, data: [item1, item2], paging: { page: 1, total_pages: 2, total_items: 3, per_page: 2 } }7. API Mockup — returnDetailObject
import { returnDetailObject } from '@libs-ui/services-http-request';
import { UtilsHttpParamsRequest } from '@libs-ui/utils';
const mockData = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
// Lookup theo id=1
const params = new UtilsHttpParamsRequest({ fromObject: { keys: '1', fieldKey: 'id' } });
const detailService = returnDetailObject(params, mockData);
const result = await detailService.detailByData();
// → { code: 200, data: { id: 1, name: 'Alice' }, paging: {} }
// Lookup nhiều keys (comma-separated)
const multiParams = new UtilsHttpParamsRequest({ fromObject: { keys: '1,2', fieldKey: 'id' } });
const multiService = returnDetailObject(multiParams, mockData);
const multiResult = await multiService.detailByData();
// → { code: 200, data: [{ id: 1, ... }, { id: 2, ... }], paging: {} }8. getConfigListDataDemo — mock data lớn cho demo
import { getConfigListDataDemo } from '@libs-ui/services-http-request';
import { UtilsHttpParamsRequest } from '@libs-ui/utils';
// Tạo sẵn 3015 items mock với schema tùy chỉnh
const config = getConfigListDataDemo(
new UtilsHttpParamsRequest({ fromObject: { keys: '', fieldKey: 'id' } }),
{ id: '', name: 'Product ', price: '100' },
);
// Dùng trong callApi config
const result = await this.httpReq.callApi({
objectInstance: config,
functionName: 'list',
argumentsValue: [new UtilsHttpParamsRequest({ fromObject: { page: '1', per_page: '20' } })],
});Methods (Service)
| Method | Signature | Mô tả |
|---|---|---|
| callApi | callApi<T>(config: IHttpRequestConfig): Promise<T> | Gọi method của service theo config, hỗ trợ caching tự động |
| updateArguments | updateArguments(args, data, paging, search, online, loaded, guide?): boolean | Cập nhật argumentsValue cho load more. Trả về true khi đã hết dữ liệu |
| fakeResponsePagingApi | fakeResponsePagingApi(): { paging: IPaging } | Tạo fake paging (page:0, total_pages:1) dùng làm fallback |
Methods (API Mockup)
| Function | Returns | Mô tả |
|---|---|---|
| returnListObject(data?, fn?, opts?) | { list, listObservable, listPaging } | Mock list API từ mảng tĩnh hoặc dynamic function |
| returnDetailObject(params, data?, fn?, ignoreSplit?, splitOp?) | { detailByData, detailByFunction } | Mock detail API — lookup theo key |
| getConfigListDataDemo(params?, schema?, fn?) | { list, detailByData } | Tạo 3015 items mock sẵn dùng |
| pagingToList(data, page, perPage) | IHttpResponse<T> | Phân trang mảng dữ liệu thành response chuẩn |
Types & Interfaces
import {
IHttpRequestConfig,
IGuideAutoUpdateArgumentsValue,
IGuideAutoUpdateArgumentsValueConfig,
} from '@libs-ui/services-http-request';
// Config object cho callApi()
interface IHttpRequestConfig<T = any> {
serviceClass?: Type<T>; // Class service — Angular DI resolve tự động
objectInstance?: T; // Hoặc truyền thẳng instance (không qua DI)
functionName?: keyof T; // Tên method cần gọi (bắt buộc)
argumentsValue: Array<any>; // Arguments truyền vào method (bắt buộc)
cacheType?: 'service' | 'local-store' | 'indexDB';
keyCache?: string; // Cache key — bắt buộc nếu dùng cache, dùng UUID cố định
timeCache?: number; // Phút — chỉ áp dụng cho indexDB
clearCache?: boolean; // true = xóa cache trước khi gọi
guideAutoUpdateArgumentsValue?: IGuideAutoUpdateArgumentsValue;
convertResponseData?: (response: any) => any; // Transform response trước khi trả về
}
// Config cho updateArguments() — điều hướng cập nhật arguments tự động
interface IGuideAutoUpdateArgumentsValue {
paging: {
page?: IGuideAutoUpdateArgumentsValueConfig;
before?: IGuideAutoUpdateArgumentsValueConfig;
after?: IGuideAutoUpdateArgumentsValueConfig;
};
search?: IGuideAutoUpdateArgumentsValueConfig;
sortOrderType?: IGuideAutoUpdateArgumentsValueConfig;
sortOrderBy?: IGuideAutoUpdateArgumentsValueConfig;
detailById?: IGuideAutoUpdateArgumentsValueConfig;
other?: Array<IGuideAutoUpdateArgumentsValueConfig>;
}
interface IGuideAutoUpdateArgumentsValueConfig {
fieldUpdate: string; // Path trong argumentsValue cần update
subFieldUpdate?: string; // Sub-field nếu target là HttpParams hoặc Object
fieldGetValue: string; // Path trong objectGetDataUpdate để đọc giá trị mới
preprocessor?: (value: any) => any; // Transform value trước khi set
log?: {
logInputs?: boolean; // Log toàn bộ inputs để debug
logGetValue?: boolean; // Log giá trị đọc được từ objectGetDataUpdate
};
}IHttpRequestConfig — mô tả chi tiết từng field
| Field | Type | Bắt buộc | Mô tả |
|---|---|---|---|
| serviceClass | Type<T> | Một trong hai | Class service — Angular DI resolve tự động qua Injector.get() |
| objectInstance | T | Một trong hai | Object instance — truyền thẳng, không qua DI |
| functionName | keyof T | ✅ | Tên method cần gọi trên service/instance |
| argumentsValue | Array<any> | ✅ | Mảng arguments truyền vào method theo thứ tự |
| cacheType | 'service' \| 'local-store' \| 'indexDB' | ❌ | Loại cache sử dụng |
| keyCache | string | Bắt buộc nếu dùng cache | Cache key — phải là UUID cố định, KHÔNG random |
| timeCache | number | ❌ | TTL tính theo phút — chỉ áp dụng cho indexDB |
| clearCache | boolean | ❌ | true = xóa cache cũ trước khi gọi API |
| guideAutoUpdateArgumentsValue | IGuideAutoUpdateArgumentsValue | ❌ | Config cho updateArguments() — không dùng trong callApi() |
| convertResponseData | (res: any) => any | ❌ | Transform response; cache lưu raw, convert chạy cả khi đọc cache |
Hidden Logic (Behavior quan trọng)
Cache key = keyCache + MD5(argumentsValue)
Cache key thực sự được tạo từ tổ hợp keyCache và MD5(argumentsValue). Cùng keyCache nhưng arguments khác sẽ cho cache key khác — tránh cache collision khi gọi cùng method với params khác.
Observable/Promise auto-detect
Service kiểm tra kết quả của method có thuộc tính .then không. Nếu không có (là Observable), tự động wrap bằng lastValueFrom(). Developer có thể truyền method trả về Observable hoặc Promise đều hoạt động bình thường.
convertResponseData chạy cả khi đọc cache
Cache luôn lưu raw response. Khi có convertResponseData, function này được gọi cả khi trả về từ API lẫn khi đọc từ cache. Điều này đảm bảo data trong cache không bị biến đổi và có thể dùng lại với transform logic khác nhau.
serviceClass vs objectInstance
serviceClass dùng Angular Injector.get() để resolve instance tại runtime — service phải là injectable (có providedIn hoặc được provide trong module/component). objectInstance truyền thẳng object — dùng khi cần mock, test, hoặc service không phải DI.
Lưu ý quan trọng
⚠️ keyCache phải là UUID cố định: KHÔNG dùng biến ngẫu nhiên hay timestamp. Cùng key với cùng arguments = cache hit.
⚠️ serviceClass hoặc objectInstance bắt buộc phải có một: Thiếu cả hai sẽ throw Error('http request config not exactly objectInstance or serviceName').
⚠️ functionName phải là key hợp lệ: Nếu method không tồn tại trên instance sẽ throw Error('http request config notfound methodInstance name: ...').
⚠️ returnListObject/returnDetailObject chỉ dùng cho dev/mock: Thay thế bằng service thật gọi API khi backend đã sẵn sàng.
⚠️ timeCache chỉ áp dụng cho indexDB: Với service và local-store, cache không có TTL tự động — dùng clearCache: true hoặc tự xóa thủ công.
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/services/http-request
