Skip to content

Commit f205cf1

Browse files
committed
feat: custom open fetch by ofetch
1 parent 6bb3d3e commit f205cf1

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

bun.lockb

368 KB
Binary file not shown.

src/composables/use-open-fetch.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {
2+
type CreateFetchOptions,
3+
type UseFetchOptions,
4+
type UseFetchReturn,
5+
createFetch,
6+
} from '@vueuse/core'
7+
import { type MaybeRefOrGetter, toValue } from 'vue'
8+
import type {
9+
FetchResponseData,
10+
FilterMethods,
11+
ParamsOption,
12+
RequestBodyOption,
13+
} from '#/utils/fetch'
14+
15+
type MethodOption<M, P> = 'get' extends keyof P ? { method?: M } : { method: M }
16+
17+
type UseOpenFetchOptions<
18+
Method,
19+
LowercasedMethod,
20+
Params,
21+
Operation = 'get' extends LowercasedMethod
22+
? 'get' extends keyof Params
23+
? Params['get']
24+
: never
25+
: LowercasedMethod extends keyof Params
26+
? Params[LowercasedMethod]
27+
: never,
28+
> = MethodOption<Method, Params> &
29+
ParamsOption<Operation> &
30+
RequestBodyOption<Operation>
31+
32+
export type UseOpenFetchClient<Paths> = <
33+
Path extends Extract<keyof Paths, string>,
34+
Methods extends FilterMethods<Paths[Path]>,
35+
Method extends
36+
| Extract<keyof Methods, string>
37+
| Uppercase<Extract<keyof Methods, string>>,
38+
LowercasedMethod extends Lowercase<Method> extends keyof Methods
39+
? Lowercase<Method>
40+
: never,
41+
DefaultMethod extends 'get' extends LowercasedMethod
42+
? 'get'
43+
: LowercasedMethod,
44+
ResT = FetchResponseData<Methods[DefaultMethod]>,
45+
>(
46+
path: Path | (() => Path),
47+
options: RequestInit & UseOpenFetchOptions<Method, LowercasedMethod, Methods>,
48+
useFetchOptions?: UseFetchOptions,
49+
) => UseFetchReturn<ResT> & PromiseLike<UseFetchReturn<ResT>>
50+
51+
export function createUseOpenFetch<Paths>(
52+
config: CreateFetchOptions,
53+
): UseOpenFetchClient<Paths> {
54+
const useFetch = createFetch({
55+
...config,
56+
options: {
57+
fetch: createOpenFetch({
58+
baseURL: toValue(config.baseUrl),
59+
}),
60+
},
61+
})
62+
63+
return (
64+
url: MaybeRefOrGetter<string>,
65+
requests: RequestInit,
66+
useFetchOptions?: UseFetchOptions,
67+
) => {
68+
return useFetch(toValue(url), requests, useFetchOptions)
69+
}
70+
}

src/utils/fetch.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {
2+
$fetch,
3+
type FetchContext,
4+
type FetchError,
5+
type FetchOptions,
6+
} from 'ofetch'
7+
import type {
8+
ErrorResponse,
9+
MediaType,
10+
OperationRequestBodyContent,
11+
ResponseObjectMap,
12+
SuccessResponse,
13+
} from 'openapi-typescript-helpers'
14+
15+
export type FetchResponseData<
16+
T,
17+
Media extends MediaType = MediaType,
18+
> = SuccessResponse<ResponseObjectMap<T>, Media>
19+
20+
export type FetchResponseError<
21+
T,
22+
Media extends MediaType = MediaType,
23+
> = FetchError<ErrorResponse<ResponseObjectMap<T>, Media>>
24+
25+
export type MethodOption<M, P> = 'get' extends keyof P
26+
? { method?: M }
27+
: { method: M }
28+
29+
export type ParamsOption<T> = T extends { parameters?: any; query?: any }
30+
? T['parameters']
31+
: Record<string, never>
32+
33+
export type RequestBodyOption<T> =
34+
OperationRequestBodyContent<T> extends never
35+
? { body?: never }
36+
: undefined extends OperationRequestBodyContent<T>
37+
? { body?: OperationRequestBodyContent<T> }
38+
: { body: OperationRequestBodyContent<T> }
39+
40+
export type FilterMethods<T> = {
41+
[K in keyof Omit<T, 'parameters'> as T[K] extends never | undefined
42+
? never
43+
: K]: T[K]
44+
}
45+
46+
export type OpenFetchOptions<
47+
Method,
48+
LowercasedMethod,
49+
Params,
50+
Operation = 'get' extends LowercasedMethod
51+
? 'get' extends keyof Params
52+
? Params['get']
53+
: never
54+
: LowercasedMethod extends keyof Params
55+
? Params[LowercasedMethod]
56+
: never,
57+
> = MethodOption<Method, Params> &
58+
ParamsOption<Operation> &
59+
RequestBodyOption<Operation> &
60+
Omit<FetchOptions, 'query' | 'body' | 'method'>
61+
62+
export type OpenFetchClient<Paths> = <
63+
ReqT extends Extract<keyof Paths, string>,
64+
Methods extends FilterMethods<Paths[ReqT]>,
65+
Method extends
66+
| Extract<keyof Methods, string>
67+
| Uppercase<Extract<keyof Methods, string>>,
68+
LowercasedMethod extends Lowercase<Method> extends keyof FilterMethods<
69+
Paths[ReqT]
70+
>
71+
? Lowercase<Method>
72+
: never,
73+
DefaultMethod extends 'get' extends LowercasedMethod
74+
? 'get'
75+
: LowercasedMethod,
76+
ResT = FetchResponseData<Paths[ReqT][DefaultMethod]>,
77+
>(
78+
url: ReqT,
79+
options?: OpenFetchOptions<Method, LowercasedMethod, Methods>,
80+
) => Promise<ResT>
81+
82+
// More flexible way to rewrite the request path,
83+
// but has problems - https://github.com/unjs/ofetch/issues/319
84+
export function openFetchRequestInterceptor(ctx: FetchContext) {
85+
ctx.request = fillPath(
86+
ctx.request as string,
87+
(ctx.options as { path: Record<string, string> }).path,
88+
)
89+
}
90+
91+
export function createOpenFetch<Paths>(
92+
options: FetchOptions | ((options: FetchOptions) => FetchOptions),
93+
): OpenFetchClient<Paths> {
94+
return (url: string, opts: any) => {
95+
return $fetch(
96+
fillPath(url, opts?.path),
97+
typeof options === 'function'
98+
? options(opts)
99+
: {
100+
...options,
101+
...opts,
102+
},
103+
)
104+
}
105+
}
106+
107+
function fillPath(path: string, params: object = {}) {
108+
for (const [k, v] of Object.entries(params))
109+
path = path.replace(`{${k}}`, encodeURIComponent(String(v)))
110+
return path
111+
}

0 commit comments

Comments
 (0)