Last active
February 20, 2024 08:05
-
-
Save byronferguson/fb808f6590ca328b5b4151738f8bb58a to your computer and use it in GitHub Desktop.
HttpFactory for Repo pattern in Nuxt3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { $Fetch } from 'nitropack'; | |
import type { AsyncDataOptions } from 'nuxt/app'; | |
import type { FetchOptions } from 'ofetch'; | |
import { joinURL } from 'ufo'; | |
import { z } from 'zod'; | |
export type { $Fetch }; | |
type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; | |
type CallOptions<T> = { | |
asyncOptions?: AsyncDataOptions<T>; | |
fetchOptions?: Omit<FetchOptions, 'method' | 'body' | 'onRequest'>; | |
schema?: z.ZodType<T>; | |
useComposable?: boolean; | |
}; | |
type GetOptions<T> = Omit<CallOptions<T>, 'fetchOptions'>; | |
export class HttpFactory { | |
private readonly fetch: $Fetch; | |
protected resource: string; | |
constructor(fetch: $Fetch, resource = '') { | |
this.fetch = fetch; | |
this.resource = resource; | |
} | |
protected async call< | |
R = unknown, | |
DTO extends BodyInit | Record<string, any> | null | undefined = Record<string, any>, | |
>( | |
method: Method, | |
url: string, | |
body?: DTO, | |
{ fetchOptions = {}, asyncOptions = {}, useComposable = true, schema }: CallOptions<R> = {}, | |
) { | |
const fullPath = joinURL(this.resource, url); | |
const options = { | |
method, | |
body, | |
...fetchOptions, | |
}; | |
if (useComposable) { | |
return useAsyncData(async () => { | |
const data = await this.fetch<R>(fullPath, options); | |
return schema ? schema.parse(data) : data; | |
}, asyncOptions); | |
} | |
const data = await this.fetch<R>(fullPath, options); | |
return schema ? schema.parse(data) : data; | |
} | |
/** | |
* A helper method for making GET requests. | |
*/ | |
protected async get<R = unknown>( | |
method: Method, | |
url: string, | |
fetchOptions: Pick<CallOptions<R>, 'fetchOptions'>, | |
{ asyncOptions = {}, useComposable = true, schema }: GetOptions<R> = {}, | |
) { | |
const fullPath = joinURL(this.resource, url); | |
const options = { | |
method, | |
...fetchOptions, | |
}; | |
if (useComposable) { | |
return useAsyncData(async () => { | |
const data = await this.fetch<R>(fullPath, options); | |
return schema ? schema.parse(data) : data; | |
}, asyncOptions); | |
} | |
const data = await this.fetch<R>(fullPath, options); | |
return schema ? schema.parse(data) : data; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import camelcaseKeys from 'camelcase-keys'; | |
import type { $Fetch, NitroFetchOptions, NitroFetchRequest } from 'nitropack'; | |
import type { AsyncDataOptions } from 'nuxt/app'; | |
import { joinURL } from 'ufo'; | |
import { z } from 'zod'; | |
export type { $Fetch }; | |
type Method = NitroFetchOptions<NitroFetchRequest>['method']; | |
type CallOptions<T> = { | |
asyncOptions?: AsyncDataOptions<T>; | |
fetchOptions?: Omit<NitroFetchOptions<NitroFetchRequest>, 'method' | 'body' | 'onRequest'>; | |
schema?: z.ZodType<T>; | |
useComposable?: boolean; | |
}; | |
type GetOptions<T> = Omit<CallOptions<T>, 'fetchOptions'>; | |
type JSONValue = string | number | boolean | { [key: string]: JSONValue } | Array<JSONValue>; | |
export class HttpFactory { | |
private readonly fetch: $Fetch; | |
protected resource: string; | |
constructor(fetch: $Fetch, resource = '') { | |
this.fetch = fetch; | |
this.resource = resource; | |
} | |
protected call< | |
R = unknown, | |
DTO extends BodyInit | Record<string, any> | null | undefined = Record<string, any>, | |
>( | |
method: Method, | |
url: string, | |
body?: DTO, | |
{ fetchOptions = {}, asyncOptions = {}, useComposable = true, schema }: CallOptions<R> = {}, | |
) { | |
const fullPath = joinURL(this.resource, url); | |
const options = { | |
method, | |
body, | |
...fetchOptions, | |
}; | |
if (useComposable) { | |
return useAsyncData(() => this.execute(fullPath, options, schema), asyncOptions); | |
} | |
return this.execute(fullPath, options, schema); | |
} | |
/** | |
* A helper method for making GET requests. | |
*/ | |
protected get<R = unknown>( | |
method: Method, | |
url: string, | |
fetchOptions: Pick<CallOptions<R>, 'fetchOptions'>, | |
{ asyncOptions = {}, useComposable = true, schema }: GetOptions<R> = {}, | |
) { | |
const fullPath = joinURL(this.resource, url); | |
const options = { | |
method, | |
...fetchOptions, | |
}; | |
if (useComposable) { | |
return useAsyncData(() => this.execute(fullPath, options, schema), asyncOptions); | |
} | |
return this.execute(fullPath, options, schema); | |
} | |
private async execute<R>( | |
fullPath: string, | |
options: NitroFetchOptions<NitroFetchRequest>, | |
schema?: z.ZodType<R>, | |
): Promise<R> { | |
let data: any = await this.fetch<JSONValue>(fullPath, options); | |
if (fullPath.includes('/v1') && typeof data === 'object' && data !== null) { | |
data = camelcaseKeys(data as Record<string, unknown> | readonly Record<string, unknown>[], { | |
deep: true, | |
}); | |
} | |
return schema ? schema.parse(data) : data; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment