Replies: 1 comment
-
As mentioned the business logic for it would be quite easy, but can't figure out how to properply type the response...: import type { Hono } from 'hono'
export const customHc = <T extends Hono<any, any, any>>(
baseUrl: string,
options?: ClientRequestOptions,
) =>
createProxy(function proxyCallback(opts) {
const parts = [...opts.path]
let method = ''
if (/^\$/.test(parts[parts.length - 1])) {
const last = parts.pop()
if (last) {
method = last.replace(/^\$/, '')
}
}
const path = parts.join('/')
const urlRaw = mergePath(baseUrl, path)
const url = replaceUrlParam(urlRaw, opts.args[0]?.param)
return {
method,
urlRaw,
url,
args: opts.args,
// omitted including ClientRequestImpl for now but don't see any issues there
}
}, []) as UnionToIntersection<Client<T>> Anyone could give me some starting points on how to correctly type this, while maintaining the autocompletion of the paths for the client? This is the full code that you could copy paste / edit, aside from the above import type { Hono, Schema } from 'hono'
import type { ClientRequest, ClientRequestOptions } from 'hono/client'
import { UnionToIntersection } from 'hono/utils/types';
type Callback = (opts: { path: string[]; args: any[] }) => unknown
const createProxy = (callback: Callback, path: string[] = []): unknown => {
return new Proxy(() => {}, {
get(_obj, key) {
if (typeof key !== 'string' || key === 'then') {
return undefined
}
return createProxy(callback, [...path, key])
},
apply(_1, _2, args) {
return callback({ path, args })
},
})
}
type PathToChain<
Path extends string,
E extends Schema,
Original extends string = Path,
> = Path extends `/${infer P}`
? PathToChain<P, E, Path>
: Path extends `${infer P}/${infer R}`
? { [K in P]: PathToChain<R, E, Original> }
: {
[K in Path extends '' ? 'index' : Path]: ClientRequest<
E extends Record<string, unknown> ? E[Original] : never
>
}
export type Client<T> =
T extends Hono<any, infer S, any>
? S extends Record<infer K, Schema>
? K extends string
? PathToChain<K, S>
// ? {K:K,S: S}
: never
: never
: never
export const replaceUrlParam = (
urlString: string,
params?: Record<string, string>,
) => {
if (params)
for (const [k, v] of Object.entries(params)) {
const reg = new RegExp('/:' + k + '(?:{[^/]+})?')
urlString = urlString.replace(reg, `/${v}`)
}
return urlString
}
export const mergePath = (base: string, path: string) => {
base = base.replace(/\/+$/, '')
base = base + '/'
path = path.replace(/^\/+/, '')
return base + path
}
export const replaceUrlProtocol = (
urlString: string,
protocol: 'ws' | 'http',
) => {
switch (protocol) {
case 'ws':
return urlString.replace(/^http/, 'ws')
case 'http':
return urlString.replace(/^ws/, 'http')
}
}
export const customHc = <T extends Hono<any, any, any>>(
baseUrl: string,
options?: ClientRequestOptions,
) =>
createProxy(function proxyCallback(opts) {
const parts = [...opts.path]
let method = ''
if (/^\$/.test(parts[parts.length - 1])) {
const last = parts.pop()
if (last) {
method = last.replace(/^\$/, '')
}
}
const path = parts.join('/')
const urlRaw = mergePath(baseUrl, path)
const url = replaceUrlParam(urlRaw, opts.args[0]?.param)
return {
method,
urlRaw,
url,
args: opts.args,
}
}, []) as UnionToIntersection<Client<T>> |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
To make a proper wrapper for e.g. @tanstack/react-query (or ) it would be ideal to access over the following information:
Currently all of the above are exposed ( clean path: .$url(), path, path .$url({params: {...}}, query function) but quite tedious to use in a wrapper function as easy input as all are proxies. e.g. if we make a useEndpoint hook you don't want to input all three functions separately (e.g. see https://github.com/orgs/honojs/discussions/3075). So an additional function (next to .$get, .$post ... .$url ) that returns all needed information would be very helpful, and as this is not the hono/core I expect there is more room for DX expanding the interface.
Business logic wise all information is already present in /hono/src/client/client.ts, but I'm not sure about the syntax for it as it should be descriptive and straight forward, but not confusing / interface pollution. Things I thought about:
const warpperClient = metaHc<app>
.This would save a lot of boiler plate and makes it very smooth to use consume the API in the client (in my case React, but this is not specific to react as all other frontend frameworks will also have the data needed to build their own query client wrapper).
Beta Was this translation helpful? Give feedback.
All reactions