@void-snippets/client
ResourceService
Generic CRUD class — list, get, create, update, delete.
#ResourceService — Generic CRUD base class
Extend it once per API resource, pass the URL prefix in the constructor, and get five typed methods for free.
#Class signature
typescript
class ResourceService<
TId, // the resource's ID type, e.g. Contact.Id
TBase, // shape in list responses, e.g. Contact.Base
TDetail = TBase, // shape in single responses, e.g. Contact.Detail
TCreate = Partial<TBase>, // create payload
TUpdate = Partial<TBase>, // update payload
TListRaw = VSDefaultPaginatedResponse<TBase>, // raw list response before adapter
TSingleRaw = VSDefaultSingleResponse<TDetail> // raw single response before adapter
>#Built-in methods
| Method | HTTP | URL |
|---|---|---|
list(params?) | GET | /endpoint?page=1&limit=10&… |
get(id) | GET | /endpoint/:id |
create(payload) | POST | /endpoint |
update(id, payload) | PATCH | /endpoint/:id |
delete(id) | DELETE | /endpoint/:id |
All five methods automatically call handleApiError on failure, so you always receive a clean Error with a readable message.
#Step 1 — Define your types
typescript
// contacts/contacts.types.ts
import type { VSId } from '@void-snippets/core';
export namespace Contact {
export type Id = VSId<string, 'Contact'>;
// Shape in list responses (lean — just what a table row needs)
export interface Base {
_id: Id;
name: string;
email: string;
phone: string;
}
// Shape in single-item responses (richer — full detail page)
export interface Detail extends Base {
createdBy: { _id: string; name: string };
notes: string;
createdAt: string;
updatedAt: string;
}
export namespace Apis {
export interface Create {
name: string;
email: string;
phone: string;
}
export interface Update {
name?: string;
email?: string;
phone?: string;
notes?: string;
}
}
}#Step 2 — Create the service
typescript
// contacts/contacts.api.ts
import { ResourceService } from '@void-snippets/client';
import type { Contact } from './contacts.types';
export class ContactsApiService extends ResourceService<
Contact.Id, // TId
Contact.Base, // TBase — returned by list()
Contact.Detail, // TDetail — returned by get(), create(), update(), delete()
Contact.Apis.Create,
Contact.Apis.Update
> {
constructor() {
super('/contacts'); // base path — list: GET /contacts, single: GET /contacts/:id
}
}
export const ContactsApis = new ContactsApiService();You can now call ContactsApis.list({ page: 1 }), ContactsApis.get(id), etc., anywhere in your app with full type safety.