strapi-rest-lite 0.1.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/README.md +65 -0
- package/dist/collection-repository.d.ts +7 -0
- package/dist/collection-repository.js +16 -0
- package/dist/collection-service.d.ts +9 -0
- package/dist/collection-service.js +13 -0
- package/dist/http-client.d.ts +7 -0
- package/dist/http-client.js +33 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/media.d.ts +4 -0
- package/dist/media.js +12 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# strapi-rest-lite
|
|
2
|
+
|
|
3
|
+
Kleines, generisches Strapi-REST-SDK zum Wiederverwenden:
|
|
4
|
+
|
|
5
|
+
- HTTP-Client (`createHttpClient`) mit optionalem Bearer-Token
|
|
6
|
+
- Collection-Repository (`createCollectionRepository`) und Service-Fabrik (`createCollectionService`, `withDefaults`)
|
|
7
|
+
- Query-Types für Strapi (filters, populate, sort, pagination)
|
|
8
|
+
- Media-Helper (`resolveMediaUrl`)
|
|
9
|
+
|
|
10
|
+
## Installation (lokal / Git)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# lokal (Monorepo)
|
|
14
|
+
pnpm add ./packages/strapi-sdk
|
|
15
|
+
# oder npm install ./packages/strapi-sdk
|
|
16
|
+
# oder via Git-URL/npm, sobald veröffentlicht: npm i strapi-rest-lite
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Verwendung
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createHttpClient, createCollectionRepository, createCollectionService } from 'strapi-rest-lite'
|
|
23
|
+
|
|
24
|
+
const client = createHttpClient({
|
|
25
|
+
baseURL: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:1337',
|
|
26
|
+
token: process.env.STRAPI_TOKEN, // optional
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Repository + Service für einen Typ
|
|
30
|
+
const articleRepo = createCollectionRepository<Article>('articles', client)
|
|
31
|
+
const articleService = createCollectionService(articleRepo)
|
|
32
|
+
|
|
33
|
+
// Query-Beispiel
|
|
34
|
+
const articles = await articleService.list({
|
|
35
|
+
populate: '*',
|
|
36
|
+
filters: { category: { slug: { $eq: 'news' } } },
|
|
37
|
+
sort: ['publishedAt:desc'],
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Defaults einbetten
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { withDefaults } from 'strapi-rest-lite'
|
|
45
|
+
|
|
46
|
+
const createArticleService = withDefaults(createCollectionService, {
|
|
47
|
+
populate: '*',
|
|
48
|
+
sort: ['publishedAt:desc', 'createdAt:desc'],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const articleService = createArticleService(articleRepo)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Media-URL
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { resolveMediaUrl } from 'strapi-rest-lite'
|
|
58
|
+
|
|
59
|
+
const fullUrl = resolveMediaUrl(article.cover?.url, process.env.NUXT_PUBLIC_API_BASE)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hinweise
|
|
63
|
+
|
|
64
|
+
- Typen (Article, Category, …) projektabhängig definieren oder aus Strapi generieren.
|
|
65
|
+
- Für Nuxt/Composables kannst du wie bisher eigene `useXYZ`-Hooks auf Basis des Services schreiben.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HttpClient, RepositoryQuery, StrapiListResponse, StrapiSingleResponse } from './types';
|
|
2
|
+
export interface CollectionRepository<T> {
|
|
3
|
+
list(query?: RepositoryQuery): Promise<StrapiListResponse<T>>;
|
|
4
|
+
get(id: number | string, query?: RepositoryQuery): Promise<StrapiSingleResponse<T>>;
|
|
5
|
+
getBySlug(slug: string, query?: RepositoryQuery): Promise<StrapiListResponse<T>>;
|
|
6
|
+
}
|
|
7
|
+
export declare const createCollectionRepository: <T>(collection: string, client: HttpClient) => CollectionRepository<T>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const apiPath = (collection) => `/api/${collection}`;
|
|
2
|
+
export const createCollectionRepository = (collection, client) => {
|
|
3
|
+
return {
|
|
4
|
+
list: (query) => client.get(apiPath(collection), { query }),
|
|
5
|
+
get: (id, query) => client.get(`${apiPath(collection)}/${id}`, { query }),
|
|
6
|
+
getBySlug: (slug, query) => client.get(apiPath(collection), {
|
|
7
|
+
query: {
|
|
8
|
+
...(query ?? {}),
|
|
9
|
+
filters: {
|
|
10
|
+
...(query?.filters ?? {}),
|
|
11
|
+
slug: { $eq: slug },
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CollectionRepository } from './collection-repository';
|
|
2
|
+
import type { RepositoryQuery, StrapiListResponse, StrapiSingleResponse } from './types';
|
|
3
|
+
export interface CollectionService<T> {
|
|
4
|
+
list(query?: RepositoryQuery): Promise<StrapiListResponse<T>>;
|
|
5
|
+
get(id: number | string, query?: RepositoryQuery): Promise<StrapiSingleResponse<T>>;
|
|
6
|
+
getBySlug(slug: string, query?: RepositoryQuery): Promise<StrapiListResponse<T>>;
|
|
7
|
+
}
|
|
8
|
+
export declare const createCollectionService: <T>(repository: CollectionRepository<T>) => CollectionService<T>;
|
|
9
|
+
export declare const withDefaults: <T>(factory: (repo: CollectionRepository<T>) => CollectionService<T>, defaults: RepositoryQuery) => (repo: CollectionRepository<T>) => CollectionService<T>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const createCollectionService = (repository) => ({
|
|
2
|
+
list: (query) => repository.list(query),
|
|
3
|
+
get: (id, query) => repository.get(id, query),
|
|
4
|
+
getBySlug: (slug, query) => repository.getBySlug(slug, query),
|
|
5
|
+
});
|
|
6
|
+
export const withDefaults = (factory, defaults) => (repo) => {
|
|
7
|
+
const base = factory(repo);
|
|
8
|
+
return {
|
|
9
|
+
list: (query) => base.list({ ...defaults, ...(query ?? {}) }),
|
|
10
|
+
get: (id, query) => base.get(id, { ...defaults, ...(query ?? {}) }),
|
|
11
|
+
getBySlug: (slug, query) => base.getBySlug(slug, { ...defaults, ...(query ?? {}) }),
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FetchLike, HttpClient } from './types';
|
|
2
|
+
export interface HttpClientOptions {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
fetch?: FetchLike;
|
|
6
|
+
}
|
|
7
|
+
export declare const createHttpClient: ({ baseURL, token, fetch: fetchImpl }: HttpClientOptions) => HttpClient;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import qs from 'qs';
|
|
2
|
+
const buildUrl = (baseURL, path, query) => {
|
|
3
|
+
const normalized = path.startsWith('/') ? path : `/${path}`;
|
|
4
|
+
const qsPart = query && Object.keys(query).length ? `?${qs.stringify(query, { encodeValuesOnly: true })}` : '';
|
|
5
|
+
return `${baseURL}${normalized}${qsPart}`;
|
|
6
|
+
};
|
|
7
|
+
export const createHttpClient = ({ baseURL, token, fetch: fetchImpl }) => {
|
|
8
|
+
const f = fetchImpl ?? fetch;
|
|
9
|
+
const request = async (method, path, body, options) => {
|
|
10
|
+
const url = buildUrl(baseURL, path, options?.query);
|
|
11
|
+
const headers = {
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
14
|
+
...(options?.headers ?? {}),
|
|
15
|
+
};
|
|
16
|
+
const res = await f(url, {
|
|
17
|
+
method,
|
|
18
|
+
headers,
|
|
19
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const message = await res.text().catch(() => res.statusText);
|
|
23
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}: ${message}`);
|
|
24
|
+
}
|
|
25
|
+
return (await res.json());
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
get: (path, options) => request('GET', path, undefined, options),
|
|
29
|
+
post: (path, body, options) => request('POST', path, body, options),
|
|
30
|
+
put: (path, body, options) => request('PUT', path, body, options),
|
|
31
|
+
delete: (path, options) => request('DELETE', path, undefined, options),
|
|
32
|
+
};
|
|
33
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/media.d.ts
ADDED
package/dist/media.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts Strapi media URLs to absolute URLs based on the API base URL.
|
|
3
|
+
*/
|
|
4
|
+
export const resolveMediaUrl = (url, apiBase) => {
|
|
5
|
+
if (!url)
|
|
6
|
+
return null;
|
|
7
|
+
if (url.startsWith('http'))
|
|
8
|
+
return url;
|
|
9
|
+
if (!apiBase)
|
|
10
|
+
return url;
|
|
11
|
+
return `${apiBase}${url}`;
|
|
12
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type FetchLike = typeof fetch;
|
|
2
|
+
export interface HttpRequestOptions {
|
|
3
|
+
query?: Record<string, unknown>;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export interface HttpClient {
|
|
7
|
+
get<T = unknown>(path: string, options?: HttpRequestOptions): Promise<T>;
|
|
8
|
+
post<T = unknown>(path: string, body?: unknown, options?: HttpRequestOptions): Promise<T>;
|
|
9
|
+
put<T = unknown>(path: string, body?: unknown, options?: HttpRequestOptions): Promise<T>;
|
|
10
|
+
delete<T = unknown>(path: string, options?: HttpRequestOptions): Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
export interface RepositoryQuery {
|
|
13
|
+
filters?: Record<string, unknown>;
|
|
14
|
+
populate?: '*' | Record<string, unknown> | Array<string | Record<string, unknown>>;
|
|
15
|
+
sort?: string[] | string;
|
|
16
|
+
pagination?: {
|
|
17
|
+
page?: number;
|
|
18
|
+
pageSize?: number;
|
|
19
|
+
start?: number;
|
|
20
|
+
limit?: number;
|
|
21
|
+
};
|
|
22
|
+
fields?: string[];
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface StrapiListResponse<T> {
|
|
26
|
+
data: T[];
|
|
27
|
+
meta: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export interface StrapiSingleResponse<T> {
|
|
30
|
+
data: T | null;
|
|
31
|
+
meta: Record<string, unknown>;
|
|
32
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "strapi-rest-lite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"qs": "^6.12.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/qs": "^6.9.16"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"node-fetch": "^3.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|