Allows us to create a ‘fake’ object that lets us intercept attribute accesses and calls with some funky stuff under the hood.
interface ProxyCallbackOptions {
path: string[];
args: unknown[];
}
type ProxyCallback = (opts: ProxyCallbackOptions) => unknown;
function createRecursiveProxy(callback: ProxyCallback, path: string[]) {
const proxy: unknown = new Proxy(
() => {
// dummy no-op function since we don't have any
// client-side target we want to remap to
},
{
get(_obj, key) {
if (typeof key !== 'string') return undefined;
// Recursively compose the full path until a function is invoked
return createRecursiveProxy(callback, [...path, key]);
},
apply(_1, _2, args) {
// Call the callback function with the entire path we
// recursively created and forward the arguments
return callback({
path,
args,
});
},
},
);
return proxy;
}
- The
get
method handles property accesses such aspost.byId
. The key is the property name we’re accessing, so when we typepost
ourkey
will bepost
, and when we typepost.byId
ourkey
will bebyId
. The recursive proxy combines all of these keys into a final path, e.g. [“post”, “byId”, “query”], that we can use to determine the URL we want to send a request to. - The
apply
method is called when we invoke a function on the proxy, such as.query(args)
. Theargs
is the arguments we pass to the function, so when we callpost.byId.query(args)
ourargs
will be our input, which we’ll provide as query parameters or request body depending on the type of procedure. ThecreateRecursiveProxy
takes in a callback function which we’ll map theapply
to with the path and args.
Note that we can cast the above unknown
proxy to whatever shape we want. In tRPC, this is done by extracting the type from the server and then doing some transformations to get a client type and then casting to that.