@hey-api/openapi-ts
Version:
🌀 OpenAPI to TypeScript code generator. Generate API clients, SDKs, validators, and more.
228 lines (200 loc) • 6 kB
text/typescript
import {
useAsyncData,
useFetch,
useLazyAsyncData,
useLazyFetch,
} from 'nuxt/app';
import { reactive, ref, watch } from 'vue';
import { createSseClient } from '../core/serverSentEvents';
import type { HttpMethod } from '../core/types';
import type { Client, Config, RequestOptions } from './types';
import {
buildUrl,
createConfig,
executeFetchFn,
mergeConfigs,
mergeHeaders,
mergeInterceptors,
serializeBody,
setAuthParams,
unwrapRefs,
} from './utils';
export const createClient = (config: Config = {}): Client => {
let _config = mergeConfigs(createConfig(), config);
const getConfig = (): Config => ({ ..._config });
const setConfig = (config: Config): Config => {
_config = mergeConfigs(_config, config);
return getConfig();
};
const beforeRequest = async (options: RequestOptions) => {
const opts = {
..._config,
...options,
$fetch: options.$fetch ?? _config.$fetch ?? $fetch,
headers: mergeHeaders(_config.headers, options.headers),
onRequest: mergeInterceptors(_config.onRequest, options.onRequest),
onResponse: mergeInterceptors(_config.onResponse, options.onResponse),
};
if (opts.security) {
await setAuthParams({
...opts,
security: opts.security,
});
}
if (opts.requestValidator) {
await opts.requestValidator(opts);
}
const url = buildUrl(opts);
return { opts, url };
};
const request: Client['request'] = ({
asyncDataOptions,
composable = '$fetch',
...options
}) => {
const key = options.key;
const opts = {
..._config,
...options,
$fetch: options.$fetch ?? _config.$fetch ?? $fetch,
headers: mergeHeaders(_config.headers, options.headers),
onRequest: mergeInterceptors(_config.onRequest, options.onRequest),
onResponse: mergeInterceptors(_config.onResponse, options.onResponse),
};
const {
requestValidator,
responseTransformer,
responseValidator,
security,
} = opts;
if (requestValidator || security) {
// auth must happen in interceptors otherwise we'd need to require
// asyncContext enabled
// https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext
opts.onRequest = [
async ({ options }) => {
if (security) {
await setAuthParams({
auth: opts.auth,
headers: options.headers,
query: options.query,
security,
});
}
if (requestValidator) {
await requestValidator({
...options,
// @ts-expect-error
body: options.rawBody,
});
}
},
...opts.onRequest,
];
}
if (responseTransformer || responseValidator) {
opts.onResponse = [
...opts.onResponse,
async ({ options, response }) => {
if (options.responseType && options.responseType !== 'json') {
return;
}
if (!response.ok) {
return;
}
if (responseValidator) {
await responseValidator(response._data);
}
if (responseTransformer) {
response._data = await responseTransformer(response._data);
}
},
];
}
// remove Content-Type header if body is empty to avoid sending invalid requests
if (opts.body === undefined || opts.body === '') {
opts.headers.delete('Content-Type');
}
const fetchFn = opts.$fetch;
if (composable === '$fetch') {
return executeFetchFn(
// @ts-expect-error
opts,
fetchFn,
);
}
if (composable === 'useFetch' || composable === 'useLazyFetch') {
opts.rawBody = opts.body;
const bodyParams = reactive({
body: opts.body,
bodySerializer: opts.bodySerializer,
});
const body = ref(serializeBody(opts));
opts.body = body;
watch(bodyParams, (changed) => {
body.value = serializeBody(changed);
});
return composable === 'useLazyFetch'
? useLazyFetch(() => buildUrl(opts), opts)
: useFetch(() => buildUrl(opts), opts);
}
const handler: any = () =>
executeFetchFn(
// @ts-expect-error
opts,
fetchFn,
);
if (composable === 'useAsyncData') {
return key
? useAsyncData(key, handler, asyncDataOptions)
: useAsyncData(handler, asyncDataOptions);
}
if (composable === 'useLazyAsyncData') {
return key
? useLazyAsyncData(key, handler, asyncDataOptions)
: useLazyAsyncData(handler, asyncDataOptions);
}
return undefined as any;
};
const makeMethodFn =
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
request({ ...options, method });
const makeSseFn =
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
const { opts, url } = await beforeRequest(options);
return createSseClient({
...unwrapRefs(opts),
body: opts.body as BodyInit | null | undefined,
method,
onRequest: undefined,
signal: unwrapRefs(opts.signal) as AbortSignal,
url,
});
};
return {
buildUrl,
connect: makeMethodFn('CONNECT'),
delete: makeMethodFn('DELETE'),
get: makeMethodFn('GET'),
getConfig,
head: makeMethodFn('HEAD'),
options: makeMethodFn('OPTIONS'),
patch: makeMethodFn('PATCH'),
post: makeMethodFn('POST'),
put: makeMethodFn('PUT'),
request,
setConfig,
sse: {
connect: makeSseFn('CONNECT'),
delete: makeSseFn('DELETE'),
get: makeSseFn('GET'),
head: makeSseFn('HEAD'),
options: makeSseFn('OPTIONS'),
patch: makeSseFn('PATCH'),
post: makeSseFn('POST'),
put: makeSseFn('PUT'),
trace: makeSseFn('TRACE'),
},
trace: makeMethodFn('TRACE'),
} as Client;
};