Skip to content

Commit d65c967

Browse files
committed
feat: allow baseUrl override
1 parent 5405963 commit d65c967

File tree

7 files changed

+77
-33
lines changed

7 files changed

+77
-33
lines changed

.eslintignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
test/examples/*.ts
2-
dist
2+
dist
3+
build.js

README.md

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ npx openapi-typescript https://petstore.swagger.io/v2/swagger.json --output pets
3636
**Typed fetch client**
3737

3838
```ts
39-
import 'whatwg-fetch'
40-
4139
import { Fetcher } from 'openapi-typescript-fetch'
4240

4341
import { paths } from './petstore'
@@ -125,30 +123,6 @@ fetcher.configure({
125123
fetcher.use(logger)
126124
```
127125

128-
### Server Side Usage
129-
130-
This library can be used server side with [node-fetch](https://www.npmjs.com/package/node-fetch)
131-
132-
Node CommonJS setup
133-
```ts
134-
// install node-fetch v2
135-
npm install node-fetch@2
136-
npm install @types/node-fetch@2
137-
138-
// fetch-polyfill.ts
139-
import fetch, { Headers, Request, Response } from 'node-fetch'
140-
141-
if (!globalThis.fetch) {
142-
globalThis.fetch = fetch as any
143-
globalThis.Headers = Headers as any
144-
globalThis.Request = Request as any
145-
globalThis.Response = Response as any
146-
}
147-
148-
// index.ts
149-
import './fetch-polyfill'
150-
```
151-
152126
### Utility Types
153127

154128
- `OpArgType` - Infer argument type of an operation
@@ -190,4 +164,23 @@ const body = arrayRequestBody([{ item: 1}], { param: 2})
190164
// body type is { item: number }[] & { param: number }
191165
```
192166

167+
### Changing the baseUrl at runtime
168+
169+
The baseUrl can be configured with a function that returns the url at runtime
170+
171+
```ts
172+
fetcher.configure({
173+
baseUrl: () => getBaseUrl(...)
174+
})
175+
```
176+
177+
It can also be overriden per method invocation
178+
179+
```ts
180+
await findPetsByStatus(
181+
{ status: ['available', 'pending'] },
182+
{ baseUrl: "https://staging.petstore.swagger.io/v2" }
183+
)
184+
```
185+
193186
Happy fetching! 👍

src/fetcher.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ function wrapMiddlewares(middlewares: Middleware[], fetch: Fetch): Fetch {
183183
return fetch(url, init)
184184
}
185185
const current = middlewares[index]
186+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
186187
return await current!(url, init, (nextUrl, nextInit) =>
187188
handler(index + 1, nextUrl, nextInit),
188189
)
@@ -228,7 +229,7 @@ function createFetch<OP>(fetch: _TypedFetch<OP>): TypedFetch<OP> {
228229
}
229230

230231
function fetcher<Paths>() {
231-
let baseUrl = ''
232+
let baseUrl = '' as string | (() => string)
232233
let defaultInit: RequestInit = {}
233234
const middlewares: Middleware[] = []
234235
const fetch = wrapMiddlewares(middlewares, fetchJson)
@@ -246,15 +247,17 @@ function fetcher<Paths>() {
246247
create: ((queryParams?: Record<string, true | 1>) =>
247248
createFetch((payload, init) =>
248249
fetchUrl({
249-
baseUrl: baseUrl || '',
250+
baseUrl:
251+
init?.baseUrl ??
252+
(typeof baseUrl === 'function' ? baseUrl() : baseUrl),
250253
path: path as string,
251254
method: method as Method,
252255
queryParams: Object.keys(queryParams || {}),
253256
payload,
254257
init: mergeRequestInit(defaultInit, init),
255258
fetch,
256259
}),
257-
)) as CreateFetch<M, Paths[P][M]>,
260+
)) as unknown as CreateFetch<M, Paths[P][M]>,
258261
}),
259262
}),
260263
}

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export type Fetch = (
9292

9393
export type _TypedFetch<OP> = (
9494
arg: OpArgType<OP>,
95-
init?: RequestInit,
95+
init?: RequestInit & { baseUrl?: string },
9696
) => Promise<ApiResponse<OpReturnType<OP>>>
9797

9898
export type TypedFetch<OP> = _TypedFetch<OP> & {
@@ -130,7 +130,7 @@ export type Middleware = (
130130
) => Promise<ApiResponse>
131131

132132
export type FetchConfig = {
133-
baseUrl?: string
133+
baseUrl?: string | (() => string)
134134
init?: RequestInit
135135
use?: Middleware[]
136136
}

test/fetch.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,32 @@ describe('fetch', () => {
231231
expect(data.headers).toEqual({ ...expectedHeaders, admin: 'true' })
232232
})
233233

234+
describe('baseUrl', () => {
235+
const baseUrl = 'https://api2.backend.dev'
236+
const payload = {
237+
a: 1,
238+
b: '2',
239+
scalar: 'a',
240+
list: ['b', 'c'],
241+
}
242+
243+
it('can override baseUrl per invocation', async () => {
244+
fetcher.configure({}) // empty baseUrl
245+
const fun = fetcher.path('/query/{a}/{b}').method('get').create()
246+
const { data } = await fun(payload, { baseUrl })
247+
expect(data.host).toBe('api2.backend.dev')
248+
})
249+
250+
it('can configure with a function', async () => {
251+
fetcher.configure({
252+
baseUrl: () => baseUrl,
253+
})
254+
const fun = fetcher.path('/query/{a}/{b}').method('get').create()
255+
const { data } = await fun(payload)
256+
expect(data.host).toBe('api2.backend.dev')
257+
})
258+
})
259+
234260
it('middleware', async () => {
235261
const fun = fetcher
236262
.path('/bodyquery/{id}')

test/mocks/handlers.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,30 @@ function getResult(
3636
)
3737
}
3838

39+
function getResultWithHost(
40+
req: RestRequest,
41+
res: ResponseComposition,
42+
ctx: RestContext,
43+
) {
44+
return res(
45+
ctx.json({
46+
params: req.params,
47+
headers: getHeaders(req),
48+
query: getQuery(req),
49+
body: req.body,
50+
host: new URL(req.url).host,
51+
}),
52+
)
53+
}
54+
3955
const HOST = 'https://api.backend.dev'
56+
const HOST2 = 'https://api2.backend.dev'
4057

4158
const methods = {
42-
withQuery: [rest.get(`${HOST}/query/:a/:b`, getResult)],
59+
withQuery: [
60+
rest.get(`${HOST}/query/:a/:b`, getResult),
61+
rest.get(`${HOST2}/query/:a/:b`, getResultWithHost),
62+
],
4363
withBody: ['post', 'put', 'patch', 'delete'].map((method) => {
4464
return (rest as any)[method](`${HOST}/body/:id`, getResult)
4565
}),

test/paths.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type Data = {
44
headers: Record<string, string>
55
query: Record<string, string | string[]>
66
body: any
7+
host?: string
78
}
89

910
type Query = {

0 commit comments

Comments
 (0)