Skip to content

Commit 41b31e6

Browse files
authored
feat(content-types): allow custom root paths for CT managers (#74)
2 parents aa35ccb + f419957 commit 41b31e6

File tree

13 files changed

+498
-196
lines changed

13 files changed

+498
-196
lines changed

README.md

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
- [Custom Headers](#custom-headers)
4242
3. [API Reference](#-api-reference)
4343
4. [Resource Managers](#-resource-managers)
44-
- [`.collection()`](#collectionresource)
45-
- [`.single()`](#singleresource)
44+
- [`.collection()`](#collectionresource-options)
45+
- [`.single()`](#singleresource-options)
4646
- [`.files`](#files)
4747
5. [Debug](#-debug)
4848
6. [Demo Projects](#-demo-projects)
@@ -157,18 +157,23 @@ const articles = await client.collection('articles').find();
157157
The Strapi client library instance provides key properties and utility methods for content and API interaction:
158158

159159
- **`baseURL`**: base URL of your Strapi backend.
160-
- **`fetch`**: perform generic requests to the Strapi Content API using fetch-like syntax.
161-
- **`.collection(resource: string)`**: get a manager instance for handling collection-type resources.
162-
- **`.single(resource: string)`**: get a manager instance for handling single-type resources.
160+
- **`fetch()`**: perform generic requests to the Strapi Content API using fetch-like syntax.
161+
- **`collection()`**: get a manager instance for handling collection-type resources.
162+
- **`single()`**: get a manager instance for handling single-type resources.
163+
- **`files`**: access the files manager instance for handling common files operations.
163164

164165
## 📁 Resource Managers
165166

166-
### `.collection(resource)`
167+
### `.collection(resource, [options])`
167168

168169
The `.collection()` method provides a manager for working with collection-type resources,
169170
which can have multiple entries.
170171

171-
**Note**: the `resource` corresponds to the plural name of your collection type, as defined in the Strapi model.
172+
#### Params
173+
174+
- `resource`: `string` - plural name of your collection type, as defined in the Strapi model
175+
- `[options]`: `object` - additional options to pass to the collection type manager
176+
- `[path]`: `string` - optional root path override for the manager's queries
172177

173178
#### Available Methods:
174179

@@ -202,11 +207,21 @@ const updatedArticle = await articles.update('article-document-id', { title: 'Up
202207
await articles.delete('article-id');
203208
```
204209

205-
### `.single(resource)`
210+
You can also customize the root path for requests by providing a value for the `path` option:
211+
212+
```typescript
213+
const articles = client.collection('articles', { path: '/my-custom-path' });
214+
```
215+
216+
### `.single(resource, [options])`
206217

207218
The `.single()` method provides a manager for working with single-type resources, which have only one entry.
208219

209-
**Note**: the `resource` corresponds to the singular name of your collection type, as defined in the Strapi model.
220+
#### Params
221+
222+
- `resource`: `string` - singular name of your single type, as defined in the Strapi model
223+
- `[options]`: `object` - additional options to pass to the single type manager
224+
- `[path]`: `string` - optional root path override for the manager's queries
210225

211226
#### Available Methods:
212227

@@ -235,9 +250,15 @@ const updatedHomepage = await homepage.update(
235250
await homepage.delete();
236251
```
237252

238-
### .files
253+
You can also customize the root path for requests by providing a value for the `path` option:
239254

240-
The `files` property provides access to the Strapi Media Library through the Upload plugin. It allows you to retrieve files metadata without directly interacting with the REST API.
255+
```typescript
256+
const homepage = client.single('homepage', { path: '/my-custom-path' });
257+
```
258+
259+
### `.files`
260+
261+
The `files` property provides access to the Strapi Media Library through the Upload plugin. It allows you to retrieve files metadata without directly interacting with the REST API manually.
241262

242263
#### Methods
243264

@@ -246,7 +267,9 @@ The `files` property provides access to the Strapi Media Library through the Upl
246267
- `update(fileId: number, fileInfo: FileUpdateData): Promise<FileResponse>` - Updates metadata for an existing file
247268
- `delete(fileId: number): Promise<void>` - Deletes a file by its ID
248269

249-
#### Example: Finding Files
270+
#### Examples
271+
272+
**Finding all files**
250273

251274
```typescript
252275
// Initialize the client
@@ -269,7 +292,7 @@ const imageFiles = await client.files.find({
269292
});
270293
```
271294

272-
#### Example: Finding a Single File
295+
**Finding a Single File**
273296

274297
```typescript
275298
// Initialize the client
@@ -286,7 +309,7 @@ console.log(file.url); // The file URL
286309
console.log(file.mime); // The file MIME type
287310
```
288311

289-
#### Example: Updating File Metadata
312+
**Updating File Metadata**
290313

291314
```typescript
292315
// Initialize the client
@@ -306,7 +329,7 @@ console.log(updatedFile.name); // Updated file name
306329
console.log(updatedFile.alternativeText); // Updated alt text
307330
```
308331

309-
#### Example: Deleting a File
332+
**Deleting a File**
310333

311334
```typescript
312335
// Initialize the client
@@ -387,6 +410,7 @@ This repository includes demo projects located in the `/demo` directory to help
387410
- **`demo/node-typescript`**: a Node.js project using TypeScript.
388411
- **`demo/node-javascript`**: a Node.js project using JavaScript.
389412
- **`demo/next-server-components`**: a Next.js project using TypeScript and server components.
413+
- **`demo/react-vite`**: a React project using Vite and TypeScript
390414

391415
### Using Demo Commands
392416

demo/react-vite/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"framer-motion": "^12.6.3",
1616
"react": "^19.0.0",
1717
"react-dom": "^19.0.0",
18+
"react-hot-toast": "^2.5.2",
1819
"react-router-dom": "^7.5.0",
1920
"tailwindcss": "^4.1.3"
2021
},

demo/react-vite/pnpm-lock.yaml

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo/react-vite/src/app.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
2-
3-
import { Home } from '@/pages/Home.tsx';
41
import { CollectionDemo } from '@/pages/CollectionDemo.tsx';
52
import { FilesDemo } from '@/pages/FilesDemo.tsx';
63

4+
import { Home } from '@/pages/Home.tsx';
5+
import { Toaster } from 'react-hot-toast';
6+
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
7+
78
export default function App() {
89
return (
9-
<Router>
10-
<Routes>
11-
<Route path="/demos/collections" element={<CollectionDemo />} />
12-
<Route path="/demos/files" element={<FilesDemo />} />
13-
<Route path="/" element={<Home />} />
14-
</Routes>
15-
</Router>
10+
<>
11+
<Router>
12+
<Routes>
13+
<Route path="/demos/collections" element={<CollectionDemo />} />
14+
<Route path="/demos/files" element={<FilesDemo />} />
15+
<Route path="/" element={<Home />} />
16+
</Routes>
17+
</Router>
18+
<Toaster position="bottom-right" reverseOrder={false} />
19+
</>
1620
);
1721
}

demo/react-vite/src/hooks/useFiles.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useStrapi } from '@/hooks/useStrapi.ts';
22

33
import type { File } from '@/types.ts';
44
import React from 'react';
5+
import toast from 'react-hot-toast';
56

67
export const useFiles = () => {
78
const strapi = useStrapi();
@@ -11,8 +12,9 @@ export const useFiles = () => {
1112
try {
1213
const response = await strapi.files.find();
1314
setFiles(response);
15+
toast.success(`${response.length} files fetched successfully`);
1416
} catch (error) {
15-
console.error('Error fetching files:', error);
17+
toast.error(error instanceof Error ? error.message : `${error}`);
1618
}
1719
};
1820

demo/react-vite/src/pages/CollectionDemo.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Layout } from '@/layouts/Layout.tsx';
77
import type { Category, QueryParam } from '@/types.ts';
88
import { DEFAULT_COLLECTION_QUERIES } from '@/utils/constants.ts';
99
import React, { useEffect, useState } from 'react';
10+
import toast from 'react-hot-toast';
1011

1112
export const CollectionDemo: React.FC = () => {
1213
const categories = useCollection('categories');
@@ -31,8 +32,9 @@ export const CollectionDemo: React.FC = () => {
3132
try {
3233
const { data } = await categories.find(query);
3334
setDocuments(data as unknown as Category[]);
35+
toast.success(`${data.length} categories fetched successfully`);
3436
} catch (error) {
35-
console.error('Error fetching categories:', error);
37+
toast.error(error instanceof Error ? error.message : `${error}`);
3638
}
3739
};
3840

src/client.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { HttpClient } from './http';
88
import { AuthInterceptors, HttpInterceptors } from './interceptors';
99
import { StrapiConfigValidator } from './validators';
1010

11+
import type { ContentTypeManagerOptions } from './content-types/abstract';
1112
import type { HttpClientConfig } from './http';
1213

1314
const debug = createDebug('strapi:core');
@@ -306,18 +307,19 @@ export class StrapiClient {
306307
*
307308
* This instance provides methods for performing operations on the associated documents: create, read, update, delete.
308309
*
309-
* @param resource - The plural name of the collection to interact with.
310-
* This should match the collection name as defined in the Strapi app.
310+
* @param resource - The plural name of the collection to interact with.
311+
* This should match the collection name as defined in the Strapi app.
312+
* @param [options] - Optional parameter to specify additional configuration such as custom API path.
311313
*
312-
* @returns An instance of {@link CollectionTypeManager} targeting the given {@link resource} name.
314+
* @returns An instance of {@link CollectionTypeManager} for the given {@link resource}.
313315
*
314316
* @example
315317
* ```typescript
316318
* // Initialize the client with required configuration
317319
* const config = { baseURL: 'http://localhost:1337/api' };
318320
* const client = new Strapi(config);
319321
*
320-
* // Retrieve a CollectionTypeManager for the 'articles' resource
322+
* // Retrieve a CollectionTypeManager for the 'articles' collection
321323
* const articles = client.collection('articles');
322324
*
323325
* // Example: find all articles
@@ -334,13 +336,19 @@ export class StrapiClient {
334336
*
335337
* // Example: delete an article
336338
* await articles.delete('dde61ffb-00a6-4cc7-a61f-fb00a63cc740');
339+
*
340+
* // Example with a custom API path
341+
* const customArticles = client.collection('articles', { path: '/custom-articles-path' });
342+
* const customAllArticles = await customArticles.find();
337343
* ```
338344
*
339345
* @see CollectionTypeManager
340346
* @see StrapiClient
341347
*/
342-
collection(resource: string) {
343-
return new CollectionTypeManager(resource, this._httpClient);
348+
collection(resource: string, options: ClientCollectionOptions = {}) {
349+
const { path } = options;
350+
351+
return new CollectionTypeManager({ resource, path }, this._httpClient);
344352
}
345353

346354
/**
@@ -350,8 +358,9 @@ export class StrapiClient {
350358
*
351359
* @param resource - The singular name of the single-type resource to interact with.
352360
* This should match the single-type name as defined in the Strapi app.
361+
* @param [options] - Optional parameter to specify additional configuration such as custom API path.
353362
*
354-
* @returns An instance of {@link SingleTypeManager} targeting the given {@link resource} name.
363+
* @returns An instance of {@link SingleTypeManager} for the given {@link resource}.
355364
*
356365
* @example
357366
* ```typescript
@@ -369,12 +378,22 @@ export class StrapiClient {
369378
*
370379
* // Example: delete the homepage content
371380
* await homepage.delete();
381+
*
382+
* // Example with a custom API path
383+
* const customHomepage = client.single('homepage', { path: '/custom-homepage-path' });
384+
* const customHomepageDocument = await customHomepage.find();
372385
* ```
373386
*
374387
* @see SingleTypeManager
375388
* @see StrapiClient
376389
*/
377-
single(resource: string) {
378-
return new SingleTypeManager(resource, this._httpClient);
390+
single(resource: string, options: SingleCollectionOptions = {}) {
391+
const { path } = options;
392+
393+
return new SingleTypeManager({ resource, path }, this._httpClient);
379394
}
380395
}
396+
397+
// Local Client Types
398+
export type ClientCollectionOptions = Pick<ContentTypeManagerOptions, 'path'>;
399+
export type SingleCollectionOptions = Pick<ContentTypeManagerOptions, 'path'>;

0 commit comments

Comments
 (0)