Skip to content

Commit

Permalink
feat: add ofetch http client
Browse files Browse the repository at this point in the history
  • Loading branch information
samydoesit committed Nov 10, 2024
1 parent 24c9fe2 commit 4e2a0af
Show file tree
Hide file tree
Showing 11 changed files with 59,281 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Options:
--module-name-index <number> determines which path index should be used for routes separation (example: GET:/fruits/getFruit -> index:0 -> moduleName -> fruits) (default: 0)
--module-name-first-tag splits routes based on the first tag (default: false)
--axios generate axios http client (default: false)
--ofetch generate ofetch http client (default: false)
--unwrap-response-data unwrap the data item from the response (default: false)
--disable-throw-on-error Do not throw an error when response.ok is not true (default: false)
--single-http-client Ability to send HttpClient instance to Api constructor (default: false)
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const FILE_PREFIX = `/* eslint-disable */

export const HTTP_CLIENT = {
FETCH: "fetch",
OFETCH: "ofetch",
AXIOS: "axios",
} as const;

Expand Down
123 changes: 123 additions & 0 deletions templates/base/http-clients/ofetch-http-client.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<%
const { apiConfig, generateResponses, config } = it;
%>

import type { $Fetch, FetchOptions } from 'ofetch'
import { $fetch } from 'ofetch'

export type QueryParamsType = Record<string | number, any>;
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;

export interface CustomFetchOptions extends FetchOptions {
/** set parameter to `true` for call `securityWorker` for this request */
secure?: boolean
}

export type RequestParams = Omit<CustomFetchOptions, 'body' | 'method'>

export interface ApiConfig<SecurityDataType = unknown> {
baseURL?: string;
basePath?: string;
baseApiParams?: Omit<RequestParams, "baseURL" | "cancelToken" | "signal">;
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
customFetch?: $Fetch;
}

type CancelToken = Symbol | string | number;

export enum ContentType {
Json = "application/json",
FormData = "multipart/form-data",
UrlEncoded = "application/x-www-form-urlencoded",
}

export class HttpClient<SecurityDataType = unknown> {
public baseURL: string = "<%~ apiConfig.baseUrl %>";
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private abortControllers = new Map<CancelToken, AbortController>();
private customFetch = (url: string, fetchParams: FetchOptions) => $fetch(url, fetchParams)

private baseApiParams: RequestParams = {
credentials: 'same-origin',
headers: {},
redirect: 'follow',
referrerPolicy: 'no-referrer',
}

constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
Object.assign(this, apiConfig);
}

public setSecurityData = (data: SecurityDataType | null) => {
this.securityData = data;
}

protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
return {
...this.baseApiParams,
...params1,
...(params2 || {}),
headers: {
...(this.baseApiParams.headers || {}),
...(params1.headers || {}),
...((params2 && params2.headers) || {}),
},
};
}

protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
if (this.abortControllers.has(cancelToken)) {
const abortController = this.abortControllers.get(cancelToken);
if (abortController) {
return abortController.signal;
}
return void 0;
}

const abortController = new AbortController();
this.abortControllers.set(cancelToken, abortController);
return abortController.signal;
}

public abortRequest = (cancelToken: CancelToken) => {
const abortController = this.abortControllers.get(cancelToken)

if (abortController) {
abortController.abort();
this.abortControllers.delete(cancelToken);
}
}

public request = async <T = any>(url: string, {
body,
secure,
method,
baseURL,
signal,
params,
...options
<% if (config.unwrapResponseData) { %>
}: CustomFetchOptions): Promise<T> => {
<% } else { %>
}: CustomFetchOptions): Promise<T> => {
<% } %>
const secureParams = ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && this.securityWorker && await this.securityWorker(this.securityData)) || {};
const requestOptions = this.mergeRequestParams(options, secureParams)

return this.customFetch(
`${baseURL || this.baseURL || ""}${this.basePath ? `${this.basePath}` : ''}${url}`,
{
params,
method,
...requestOptions,
signal,
body,
}
<% if (config.unwrapResponseData) { %>
).then((response: T) => response.data)
<% } else { %>
).then((response: T) => response)
<% } %>
};
}
7 changes: 7 additions & 0 deletions templates/default/procedure-call.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,20 @@ const describeReturnType = () => {

*/
<%~ route.routeName.usage %><%~ route.namespace ? ': ' : ' = ' %>(<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> =>
<% if (config.httpClientType === config.constants.HTTP_CLIENT.OFETCH) { %>
<%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>>(`<%~ path %>`, {
<% } %>
<% if (config.httpClientType !== config.constants.HTTP_CLIENT.OFETCH) { %>
<%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({
path: `<%~ path %>`,
<% } %>
method: '<%~ _.upperCase(method) %>',
<%~ queryTmpl ? `query: ${queryTmpl},` : '' %>
<%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %>
<%~ securityTmpl ? `secure: ${securityTmpl},` : '' %>
<% if (config.httpClientType !== config.constants.HTTP_CLIENT.OFETCH) { %>
<%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %>
<%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %>
<% } %>
...<%~ _.get(requestConfigParam, "name") %>,
})<%~ route.namespace ? ',' : '' %>
Loading

0 comments on commit 4e2a0af

Please sign in to comment.