@biblioteksentralen/bmdb-search 0.0.0-beta.6 → 0.0.0-beta.8
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 +12 -38
- package/dist/index.cjs +7 -1
- package/dist/index.d.cts +110 -3
- package/dist/index.d.ts +110 -3
- package/dist/index.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,8 +6,7 @@ TypeScript client for searching Bibliotekenes metadatabrønn (BMDB) using [opena
|
|
|
6
6
|
|
|
7
7
|
### Client identification policy
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
name and a contact address in the `clientIdentifier` string. We will only contact you about usage of the API.
|
|
9
|
+
Clients should identify themselves using a descriptive name and a contact address in the `clientIdentifier` string. We will only contact you about usage of the API.
|
|
11
10
|
|
|
12
11
|
## Usage examples
|
|
13
12
|
|
|
@@ -16,54 +15,29 @@ name and a contact address in the `clientIdentifier` string. We will only contac
|
|
|
16
15
|
```typescript
|
|
17
16
|
import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search";
|
|
18
17
|
|
|
19
|
-
const bmdbSearchClient = createBmdbFetchClient({
|
|
18
|
+
const bmdbSearchClient = createBmdbFetchClient({
|
|
19
|
+
clientIdentifier: "client-unique-description",
|
|
20
|
+
host: "https://search.data.bs.no",
|
|
21
|
+
});
|
|
22
|
+
|
|
20
23
|
const { data, error } = await bmdbSearchClient.GET("/works/search", { params: { query: { query: "test" } } });
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
The arguments and response are fully typed.
|
|
24
27
|
|
|
25
|
-
### Usage with tanstack-query, openapi-react-query and
|
|
28
|
+
### Usage with tanstack-query, openapi-react-query and React hooks
|
|
26
29
|
|
|
27
30
|
Using [tanstack](https://tanstack.com/query/latest) for fetching with state management and [openapi-react-query](https://openapi-ts.dev/openapi-react-query/) for typing.
|
|
28
31
|
|
|
29
|
-
Add a rewrite in the next.js config file to be able to use the frontend host without violating the content service policy:
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
// next.config.js or similar
|
|
33
|
-
const searchApiHost = "https://search.data.bs.no";
|
|
34
|
-
|
|
35
|
-
const config = {
|
|
36
|
-
// ... other config ...
|
|
37
|
-
rewrites() {
|
|
38
|
-
return [
|
|
39
|
-
{
|
|
40
|
-
source: "/bmdb/api/:path*",
|
|
41
|
-
destination: `${searchApiHost}/bmdb/api/:path*`,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
If necessary, skip in middleware to exempt the API paths from internationalization etc:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
// middleware.ts
|
|
52
|
-
export const config = {
|
|
53
|
-
matcher: [
|
|
54
|
-
// ...other
|
|
55
|
-
"/((?!|bmdb/api|_next/static).*)",
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Create hooks and fetch data:
|
|
61
|
-
|
|
62
32
|
```typescript
|
|
63
33
|
import { createBmdbFetchClient } from "@biblioteksentralen/bmdb-search";
|
|
64
34
|
import createReactQueryClient from "openapi-react-query";
|
|
65
35
|
|
|
66
|
-
const bmdbSearchClient = createBmdbFetchClient({
|
|
36
|
+
const bmdbSearchClient = createBmdbFetchClient({
|
|
37
|
+
clientIdentifier: "client-unique-description",
|
|
38
|
+
host: "https://search.data.bs.no"
|
|
39
|
+
});
|
|
40
|
+
|
|
67
41
|
const { useQuery } = createReactQueryClient(bmdbSearchClient);
|
|
68
42
|
|
|
69
43
|
const Component = () => {
|
package/dist/index.cjs
CHANGED
|
@@ -19,10 +19,16 @@ var formatSingleTitle = ({ mainTitle, subtitle, partNumber, partTitle }, options
|
|
|
19
19
|
var formatUniformTitleMusic = ({ form, instrumentation, serialNumber, opusNumber, thematicCatalogueNumber, part, key, arrangement }) => form + (instrumentation ? ` ${instrumentation}` : "") + (serialNumber ? `, ${serialNumber}` : "") + (opusNumber ? `, ${opusNumber}` : "") + (thematicCatalogueNumber ? `, ${thematicCatalogueNumber}` : "") + (part ? `, ${part}` : "") + (key ? `, ${key}` : "") + (arrangement ? `, ${arrangement}` : "");
|
|
20
20
|
var createBmdbFetchClient = ({
|
|
21
21
|
clientIdentifier,
|
|
22
|
+
host,
|
|
22
23
|
version = "v1",
|
|
23
24
|
...options
|
|
24
25
|
}) => {
|
|
25
|
-
|
|
26
|
+
try {
|
|
27
|
+
new URL(host);
|
|
28
|
+
} catch {
|
|
29
|
+
throw new Error(`Invalid host URL: ${host}. The URL must be fully qualified with protocol.`);
|
|
30
|
+
}
|
|
31
|
+
const baseUrl = `${host.replace(/(.*)\/$/, "$1")}/bmdb/api/${version}/`;
|
|
26
32
|
const client = createFetchClient__default.default({
|
|
27
33
|
baseUrl,
|
|
28
34
|
querySerializer: { array: { style: "form", explode: false } },
|
package/dist/index.d.cts
CHANGED
|
@@ -26,6 +26,27 @@ interface paths {
|
|
|
26
26
|
patch?: never;
|
|
27
27
|
trace?: never;
|
|
28
28
|
};
|
|
29
|
+
"/entities/search": {
|
|
30
|
+
parameters: {
|
|
31
|
+
query?: never;
|
|
32
|
+
header?: never;
|
|
33
|
+
path?: never;
|
|
34
|
+
cookie?: never;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Search entities
|
|
38
|
+
* @description Endpoint for searching BMDB entites. Implemented: agents.
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
get: operations["searchEntities"];
|
|
42
|
+
put?: never;
|
|
43
|
+
post?: never;
|
|
44
|
+
delete?: never;
|
|
45
|
+
options?: never;
|
|
46
|
+
head?: never;
|
|
47
|
+
patch?: never;
|
|
48
|
+
trace?: never;
|
|
49
|
+
};
|
|
29
50
|
}
|
|
30
51
|
interface components {
|
|
31
52
|
schemas: {
|
|
@@ -1084,6 +1105,52 @@ interface components {
|
|
|
1084
1105
|
advancedQueryErrors?: components["schemas"]["AdvanvedQueryError"][];
|
|
1085
1106
|
sorting: components["schemas"]["WorkSort"];
|
|
1086
1107
|
};
|
|
1108
|
+
/**
|
|
1109
|
+
* @description Facet type, this is the value given to the search facet parameter. The facet should always exists in as a corresponding filter.
|
|
1110
|
+
*
|
|
1111
|
+
* @enum {string}
|
|
1112
|
+
*/
|
|
1113
|
+
EntityFacetType: "agent.nationality";
|
|
1114
|
+
/**
|
|
1115
|
+
* @description Sorting type, this is the value given to the search facet parameter.
|
|
1116
|
+
*
|
|
1117
|
+
* @enum {string}
|
|
1118
|
+
*/
|
|
1119
|
+
EntitySort: "relevance.desc" | "updateTime.desc" | "updateTime.asc";
|
|
1120
|
+
EntityFilter: {
|
|
1121
|
+
/** @enum {string} */
|
|
1122
|
+
"type"?: "agent" | "place";
|
|
1123
|
+
"identifier"?: string;
|
|
1124
|
+
"agent.id"?: string;
|
|
1125
|
+
"agent.noraf_id"?: string;
|
|
1126
|
+
"agent.bokbasen_id"?: string;
|
|
1127
|
+
"agent.isni_id"?: string;
|
|
1128
|
+
};
|
|
1129
|
+
EntityFacet: {
|
|
1130
|
+
type: components["schemas"]["EntityFacetType"];
|
|
1131
|
+
/** @description A displayable (human readable) facet name. */
|
|
1132
|
+
name?: string;
|
|
1133
|
+
terms: components["schemas"]["FacetTerm"][];
|
|
1134
|
+
};
|
|
1135
|
+
GetEntitySearch200Response: {
|
|
1136
|
+
/** @description The total number of results in the whole search result. */
|
|
1137
|
+
total?: number;
|
|
1138
|
+
results: {
|
|
1139
|
+
entity?: components["schemas"]["Person"] | components["schemas"]["CollectiveAgent"] | components["schemas"]["Place"];
|
|
1140
|
+
}[];
|
|
1141
|
+
/** @description If this is false, we haven't reached the end of the search result yet, if it's false we have, and if it's missing or null we don't know. */
|
|
1142
|
+
endOfResults: boolean;
|
|
1143
|
+
/** @description This array contains the requested facets, and their terms. Each facet type should only appear once in the array. */
|
|
1144
|
+
facets?: components["schemas"]["EntityFacet"][];
|
|
1145
|
+
/**
|
|
1146
|
+
* @description Indicates what kind of search was performed, based on parsed input query.
|
|
1147
|
+
* @enum {string}
|
|
1148
|
+
*/
|
|
1149
|
+
queryType?: "empty" | "standard" | "advanced";
|
|
1150
|
+
/** @description Parsing errors from advanced query. When they occur, the request is treated as a standard search. */
|
|
1151
|
+
advancedQueryErrors?: components["schemas"]["AdvanvedQueryError"][];
|
|
1152
|
+
sorting: components["schemas"]["EntitySort"];
|
|
1153
|
+
};
|
|
1087
1154
|
};
|
|
1088
1155
|
responses: never;
|
|
1089
1156
|
parameters: never;
|
|
@@ -1102,10 +1169,10 @@ interface operations {
|
|
|
1102
1169
|
* This is important to consider in any product design.
|
|
1103
1170
|
* */
|
|
1104
1171
|
size?: number;
|
|
1105
|
-
/** @description Page number (
|
|
1172
|
+
/** @description Page number (default 1). */
|
|
1106
1173
|
page?: number;
|
|
1107
1174
|
facet?: components["schemas"]["WorkFacetType"][];
|
|
1108
|
-
/** @description Retun this number of facet terms for each requested facet (
|
|
1175
|
+
/** @description Retun this number of facet terms for each requested facet (default 10). */
|
|
1109
1176
|
facet_terms_size?: number;
|
|
1110
1177
|
/** @description Sorting */
|
|
1111
1178
|
sort?: components["schemas"]["WorkSort"];
|
|
@@ -1131,6 +1198,45 @@ interface operations {
|
|
|
1131
1198
|
};
|
|
1132
1199
|
};
|
|
1133
1200
|
};
|
|
1201
|
+
searchEntities: {
|
|
1202
|
+
parameters: {
|
|
1203
|
+
query?: {
|
|
1204
|
+
/** @description The search query. */
|
|
1205
|
+
query?: string;
|
|
1206
|
+
/** @description Page size (default 10).
|
|
1207
|
+
* It's important to be aware that, the page size here is only a preferred page size, the result set can be both small and greater, depending on the implementation from the LMS provider.
|
|
1208
|
+
* This is important to consider in any product design.
|
|
1209
|
+
* */
|
|
1210
|
+
size?: number;
|
|
1211
|
+
/** @description Page number (default 1). */
|
|
1212
|
+
page?: number;
|
|
1213
|
+
facet?: components["schemas"]["EntityFacetType"][];
|
|
1214
|
+
/** @description Retun this number of facet terms for each requested facet (default 10). */
|
|
1215
|
+
facet_terms_size?: number;
|
|
1216
|
+
/** @description Sorting */
|
|
1217
|
+
sort?: components["schemas"]["EntitySort"];
|
|
1218
|
+
/** @description Each filter accepts multiple comma-separated values that are OR'ed together.
|
|
1219
|
+
* The filters themselves are AND'ed together.
|
|
1220
|
+
* */
|
|
1221
|
+
filter?: components["schemas"]["EntityFilter"];
|
|
1222
|
+
};
|
|
1223
|
+
header?: never;
|
|
1224
|
+
path?: never;
|
|
1225
|
+
cookie?: never;
|
|
1226
|
+
};
|
|
1227
|
+
requestBody?: never;
|
|
1228
|
+
responses: {
|
|
1229
|
+
/** @description List of entities */
|
|
1230
|
+
200: {
|
|
1231
|
+
headers: {
|
|
1232
|
+
[name: string]: unknown;
|
|
1233
|
+
};
|
|
1234
|
+
content: {
|
|
1235
|
+
"application/json": components["schemas"]["GetEntitySearch200Response"];
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
1238
|
+
};
|
|
1239
|
+
};
|
|
1134
1240
|
}
|
|
1135
1241
|
|
|
1136
1242
|
type BmdbApiSchemas$1 = components["schemas"];
|
|
@@ -1151,10 +1257,11 @@ type BmdbApiPaths = paths;
|
|
|
1151
1257
|
|
|
1152
1258
|
type BmdbSearchClientOptions = ClientOptions & {
|
|
1153
1259
|
clientIdentifier: string;
|
|
1260
|
+
host: string;
|
|
1154
1261
|
version?: "v1";
|
|
1155
1262
|
};
|
|
1156
1263
|
type BmdbSearchClient = Client<BmdbApiPaths>;
|
|
1157
|
-
declare const createBmdbFetchClient: ({ clientIdentifier, version, ...options }: BmdbSearchClientOptions) => BmdbSearchClient;
|
|
1264
|
+
declare const createBmdbFetchClient: ({ clientIdentifier, host, version, ...options }: BmdbSearchClientOptions) => BmdbSearchClient;
|
|
1158
1265
|
|
|
1159
1266
|
type BmdbWork = BmdbApiSchemas["Work"];
|
|
1160
1267
|
type BmdbExpression = BmdbApiSchemas["Expression"];
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,27 @@ interface paths {
|
|
|
26
26
|
patch?: never;
|
|
27
27
|
trace?: never;
|
|
28
28
|
};
|
|
29
|
+
"/entities/search": {
|
|
30
|
+
parameters: {
|
|
31
|
+
query?: never;
|
|
32
|
+
header?: never;
|
|
33
|
+
path?: never;
|
|
34
|
+
cookie?: never;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Search entities
|
|
38
|
+
* @description Endpoint for searching BMDB entites. Implemented: agents.
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
get: operations["searchEntities"];
|
|
42
|
+
put?: never;
|
|
43
|
+
post?: never;
|
|
44
|
+
delete?: never;
|
|
45
|
+
options?: never;
|
|
46
|
+
head?: never;
|
|
47
|
+
patch?: never;
|
|
48
|
+
trace?: never;
|
|
49
|
+
};
|
|
29
50
|
}
|
|
30
51
|
interface components {
|
|
31
52
|
schemas: {
|
|
@@ -1084,6 +1105,52 @@ interface components {
|
|
|
1084
1105
|
advancedQueryErrors?: components["schemas"]["AdvanvedQueryError"][];
|
|
1085
1106
|
sorting: components["schemas"]["WorkSort"];
|
|
1086
1107
|
};
|
|
1108
|
+
/**
|
|
1109
|
+
* @description Facet type, this is the value given to the search facet parameter. The facet should always exists in as a corresponding filter.
|
|
1110
|
+
*
|
|
1111
|
+
* @enum {string}
|
|
1112
|
+
*/
|
|
1113
|
+
EntityFacetType: "agent.nationality";
|
|
1114
|
+
/**
|
|
1115
|
+
* @description Sorting type, this is the value given to the search facet parameter.
|
|
1116
|
+
*
|
|
1117
|
+
* @enum {string}
|
|
1118
|
+
*/
|
|
1119
|
+
EntitySort: "relevance.desc" | "updateTime.desc" | "updateTime.asc";
|
|
1120
|
+
EntityFilter: {
|
|
1121
|
+
/** @enum {string} */
|
|
1122
|
+
"type"?: "agent" | "place";
|
|
1123
|
+
"identifier"?: string;
|
|
1124
|
+
"agent.id"?: string;
|
|
1125
|
+
"agent.noraf_id"?: string;
|
|
1126
|
+
"agent.bokbasen_id"?: string;
|
|
1127
|
+
"agent.isni_id"?: string;
|
|
1128
|
+
};
|
|
1129
|
+
EntityFacet: {
|
|
1130
|
+
type: components["schemas"]["EntityFacetType"];
|
|
1131
|
+
/** @description A displayable (human readable) facet name. */
|
|
1132
|
+
name?: string;
|
|
1133
|
+
terms: components["schemas"]["FacetTerm"][];
|
|
1134
|
+
};
|
|
1135
|
+
GetEntitySearch200Response: {
|
|
1136
|
+
/** @description The total number of results in the whole search result. */
|
|
1137
|
+
total?: number;
|
|
1138
|
+
results: {
|
|
1139
|
+
entity?: components["schemas"]["Person"] | components["schemas"]["CollectiveAgent"] | components["schemas"]["Place"];
|
|
1140
|
+
}[];
|
|
1141
|
+
/** @description If this is false, we haven't reached the end of the search result yet, if it's false we have, and if it's missing or null we don't know. */
|
|
1142
|
+
endOfResults: boolean;
|
|
1143
|
+
/** @description This array contains the requested facets, and their terms. Each facet type should only appear once in the array. */
|
|
1144
|
+
facets?: components["schemas"]["EntityFacet"][];
|
|
1145
|
+
/**
|
|
1146
|
+
* @description Indicates what kind of search was performed, based on parsed input query.
|
|
1147
|
+
* @enum {string}
|
|
1148
|
+
*/
|
|
1149
|
+
queryType?: "empty" | "standard" | "advanced";
|
|
1150
|
+
/** @description Parsing errors from advanced query. When they occur, the request is treated as a standard search. */
|
|
1151
|
+
advancedQueryErrors?: components["schemas"]["AdvanvedQueryError"][];
|
|
1152
|
+
sorting: components["schemas"]["EntitySort"];
|
|
1153
|
+
};
|
|
1087
1154
|
};
|
|
1088
1155
|
responses: never;
|
|
1089
1156
|
parameters: never;
|
|
@@ -1102,10 +1169,10 @@ interface operations {
|
|
|
1102
1169
|
* This is important to consider in any product design.
|
|
1103
1170
|
* */
|
|
1104
1171
|
size?: number;
|
|
1105
|
-
/** @description Page number (
|
|
1172
|
+
/** @description Page number (default 1). */
|
|
1106
1173
|
page?: number;
|
|
1107
1174
|
facet?: components["schemas"]["WorkFacetType"][];
|
|
1108
|
-
/** @description Retun this number of facet terms for each requested facet (
|
|
1175
|
+
/** @description Retun this number of facet terms for each requested facet (default 10). */
|
|
1109
1176
|
facet_terms_size?: number;
|
|
1110
1177
|
/** @description Sorting */
|
|
1111
1178
|
sort?: components["schemas"]["WorkSort"];
|
|
@@ -1131,6 +1198,45 @@ interface operations {
|
|
|
1131
1198
|
};
|
|
1132
1199
|
};
|
|
1133
1200
|
};
|
|
1201
|
+
searchEntities: {
|
|
1202
|
+
parameters: {
|
|
1203
|
+
query?: {
|
|
1204
|
+
/** @description The search query. */
|
|
1205
|
+
query?: string;
|
|
1206
|
+
/** @description Page size (default 10).
|
|
1207
|
+
* It's important to be aware that, the page size here is only a preferred page size, the result set can be both small and greater, depending on the implementation from the LMS provider.
|
|
1208
|
+
* This is important to consider in any product design.
|
|
1209
|
+
* */
|
|
1210
|
+
size?: number;
|
|
1211
|
+
/** @description Page number (default 1). */
|
|
1212
|
+
page?: number;
|
|
1213
|
+
facet?: components["schemas"]["EntityFacetType"][];
|
|
1214
|
+
/** @description Retun this number of facet terms for each requested facet (default 10). */
|
|
1215
|
+
facet_terms_size?: number;
|
|
1216
|
+
/** @description Sorting */
|
|
1217
|
+
sort?: components["schemas"]["EntitySort"];
|
|
1218
|
+
/** @description Each filter accepts multiple comma-separated values that are OR'ed together.
|
|
1219
|
+
* The filters themselves are AND'ed together.
|
|
1220
|
+
* */
|
|
1221
|
+
filter?: components["schemas"]["EntityFilter"];
|
|
1222
|
+
};
|
|
1223
|
+
header?: never;
|
|
1224
|
+
path?: never;
|
|
1225
|
+
cookie?: never;
|
|
1226
|
+
};
|
|
1227
|
+
requestBody?: never;
|
|
1228
|
+
responses: {
|
|
1229
|
+
/** @description List of entities */
|
|
1230
|
+
200: {
|
|
1231
|
+
headers: {
|
|
1232
|
+
[name: string]: unknown;
|
|
1233
|
+
};
|
|
1234
|
+
content: {
|
|
1235
|
+
"application/json": components["schemas"]["GetEntitySearch200Response"];
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
1238
|
+
};
|
|
1239
|
+
};
|
|
1134
1240
|
}
|
|
1135
1241
|
|
|
1136
1242
|
type BmdbApiSchemas$1 = components["schemas"];
|
|
@@ -1151,10 +1257,11 @@ type BmdbApiPaths = paths;
|
|
|
1151
1257
|
|
|
1152
1258
|
type BmdbSearchClientOptions = ClientOptions & {
|
|
1153
1259
|
clientIdentifier: string;
|
|
1260
|
+
host: string;
|
|
1154
1261
|
version?: "v1";
|
|
1155
1262
|
};
|
|
1156
1263
|
type BmdbSearchClient = Client<BmdbApiPaths>;
|
|
1157
|
-
declare const createBmdbFetchClient: ({ clientIdentifier, version, ...options }: BmdbSearchClientOptions) => BmdbSearchClient;
|
|
1264
|
+
declare const createBmdbFetchClient: ({ clientIdentifier, host, version, ...options }: BmdbSearchClientOptions) => BmdbSearchClient;
|
|
1158
1265
|
|
|
1159
1266
|
type BmdbWork = BmdbApiSchemas["Work"];
|
|
1160
1267
|
type BmdbExpression = BmdbApiSchemas["Expression"];
|
package/dist/index.js
CHANGED
|
@@ -13,10 +13,16 @@ var formatSingleTitle = ({ mainTitle, subtitle, partNumber, partTitle }, options
|
|
|
13
13
|
var formatUniformTitleMusic = ({ form, instrumentation, serialNumber, opusNumber, thematicCatalogueNumber, part, key, arrangement }) => form + (instrumentation ? ` ${instrumentation}` : "") + (serialNumber ? `, ${serialNumber}` : "") + (opusNumber ? `, ${opusNumber}` : "") + (thematicCatalogueNumber ? `, ${thematicCatalogueNumber}` : "") + (part ? `, ${part}` : "") + (key ? `, ${key}` : "") + (arrangement ? `, ${arrangement}` : "");
|
|
14
14
|
var createBmdbFetchClient = ({
|
|
15
15
|
clientIdentifier,
|
|
16
|
+
host,
|
|
16
17
|
version = "v1",
|
|
17
18
|
...options
|
|
18
19
|
}) => {
|
|
19
|
-
|
|
20
|
+
try {
|
|
21
|
+
new URL(host);
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error(`Invalid host URL: ${host}. The URL must be fully qualified with protocol.`);
|
|
24
|
+
}
|
|
25
|
+
const baseUrl = `${host.replace(/(.*)\/$/, "$1")}/bmdb/api/${version}/`;
|
|
20
26
|
const client = createFetchClient({
|
|
21
27
|
baseUrl,
|
|
22
28
|
querySerializer: { array: { style: "form", explode: false } },
|