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
getmethod handles property accesses such aspost.byId. The key is the property name we’re accessing, so when we typepostourkeywill bepost, and when we typepost.byIdourkeywill 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
applymethod is called when we invoke a function on the proxy, such as.query(args). Theargsis the arguments we pass to the function, so when we callpost.byId.query(args)ourargswill be our input, which we’ll provide as query parameters or request body depending on the type of procedure. ThecreateRecursiveProxytakes in a callback function which we’ll map theapplyto 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.