@ticatec/app-data-service
v2.1.0
Published
A browser SPA TypeScript data access abstraction layer built on top of REST services, providing typed CRUD, pagination, and list retrieval.
Downloads
244
Readme
Application Data Service
A TypeScript library providing a hierarchical set of classes for handling data operations via HTTP requests in browser Single Page Applications (SPA). Built with a foundation of BaseDataService and specialized services for common data operations including CRUD, full list retrieval, and paginated search functionality.
Browser-only package: This package is designed for browser Single Page Applications (SPA). Server-Side Rendering (SSR) and direct Node.js backend execution are not supported.
Features & Improvements in v2.1+
- Browser SPA Targeted: Framework-agnostic data access abstraction layer.
- Instance Injection & Static Fallback: Supports
new UserService(restService)instance injection while retaining staticBaseDataService.setProxy(restService)default fallback. - Safe Delete Logic: Prevents accidental
DELETE /resourcewithout ID. Subclasses that callremove()must overridegetDeleteUrl(item). - Strong Generics Support: Typed
CommonDataService<TEntity, TCreate, TUpdate>,PagingDataService<TEntity, TCriteria>,FullListDataService<TEntity, TQuery>, andPaginationList<T>. - Explicit CRUD API: Primary
create(),update(),remove()alongside overloadedsave(). - Zero Prototype Pollution: Pure utilities with no mutations to
Array.prototype.
中文 | English
Installation
npm install @ticatec/app-data-servicePrerequisites
This library requires a REST service implementation. The recommended implementation is:
npm install @ticatec/axios-restful-serviceQuick Start
import { BaseDataService, FullListDataService } from '@ticatec/app-data-service';
import AxiosRestService from '@ticatec/axios-restful-service';
// Configure the REST proxy (or pass instance to service constructor)
const restService = new AxiosRestService('https://api.example.com');
BaseDataService.setProxy(restService);
interface User {
id: number;
name: string;
}
// Create a service for your data
class UserService extends FullListDataService<User> {
constructor() {
super('/users');
}
}
const userService = new UserService();
const users = await userService.getList();
console.log(users);Architecture Overview
The library follows a hierarchical architecture:
BaseDataService (Abstract)
├── CommonDataService (Abstract)
├── FullListDataService (Concrete)
└── PagingDataService (Concrete)- BaseDataService: Foundation class providing HTTP methods, static proxy management, and constructor instance injection.
- CommonDataService: Adds CRUD operations (
create,update,remove,save) with URL-based endpoints. - FullListDataService: Specialized for retrieving complete lists without pagination.
- PagingDataService: Adds paginated search capabilities with safe recursive criteria purging.
API Reference
BaseDataService
Abstract base class providing foundational HTTP request methods, static proxy management, and constructor instance injection.
Constructor
constructor(service?: RestService)
Initializes the service with an optional RestService instance. If omitted, falls back to the static BaseDataService.getDefaultService().
Static Methods
setProxy(value: RestService): void
Configures the static HTTP client proxy for all data service instances.
import AxiosRestService from '@ticatec/axios-restful-service';
const restService = new AxiosRestService('https://api.example.com');
BaseDataService.setProxy(restService);CommonDataService<TEntity, TCreate, TUpdate>
Abstract class extending BaseDataService with CRUD operations for URL-based endpoints.
Constructor
constructor(url: string, service?: RestService)
Initializes the service with a base URL endpoint and an optional RestService instance.
class UserService extends CommonDataService<User, CreateUserDTO, UpdateUserDTO> {
constructor(service?: RestService) {
super('/users', service);
}
protected override getDeleteUrl(item: DeleteItem<User>): string {
const id =
typeof item !== 'object'
? item
: 'id' in item
? item.id
: item._id;
const value = String(id).trim();
if (!value) {
throw new Error('Entity ID must not be empty');
}
return `${this.url}/${encodeURIComponent(value)}`;
}
}Methods
create(data: TCreate, options?: RestfulOptions<TEntity>): Promise<TEntity>
Creates a new entity using POST request.
update(data: TUpdate, options?: RestfulOptions<TEntity>): Promise<TEntity>
Updates an existing entity using PUT request.
remove(item: DeleteItem<TEntity>, options?: RestfulOptions): Promise<any>
Removes a data entry using DELETE request.
⚠️ Important Note on Deletion (
remove&getDeleteUrl): Only data services that invokeremove(item)are required to overridegetDeleteUrl(item)to define their entity deletion URL. Ifremove()is called on a service without overridinggetDeleteUrl(item), it safely throws an Error requiring subclass implementation.
Legacy Method: save(data, isNew, options)
Saves data using create (when isNew: true) or update (when isNew: false).
save(data: TCreate, isNew: true, options?: RestfulOptions<TEntity>): Promise<TEntity>;
save(data: TUpdate, isNew: false, options?: RestfulOptions<TEntity>): Promise<TEntity>;FullListDataService<TEntity, TQuery, TCreate, TUpdate>
Concrete class for retrieving complete lists of data without pagination.
Methods
getList(params?: TQuery, dataProcessor?: DataProcessor<Array<TEntity>>): Promise<Array<TEntity>>
Retrieves the complete list of data items.
PagingDataService<TEntity, TCriteria, TCreate, TUpdate>
Concrete class providing paginated search functionality. Automatically purges null, undefined, empty strings (''), empty arrays ([]), and empty plain objects ({}) recursively from search criteria.
Methods
async search(criteria?: TCriteria, dataProcessor?: DataProcessor<any>): Promise<PaginationList<TEntity>>
Searches for data with pagination support.
const results = await orderService.search({
status: 'pending',
pageNo: 1
});Usage Examples
Basic CRUD Operations
import { CommonDataService, BaseDataService } from '@ticatec/app-data-service';
import type { DeleteItem } from '@ticatec/app-data-service';
import AxiosRestService from '@ticatec/axios-restful-service';
BaseDataService.setProxy(new AxiosRestService('https://api.example.com'));
interface User {
id: number;
name: string;
email: string;
}
type CreateUserDTO = Omit<User, 'id'>;
type UpdateUserDTO = Partial<User>;
class UserService extends CommonDataService<User, CreateUserDTO, UpdateUserDTO> {
constructor() {
super('/users');
}
protected override getDeleteUrl(item: DeleteItem<User>): string {
const id =
typeof item !== 'object'
? item
: 'id' in item
? item.id
: item._id;
const value = String(id).trim();
if (!value) {
throw new Error('Entity ID must not be empty');
}
return `${this.url}/${encodeURIComponent(value)}`;
}
}
const userService = new UserService();
// Create
const newUser = await userService.create({
name: 'John Doe',
email: '[email protected]'
});
// Update
const updatedUser = await userService.update({
id: newUser.id,
name: 'John Smith'
});
// Delete
await userService.remove({ id: newUser.id });Dependencies
Peer Dependencies (must install separately)
- @ticatec/restful_service_api: Interface definitions for REST services (>=0.6.1)
Recommended Installation
- @ticatec/axios-restful-service: Recommended REST service implementation
Complete installation:
npm install @ticatec/app-data-service
npm install @ticatec/restful_service_api @ticatec/axios-restful-serviceLicense
MIT License - see the LICENSE file for details.
