@ipetsadmin/api-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +18 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
### Deprecated
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
## [1.0.0] - 2026-04-02
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Initial public release of `@ipetsadmin/api-client`.
|
|
27
|
+
- MIT license, README, and npm publish tooling (validate, build, Husky, lint-staged).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ipetsadmin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# `@ipetsadmin/api-client`
|
|
2
|
+
|
|
3
|
+
Typed HTTP client for the Truffa API. This package is meant to be consumed by front-end and mobile apps. It does **not** own API URL configuration: you pass in a preconfigured HTTP implementation (typically an [Axios](https://axios-http.com/) instance with `baseURL` already set). Request and response **shapes** come from [`@ipetsadmin/contracts`](https://www.npmjs.com/package/@ipetsadmin/contracts) (or domain-specific types you align with that package).
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Node.js** ≥ 18.18
|
|
8
|
+
- **Peer dependency:** `@ipetsadmin/contracts` (install it next to this package in your app)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @ipetsadmin/api-client @ipetsadmin/contracts
|
|
14
|
+
# or
|
|
15
|
+
npm install @ipetsadmin/api-client @ipetsadmin/contracts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Current implementation
|
|
19
|
+
|
|
20
|
+
### What ships today
|
|
21
|
+
|
|
22
|
+
| Export | Description |
|
|
23
|
+
| -------------------------- | ----------------------------------------------------------------------- |
|
|
24
|
+
| `createApiClient(options)` | Factory that returns an object of API methods. |
|
|
25
|
+
| `ApiClient` | Type of the object returned by `createApiClient`. |
|
|
26
|
+
| `CreateApiClientOptions` | `{ http: HttpClient }`. |
|
|
27
|
+
| `HttpClient` | Minimal interface your HTTP layer must satisfy (today: **`get` only**). |
|
|
28
|
+
| `HttpGet` | Type of the `get` method. |
|
|
29
|
+
|
|
30
|
+
### Endpoints implemented
|
|
31
|
+
|
|
32
|
+
| Method | Path | Client method |
|
|
33
|
+
| ------ | ---------------------- | ---------------------- |
|
|
34
|
+
| `GET` | `/api/v1/health-check` | `client.healthCheck()` |
|
|
35
|
+
|
|
36
|
+
The health check returns `Promise<IApiResponse<unknown>>` using the shared envelope from `@ipetsadmin/contracts`. When the canonical health payload exists in contracts, replace `unknown` with that type in `src/endpoints/health-check.ts`.
|
|
37
|
+
|
|
38
|
+
### Usage example (Axios)
|
|
39
|
+
|
|
40
|
+
Configure Axios once in your app (base URL, timeouts, auth interceptors). Paths in this library are **relative to `baseURL`** (e.g. `/api/v1/...`).
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import axios from 'axios';
|
|
44
|
+
import { createApiClient } from '@ipetsadmin/api-client';
|
|
45
|
+
|
|
46
|
+
const http = axios.create({
|
|
47
|
+
baseURL: process.env.API_BASE_URL, // e.g. https://api.example.com
|
|
48
|
+
timeout: 15_000,
|
|
49
|
+
headers: { 'Content-Type': 'application/json' },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const api = createApiClient({ http });
|
|
53
|
+
|
|
54
|
+
const health = await api.healthCheck();
|
|
55
|
+
// health is IApiResponse<unknown>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Axios’s `get` signature matches what this library expects: it returns a `Promise` that resolves to an object with a `data` property.
|
|
59
|
+
|
|
60
|
+
### Why a factory?
|
|
61
|
+
|
|
62
|
+
`createApiClient({ http })` keeps this package free of environment-specific configuration, supports multiple backends or tenants in one app, and makes tests trivial by injecting a mock `http` object.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Architecture overview
|
|
67
|
+
|
|
68
|
+
1. **`HttpClient`** — Abstraction over “how” HTTP is performed (Axios, `fetch` wrapper, etc.).
|
|
69
|
+
2. **`src/endpoints/*.ts`** — One module per route (or per resource): pure async functions that take `http` and return typed data.
|
|
70
|
+
3. **`createApiClient`** — Wires `http` into those functions and exposes a stable, discoverable API.
|
|
71
|
+
4. **`@ipetsadmin/contracts`** — Source of truth for DTOs shared with the server (e.g. `IApiResponse<T>`).
|
|
72
|
+
|
|
73
|
+
When the API adds a route, add an endpoint module, extend `HttpClient` if you need new verbs, and add a method on the object returned by `createApiClient`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Extending `HttpClient` for all HTTP verbs
|
|
78
|
+
|
|
79
|
+
Right now `HttpClient` only declares `get`. For **POST**, **PATCH**, **PUT**, and **DELETE**, extend the interface so adapters (Axios) stay type-safe and consistent.
|
|
80
|
+
|
|
81
|
+
Axios maps HTTP concepts roughly as follows:
|
|
82
|
+
|
|
83
|
+
| Concern | Axios request config field |
|
|
84
|
+
| ------------ | -------------------------------------------------------------- |
|
|
85
|
+
| URL path | First argument `url` (often built from path + **path params**) |
|
|
86
|
+
| Query string | `params` |
|
|
87
|
+
| JSON body | `data` |
|
|
88
|
+
| Headers | `headers` |
|
|
89
|
+
|
|
90
|
+
Recommended shape (mirror Axios so an `AxiosInstance` is assignable with minimal wrapping):
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// src/http-client.ts — extend when you add non-GET endpoints
|
|
94
|
+
|
|
95
|
+
/** Optional config aligned with Axios (params, data, headers, etc.). */
|
|
96
|
+
export type HttpRequestConfig = {
|
|
97
|
+
params?: Record<string, unknown>;
|
|
98
|
+
data?: unknown;
|
|
99
|
+
headers?: Record<string, string>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type HttpGet = <TResponse>(
|
|
103
|
+
url: string,
|
|
104
|
+
config?: HttpRequestConfig,
|
|
105
|
+
) => Promise<{ data: TResponse }>;
|
|
106
|
+
|
|
107
|
+
export type HttpPost = <TResponse>(
|
|
108
|
+
url: string,
|
|
109
|
+
config?: HttpRequestConfig,
|
|
110
|
+
) => Promise<{ data: TResponse }>;
|
|
111
|
+
|
|
112
|
+
export type HttpPut = HttpPost;
|
|
113
|
+
export type HttpPatch = HttpPost;
|
|
114
|
+
|
|
115
|
+
export type HttpDelete = <TResponse>(
|
|
116
|
+
url: string,
|
|
117
|
+
config?: HttpRequestConfig,
|
|
118
|
+
) => Promise<{ data: TResponse }>;
|
|
119
|
+
|
|
120
|
+
export interface HttpClient {
|
|
121
|
+
readonly get: HttpGet;
|
|
122
|
+
readonly post: HttpPost;
|
|
123
|
+
readonly put: HttpPut;
|
|
124
|
+
readonly patch: HttpPatch;
|
|
125
|
+
readonly delete: HttpDelete;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Adapter example (Axios):** if TypeScript complains about `config` width, use a typed wrapper:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import type { AxiosInstance } from 'axios';
|
|
133
|
+
import type { HttpClient, HttpRequestConfig } from '@ipetsadmin/api-client';
|
|
134
|
+
|
|
135
|
+
export function createAxiosHttpClient(instance: AxiosInstance): HttpClient {
|
|
136
|
+
return {
|
|
137
|
+
get: (url, config) => instance.get(url, config),
|
|
138
|
+
post: (url, config) => instance.post(url, config?.data, config),
|
|
139
|
+
put: (url, config) => instance.put(url, config?.data, config),
|
|
140
|
+
patch: (url, config) => instance.patch(url, config?.data, config),
|
|
141
|
+
delete: (url, config) => instance.delete(url, config),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Note: Axios’s `post`/`put`/`patch` take `data` as the second argument and `config` as the third; the wrapper above keeps your **endpoint** code calling `http.post(url, { data, params, headers })` in one place. Adjust the wrapper to match how you prefer to thread `config`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Implementing endpoints: patterns by verb
|
|
151
|
+
|
|
152
|
+
Conventions used below:
|
|
153
|
+
|
|
154
|
+
- **Path** — string constant; combine with **path parameters** via a small helper or template.
|
|
155
|
+
- **Query** — `config.params` (Axios serializes to the query string).
|
|
156
|
+
- **Body** — `config.data` for JSON bodies.
|
|
157
|
+
- **Headers** — `config.headers` for per-request headers (auth, idempotency keys, etc.).
|
|
158
|
+
- **Types** — import request/response types from `@ipetsadmin/contracts` when they exist.
|
|
159
|
+
|
|
160
|
+
Assume:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import type { IApiResponse } from '@ipetsadmin/contracts';
|
|
164
|
+
import type { HttpClient } from '../http-client';
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Path parameters
|
|
168
|
+
|
|
169
|
+
Build the URL in the endpoint (keep templates next to the constant for clarity):
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const USER_BY_ID_PATH = '/api/v1/users/:userId' as const;
|
|
173
|
+
|
|
174
|
+
function buildUserPath(userId: string): string {
|
|
175
|
+
return USER_BY_ID_PATH.replace(':userId', encodeURIComponent(userId));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// GET /api/v1/users/42
|
|
179
|
+
await http.get<IApiResponse<UserDto>>(buildUserPath('42'));
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For many segments, a tiny helper avoids mistakes:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
function buildPath(template: string, params: Record<string, string | number>): string {
|
|
186
|
+
let path = template;
|
|
187
|
+
for (const [key, value] of Object.entries(params)) {
|
|
188
|
+
path = path.replace(`:${key}`, encodeURIComponent(String(value)));
|
|
189
|
+
}
|
|
190
|
+
return path;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// '/api/v1/orgs/:orgId/projects/:projectId'
|
|
194
|
+
buildPath('/api/v1/orgs/:orgId/projects/:projectId', {
|
|
195
|
+
orgId: 'acme',
|
|
196
|
+
projectId: 'proj-1',
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### GET — query and headers
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const SEARCH_PATH = '/api/v1/items' as const;
|
|
204
|
+
|
|
205
|
+
export interface ListItemsParams {
|
|
206
|
+
readonly query?: Record<string, string | number | boolean | undefined>;
|
|
207
|
+
readonly headers?: Record<string, string>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function fetchItems(
|
|
211
|
+
http: HttpClient,
|
|
212
|
+
args: ListItemsParams = {},
|
|
213
|
+
): Promise<IApiResponse<ItemSummary[]>> {
|
|
214
|
+
const { data } = await http.get<IApiResponse<ItemSummary[]>>(SEARCH_PATH, {
|
|
215
|
+
params: args.query,
|
|
216
|
+
headers: args.headers,
|
|
217
|
+
});
|
|
218
|
+
return data;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### POST — body, query, headers
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const CREATE_ITEM_PATH = '/api/v1/items' as const;
|
|
226
|
+
|
|
227
|
+
export interface CreateItemInput {
|
|
228
|
+
readonly body: CreateItemRequestDto;
|
|
229
|
+
readonly query?: Record<string, string | number | boolean | undefined>;
|
|
230
|
+
readonly headers?: Record<string, string>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function createItem(
|
|
234
|
+
http: HttpClient,
|
|
235
|
+
input: CreateItemInput,
|
|
236
|
+
): Promise<IApiResponse<ItemDto>> {
|
|
237
|
+
const { data } = await http.post<IApiResponse<ItemDto>>(CREATE_ITEM_PATH, {
|
|
238
|
+
data: input.body,
|
|
239
|
+
params: input.query,
|
|
240
|
+
headers: input.headers,
|
|
241
|
+
});
|
|
242
|
+
return data;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### PATCH — partial body, path id
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const ITEM_PATH = '/api/v1/items/:itemId' as const;
|
|
250
|
+
|
|
251
|
+
export interface UpdateItemInput {
|
|
252
|
+
readonly itemId: string;
|
|
253
|
+
readonly body: Partial<UpdateItemRequestDto>;
|
|
254
|
+
readonly headers?: Record<string, string>;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function updateItem(
|
|
258
|
+
http: HttpClient,
|
|
259
|
+
input: UpdateItemInput,
|
|
260
|
+
): Promise<IApiResponse<ItemDto>> {
|
|
261
|
+
const path = buildPath(ITEM_PATH, { itemId: input.itemId });
|
|
262
|
+
const { data } = await http.patch<IApiResponse<ItemDto>>(path, {
|
|
263
|
+
data: input.body,
|
|
264
|
+
headers: input.headers,
|
|
265
|
+
});
|
|
266
|
+
return data;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### PUT — full replacement body
|
|
271
|
+
|
|
272
|
+
Same pattern as PATCH; use when the API semantics are “replace resource”:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
export async function replaceItem(
|
|
276
|
+
http: HttpClient,
|
|
277
|
+
input: { itemId: string; body: ReplaceItemRequestDto; headers?: Record<string, string> },
|
|
278
|
+
): Promise<IApiResponse<ItemDto>> {
|
|
279
|
+
const path = buildPath('/api/v1/items/:itemId', { itemId: input.itemId });
|
|
280
|
+
const { data } = await http.put<IApiResponse<ItemDto>>(path, {
|
|
281
|
+
data: input.body,
|
|
282
|
+
headers: input.headers,
|
|
283
|
+
});
|
|
284
|
+
return data;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### DELETE — path params; optional body for APIs that require one
|
|
289
|
+
|
|
290
|
+
Many DELETE endpoints have **no body**; omit `data`:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
export async function deleteItem(
|
|
294
|
+
http: HttpClient,
|
|
295
|
+
itemId: string,
|
|
296
|
+
headers?: Record<string, string>,
|
|
297
|
+
): Promise<IApiResponse<void>> {
|
|
298
|
+
const path = buildPath('/api/v1/items/:itemId', { itemId });
|
|
299
|
+
const { data } = await http.delete<IApiResponse<void>>(path, { headers });
|
|
300
|
+
return data;
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
If your API uses a JSON body on DELETE (uncommon but possible):
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
await http.delete<IApiResponse<void>>(path, {
|
|
308
|
+
data: { reason: 'user_request' },
|
|
309
|
+
headers: { 'Content-Type': 'application/json' },
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Wiring new endpoints into `createApiClient`
|
|
316
|
+
|
|
317
|
+
1. Add `src/endpoints/your-endpoint.ts` with a `fetch*` / `create*` / `update*` function that takes `http` first (and an `input` object for non-trivial inputs).
|
|
318
|
+
2. Extend `HttpClient` if the verb is not available yet.
|
|
319
|
+
3. Add a method on `ApiClient` and delegate to the endpoint function.
|
|
320
|
+
|
|
321
|
+
Example after adding `fetchItems` and `createItem`:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// src/create-api-client.ts
|
|
325
|
+
import { createItem } from './endpoints/create-item';
|
|
326
|
+
import { fetchItems } from './endpoints/list-items';
|
|
327
|
+
import { fetchHealthCheck } from './endpoints/health-check';
|
|
328
|
+
import type { HttpClient } from './http-client';
|
|
329
|
+
|
|
330
|
+
export function createApiClient(options: { http: HttpClient }) {
|
|
331
|
+
const { http } = options;
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
healthCheck: () => fetchHealthCheck(http),
|
|
335
|
+
listItems: (params?: ListItemsParams) => fetchItems(http, params),
|
|
336
|
+
createItem: (input: CreateItemInput) => createItem(http, input),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Export new types (`ListItemsParams`, `CreateItemInput`, etc.) from `src/index.ts` if consumers need them.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Testing
|
|
346
|
+
|
|
347
|
+
Inject a fake `HttpClient` and assert the right URL, method, and payload were used.
|
|
348
|
+
|
|
349
|
+
**With the current `HttpClient` (only `get`):**
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import type { HttpClient } from '@ipetsadmin/api-client';
|
|
353
|
+
import { createApiClient } from '@ipetsadmin/api-client';
|
|
354
|
+
|
|
355
|
+
const http: HttpClient = {
|
|
356
|
+
get: jest.fn().mockResolvedValue({ data: { success: true, data: {} } }),
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const api = createApiClient({ http });
|
|
360
|
+
await api.healthCheck();
|
|
361
|
+
|
|
362
|
+
expect(http.get).toHaveBeenCalledWith('/api/v1/health-check', undefined);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**After you extend `HttpClient` with `post` / `put` / `patch` / `delete`,** add matching mocks (e.g. `post: jest.fn()`) so the object satisfies the full interface.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Development (this repository)
|
|
370
|
+
|
|
371
|
+
| Script | Purpose |
|
|
372
|
+
| ------------------------- | ------------------------------------------------------------------------- |
|
|
373
|
+
| `pnpm run build` | Produce `dist/` (CJS, ESM, `.d.ts`) via [tsup](https://tsup.egoist.dev/). |
|
|
374
|
+
| `pnpm run typecheck` | `tsc --noEmit` |
|
|
375
|
+
| `pnpm run lint` | ESLint |
|
|
376
|
+
| `pnpm run validate` | typecheck + lint + Prettier check |
|
|
377
|
+
| `pnpm run prepublishOnly` | runs validate and build before publish |
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Summary
|
|
382
|
+
|
|
383
|
+
- **Today:** `createApiClient({ http })` + `healthCheck()` + `HttpClient` with **`get` only**.
|
|
384
|
+
- **Next:** Extend `HttpClient` with `post` / `put` / `patch` / `delete` and optional `HttpRequestConfig`, then add endpoint modules that use **path helpers**, **`params`**, **`data`**, and **`headers`** as shown above.
|
|
385
|
+
- **Types:** Prefer `@ipetsadmin/contracts` for DTOs and `IApiResponse<T>` for envelope responses.
|
|
386
|
+
|
|
387
|
+
For changes to this package, see [CHANGELOG.md](./CHANGELOG.md).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IApiResponse } from '@ipetsadmin/contracts';
|
|
2
|
+
|
|
3
|
+
type HttpGet = <TResponse>(url: string, config?: unknown) => Promise<{
|
|
4
|
+
data: TResponse;
|
|
5
|
+
}>;
|
|
6
|
+
interface HttpClient {
|
|
7
|
+
readonly get: HttpGet;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>>;
|
|
11
|
+
|
|
12
|
+
interface CreateApiClientOptions {
|
|
13
|
+
readonly http: HttpClient;
|
|
14
|
+
}
|
|
15
|
+
interface ApiClient {
|
|
16
|
+
readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
|
|
17
|
+
}
|
|
18
|
+
declare function createApiClient(options: CreateApiClientOptions): ApiClient;
|
|
19
|
+
|
|
20
|
+
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IApiResponse } from '@ipetsadmin/contracts';
|
|
2
|
+
|
|
3
|
+
type HttpGet = <TResponse>(url: string, config?: unknown) => Promise<{
|
|
4
|
+
data: TResponse;
|
|
5
|
+
}>;
|
|
6
|
+
interface HttpClient {
|
|
7
|
+
readonly get: HttpGet;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>>;
|
|
11
|
+
|
|
12
|
+
interface CreateApiClientOptions {
|
|
13
|
+
readonly http: HttpClient;
|
|
14
|
+
}
|
|
15
|
+
interface ApiClient {
|
|
16
|
+
readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
|
|
17
|
+
}
|
|
18
|
+
declare function createApiClient(options: CreateApiClientOptions): ApiClient;
|
|
19
|
+
|
|
20
|
+
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/endpoints/health-check.ts
|
|
4
|
+
var HEALTH_CHECK_PATH = "/api/v1/health-check";
|
|
5
|
+
async function fetchHealthCheck(http) {
|
|
6
|
+
const { data } = await http.get(HEALTH_CHECK_PATH);
|
|
7
|
+
return data;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/create-api-client.ts
|
|
11
|
+
function createApiClient(options) {
|
|
12
|
+
const { http } = options;
|
|
13
|
+
return {
|
|
14
|
+
healthCheck: () => fetchHealthCheck(http)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.createApiClient = createApiClient;
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":";;;AAGA,IAAM,iBAAA,GAAoB,sBAAA;AAO1B,eAAsB,iBAAiB,IAAA,EAAkD;AACvF,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,IAAA,CAAK,IAA2B,iBAAiB,CAAA;AACxE,EAAA,OAAO,IAAA;AACT;;;ACKO,SAAS,gBAAgB,OAAA,EAA4C;AAC1E,EAAA,MAAM,EAAE,MAAK,GAAI,OAAA;AAEjB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,MAAM,gBAAA,CAAiB,IAAI;AAAA,GAC1C;AACF","file":"index.js","sourcesContent":["import type { IApiResponse } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n * Response shape is the standard API envelope; payload typing can be refined\n * in `@ipetsadmin/contracts` when the health payload is finalized.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>> {\n const { data } = await http.get<IApiResponse<unknown>>(HEALTH_CHECK_PATH);\n return data;\n}\n","import { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n };\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/endpoints/health-check.ts
|
|
2
|
+
var HEALTH_CHECK_PATH = "/api/v1/health-check";
|
|
3
|
+
async function fetchHealthCheck(http) {
|
|
4
|
+
const { data } = await http.get(HEALTH_CHECK_PATH);
|
|
5
|
+
return data;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// src/create-api-client.ts
|
|
9
|
+
function createApiClient(options) {
|
|
10
|
+
const { http } = options;
|
|
11
|
+
return {
|
|
12
|
+
healthCheck: () => fetchHealthCheck(http)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { createApiClient };
|
|
17
|
+
//# sourceMappingURL=index.mjs.map
|
|
18
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":";AAGA,IAAM,iBAAA,GAAoB,sBAAA;AAO1B,eAAsB,iBAAiB,IAAA,EAAkD;AACvF,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,IAAA,CAAK,IAA2B,iBAAiB,CAAA;AACxE,EAAA,OAAO,IAAA;AACT;;;ACKO,SAAS,gBAAgB,OAAA,EAA4C;AAC1E,EAAA,MAAM,EAAE,MAAK,GAAI,OAAA;AAEjB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,MAAM,gBAAA,CAAiB,IAAI;AAAA,GAC1C;AACF","file":"index.mjs","sourcesContent":["import type { IApiResponse } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n * Response shape is the standard API envelope; payload typing can be refined\n * in `@ipetsadmin/contracts` when the health payload is finalized.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>> {\n const { data } = await http.get<IApiResponse<unknown>>(HEALTH_CHECK_PATH);\n return data;\n}\n","import { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ipetsadmin/api-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Client used to interact with Truffa project API",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"CHANGELOG.md"
|
|
21
|
+
],
|
|
22
|
+
"lint-staged": {
|
|
23
|
+
"src/**/*.{ts,tsx}": [
|
|
24
|
+
"eslint --fix --max-warnings 0",
|
|
25
|
+
"prettier --write"
|
|
26
|
+
],
|
|
27
|
+
"tsup.config.ts": "prettier --write",
|
|
28
|
+
"*.{json,md}": "prettier --write"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.18.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"types",
|
|
35
|
+
"typescript",
|
|
36
|
+
"shared"
|
|
37
|
+
],
|
|
38
|
+
"author": "",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@ipetsadmin/contracts": "^1.0.3",
|
|
42
|
+
"@eslint/js": "^10.0.1",
|
|
43
|
+
"@types/express": "^5.0.5",
|
|
44
|
+
"@types/node": "^24.10.0",
|
|
45
|
+
"eslint": "^10.1.0",
|
|
46
|
+
"eslint-config-prettier": "^10.1.8",
|
|
47
|
+
"husky": "^9.1.7",
|
|
48
|
+
"lint-staged": "^16.4.0",
|
|
49
|
+
"prettier": "^3.8.1",
|
|
50
|
+
"tsup": "^8.5.1",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"typescript-eslint": "^8.58.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@ipetsadmin/contracts": "^1.0.0"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"clean": "rm -rf dist",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"lint": "eslint . --cache --cache-location .eslintcache",
|
|
65
|
+
"lint:fix": "eslint . --fix --cache --cache-location .eslintcache",
|
|
66
|
+
"format": "prettier --write .",
|
|
67
|
+
"format:check": "prettier --check .",
|
|
68
|
+
"validate": "pnpm run typecheck && pnpm run lint && pnpm run format:check",
|
|
69
|
+
"publish:npm": "pnpm publish --access public --no-git-checks"
|
|
70
|
+
}
|
|
71
|
+
}
|