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 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
+ };
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './http-client';
3
+ export * from './collection-repository';
4
+ export * from './collection-service';
5
+ export * from './media';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './http-client';
3
+ export * from './collection-repository';
4
+ export * from './collection-service';
5
+ export * from './media';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Converts Strapi media URLs to absolute URLs based on the API base URL.
3
+ */
4
+ export declare const resolveMediaUrl: (url?: string | null, apiBase?: string) => string | null;
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
+ };
@@ -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
+ }