Skip to content

Commit 5c4f589

Browse files
Merge #1081
1081: Provide the possibility to give your own httpClient and request configs r=bidoubiwa a=bidoubiwa Fixes: #1060 Fixes: #928 Co-authored-by: Charlotte Vermandel <[email protected]>
2 parents ce8e9c4 + 8ae81a7 commit 5c4f589

File tree

8 files changed

+208
-6
lines changed

8 files changed

+208
-6
lines changed

.changeset/curvy-ravens-walk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@meilisearch/instant-meilisearch": patch
3+
---
4+
5+
Add the possibility to provide a custom HTTP request and custom requests configurations

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ const searchClient = instantMeiliSearch(
9494
- [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`).
9595
- [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`).
9696
- [`matchingStrategy`](#matching-strategy): Determine the search strategy on words matching (default `last`).
97+
- [`requestConfig`](#request-config): Use custom request configurations.
98+
- ['httpClient'](#custom-http-client): Use a custom HTTP client.
9799

98100
The options are added as the third parameter of the `instantMeilisearch` function.
99101

@@ -178,6 +180,41 @@ The other strategy is `all`, where both `hello` and `world` **must** be present
178180
}
179181
```
180182

183+
### Request Config
184+
185+
You can provide a custom request configuration. Available field can be [found here](https://fetch.spec.whatwg.org/#requestinit).
186+
187+
for example, with custom headers.
188+
189+
```ts
190+
{
191+
requestConfig: {
192+
headers: {
193+
Authorization: AUTH_TOKEN
194+
},
195+
credentials: 'include'
196+
}
197+
}
198+
```
199+
200+
### Custom HTTP client
201+
202+
You can use your own HTTP client, for example, with [`axios`](https://github.com/axios/axios).
203+
204+
```ts
205+
{
206+
httpClient: async (url, opts) => {
207+
const response = await $axios.request({
208+
url,
209+
data: opts?.body,
210+
headers: opts?.headers,
211+
method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
212+
})
213+
return response.data
214+
}
215+
}
216+
```
217+
181218
## 🪡 Example with InstantSearch
182219

183220
The open-source [InstantSearch](https://www.algolia.com/doc/api-reference/widgets/js/) library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment.

packages/instant-meilisearch/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ const searchClient = instantMeiliSearch(
9494
- [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`).
9595
- [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`).
9696
- [`matchingStrategy`](#matching-strategy): Determine the search strategy on words matching (default `last`).
97+
- [`requestConfig`](#request-config): Use custom request configurations.
98+
- ['httpClient'](#custom-http-client): Use a custom HTTP client.
9799

98100
The options are added as the third parameter of the `instantMeilisearch` function.
99101

@@ -178,6 +180,41 @@ The other strategy is `all`, where both `hello` and `world` **must** be present
178180
}
179181
```
180182

183+
### Request Config
184+
185+
You can provide a custom request configuration. Available field can be [found here](https://fetch.spec.whatwg.org/#requestinit).
186+
187+
for example, with custom headers.
188+
189+
```ts
190+
{
191+
requestConfig: {
192+
headers: {
193+
Authorization: AUTH_TOKEN
194+
},
195+
credentials: 'include'
196+
}
197+
}
198+
```
199+
200+
### Custom HTTP client
201+
202+
You can use your own HTTP client, for example, with [`axios`](https://github.com/axios/axios).
203+
204+
```ts
205+
{
206+
httpClient: async (url, opts) => {
207+
const response = await $axios.request({
208+
url,
209+
data: opts?.body,
210+
headers: opts?.headers,
211+
method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
212+
})
213+
return response.data
214+
}
215+
}
216+
```
217+
181218
## 🪡 Example with InstantSearch
182219

183220
The open-source [InstantSearch](https://www.algolia.com/doc/api-reference/widgets/js/) library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { instantMeiliSearch } from '../src'
2+
import { meilisearchClient, dataset } from './assets/utils'
3+
4+
describe('Custom HTTP client tests', () => {
5+
beforeAll(async () => {
6+
const deleteTask = await meilisearchClient.deleteIndex('movies')
7+
await meilisearchClient.waitForTask(deleteTask.taskUid)
8+
9+
const documentsTask = await meilisearchClient
10+
.index('movies')
11+
.addDocuments(dataset)
12+
await meilisearchClient.index('movies').waitForTask(documentsTask.taskUid)
13+
})
14+
15+
test('a custom HTTP client', async () => {
16+
const httpClient = jest.fn(async (url: string, init?: RequestInit) => {
17+
const result = await fetch(url, init)
18+
return await result.json()
19+
})
20+
21+
const searchClient = instantMeiliSearch(
22+
'http://localhost:7700',
23+
'masterKey',
24+
{
25+
httpClient,
26+
}
27+
)
28+
29+
const response = await searchClient.search([
30+
{
31+
indexName: 'movies',
32+
params: {
33+
attributesToRetrieve: [],
34+
query: 'ariel',
35+
},
36+
},
37+
])
38+
expect(httpClient).toHaveBeenCalledTimes(2)
39+
expect(response.results[0].hits.length).toEqual(1)
40+
})
41+
})

packages/instant-meilisearch/__tests__/instantiation.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,58 @@ describe('InstantMeiliSearch instantiation', () => {
4040
})
4141
}).toThrow(TypeError)
4242
})
43+
44+
test('instantiation with custom request config with correct type', () => {
45+
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
46+
requestConfig: {},
47+
})
48+
49+
expect(searchClient).toBeTruthy()
50+
})
51+
52+
test('instantiation with custom request config set to undefined', () => {
53+
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
54+
requestConfig: undefined,
55+
})
56+
57+
expect(searchClient).toBeTruthy()
58+
})
59+
60+
test('instantiation with custom request config set to a string', () => {
61+
expect(() => {
62+
instantMeiliSearch('http://localhost:7700', '', {
63+
// @ts-expect-error
64+
requestConfig: '',
65+
})
66+
}).toThrow('Provided requestConfig should be an object')
67+
})
68+
69+
test('instantiation with custom HTTP client with correct type', () => {
70+
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
71+
httpClient: async () => {
72+
return new Promise((resolve) => {
73+
resolve({})
74+
})
75+
},
76+
})
77+
78+
expect(searchClient).toBeTruthy()
79+
})
80+
81+
test('instantiation with custom HTTP client set to undefined', () => {
82+
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
83+
httpClient: undefined,
84+
})
85+
86+
expect(searchClient).toBeTruthy()
87+
})
88+
89+
test('instantiation with custom HTTP client set to a string', () => {
90+
expect(() => {
91+
instantMeiliSearch('http://localhost:7700', '', {
92+
// @ts-expect-error
93+
httpClient: 'wrong type',
94+
})
95+
}).toThrow('Provided custom httpClient should be a function')
96+
})
4397
})

packages/instant-meilisearch/src/client/config/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
InstantMeiliSearchOptions,
33
InstantMeiliSearchConfig,
44
} from '../../types'
5+
import { isPureObject } from '../../utils/object'
56

67
/**
78
* Get the configuration of instant meilisearch
@@ -54,8 +55,10 @@ export function getApiKey(apiKey: string | (() => string)): string {
5455
*/
5556
export function validateInstantMeiliSearchParams(
5657
hostUrl: string,
57-
apiKey: string | (() => string)
58+
apiKey: string | (() => string),
59+
instantMeiliSearchOptions: InstantMeiliSearchOptions
5860
) {
61+
const { requestConfig, httpClient } = instantMeiliSearchOptions
5962
// Validate host url
6063
if (typeof hostUrl !== 'string') {
6164
throw new TypeError(
@@ -69,4 +72,14 @@ export function validateInstantMeiliSearchParams(
6972
'Provided apiKey value (2nd parameter) is not a string or a function, expected string or function'
7073
)
7174
}
75+
76+
// Validate requestConfig
77+
if (requestConfig !== undefined && !isPureObject(requestConfig)) {
78+
throw new TypeError('Provided requestConfig should be an object')
79+
}
80+
81+
// Validate custom HTTP client
82+
if (httpClient && typeof httpClient !== 'function') {
83+
throw new TypeError('Provided custom httpClient should be a function')
84+
}
7285
}

packages/instant-meilisearch/src/client/instant-meilisearch-client.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
SearchContext,
88
FacetDistribution,
99
PaginationState,
10+
MeilisearchConfig,
1011
} from '../types'
1112
import {
1213
getApiKey,
@@ -39,7 +40,7 @@ export function instantMeiliSearch(
3940
instantMeiliSearchOptions: InstantMeiliSearchOptions = {}
4041
): InstantMeiliSearchInstance {
4142
// Validate parameters
42-
validateInstantMeiliSearchParams(hostUrl, apiKey)
43+
validateInstantMeiliSearchParams(hostUrl, apiKey, instantMeiliSearchOptions)
4344

4445
// Resolve possible function to get apiKey
4546
apiKey = getApiKey(apiKey)
@@ -48,11 +49,21 @@ export function instantMeiliSearch(
4849
instantMeiliSearchOptions.clientAgents
4950
)
5051

51-
const meilisearchClient = new MeiliSearch({
52+
const meilisearchConfig: MeilisearchConfig = {
5253
host: hostUrl,
5354
apiKey,
5455
clientAgents,
55-
})
56+
}
57+
58+
if (instantMeiliSearchOptions.httpClient !== undefined) {
59+
meilisearchConfig.httpClient = instantMeiliSearchOptions.httpClient
60+
}
61+
62+
if (instantMeiliSearchOptions.requestConfig !== undefined) {
63+
meilisearchConfig.requestConfig = instantMeiliSearchOptions.requestConfig
64+
}
65+
66+
const meilisearchClient = new MeiliSearch(meilisearchConfig)
5667

5768
const searchCache = SearchCache()
5869
// create search resolver with included cache

packages/instant-meilisearch/src/types/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { SearchClient } from 'instantsearch.js'
22
import type { MultipleQueriesQuery as AlgoliaMultipleQueriesQuery } from '@algolia/client-search'
3-
43
import type {
54
MultiSearchQuery as MeiliSearchMultiSearchParams,
65
MultiSearchResult,
6+
Config as MeilisearchConfig,
77
} from 'meilisearch'
88

99
export type { AlgoliaMultipleQueriesQuery, MultiSearchResult }
@@ -15,6 +15,7 @@ export type {
1515
MeiliSearch,
1616
FacetStats as MeiliFacetStats,
1717
MultiSearchQuery as MeiliSearchMultiSearchParams,
18+
Config as MeilisearchConfig,
1819
} from 'meilisearch'
1920

2021
export type InstantSearchParams = AlgoliaMultipleQueriesQuery['params']
@@ -24,7 +25,10 @@ export const enum MatchingStrategies {
2425
LAST = 'last',
2526
}
2627

27-
export type InstantMeiliSearchOptions = {
28+
export type InstantMeiliSearchOptions = Pick<
29+
MeilisearchConfig,
30+
'requestConfig' | 'httpClient'
31+
> & {
2832
placeholderSearch?: boolean
2933
primaryKey?: string
3034
keepZeroFacets?: boolean

0 commit comments

Comments
 (0)