Unleashing Object Proxies: Building Type-Safe Wrappers for Anything

Rate this content
Bookmark

You must or must not have heard of object proxies before, but have you ever understood how to use them? What the actual usecase of an object proxy is? In this talk, we will do a deep dive into object proxies, understanding how they work, and the kinds of things you can build using them. We will build higher-order wrappers for existing libraries, and learn how to wrap over them in a type-safe manner.

16 min
21 Sep, 2023

Video Summary and Transcription

Object proxies are middleware for objects that allow control over input and output. They have various use cases such as controlling data access, adding logging, and handling responses. Implementing object proxies involves transpiling calls into network requests and handling property access and method calls. Handling object proxy traps and errors involves faking object structure, logging target objects and properties, and resolving errors. Making API calls with object proxies involves defining the correct type, making backend calls, and wrapping methods to return promises. Object proxies are widely used in ORMs and RPC libraries and should be explored and experimented with.

Available in Español

1. Introduction to Object Proxies

Short description:

I'm going to talk about unleashing object proxies, building type-safe wrappers for anything. Object proxies are middleware for your objects that allow you to control input and output. They have various use cases such as controlling data access, adding logging, and handling responses. Prisma and PRPC are examples of frameworks that use object proxies to access databases and enable transparent method calls.

Hey guys, my name is Akash Joshi. I'm a software engineer at SIGTECH and today I'm going to talk about unleashing object proxies, building type-safe wrappers for anything. Object proxies are an amazing tool that are severely under-explored but are quite widely used in all of the libraries that we use in our day-to-day. So I'm going to explain how they work, what they are and how you can use them as well.

So what are object proxies? Object proxies are basically middleware for your objects. So think of them as a wrapper around your object with which you can control what input comes into the object and what you return from the object whenever a user tries to access a method or a property. So what are some of the use cases that we see with object proxies in the real life? So one of the first use cases that one can think of is controlling data access. So doing input validation or implementing an access control layer on any sort of object which can contain privileged properties. So for example, on a bank, if you want to create an object which contains the user's data then based on whether a user has, one of your developers have, access to that property or not, you can actually do input validation on it. So if the user or the developer has access to that property, then you return it, otherwise you can return an error or whatever you want to do instead.

Similarly, you can add logging to your objects via object proxies. So if a user tries to repeatedly access an unauthorized data, or you just want to have a log of all of the activities that your developers are doing on any privileged object, then you can use, you can add logging to it by object proxies. Any accesses are logged to one of your access logs. But most importantly, what we use object proxies for is response handling. So this can involve returning something else based on what the user provides or lazy loading certain objects. So for example, just continuing with the user case. Let's say we have an object which contains a user's banking details, like their address history, their transaction history, anything. But you don't want to fetch all of it at runtime. So whenever someone tries to access their address history, then we make a network call to the address API, get all of that data, and then return it via the output. So on the on the developer side, it just looks like you made a property call, like a .call to an object. So that would be user.address. But in the backend, it will actually make an API call. And you can also implement caching and other things on top of it to actually make your object calls faster.

In the wild, object proxies are used by Prisma. So have you ever thought about how Prisma knows what all of your tables are and how it is able to access all of them at runtime, even though it actually isn't present in their codebase? What they do is they generate the types for all of your tables and then they apply them to an object proxy. So whenever you are accessing your property by using the dot method, so like prisma.analytics, prisma.user, prisma.posts, what it actually does is it makes a network call via object proxies to the actual database and then fetches those results and then returns them to your client. Of course, it isn't all hard-coded into the Prisma's codebase, but they generate it at runtime via TypeScript and then object proxies take care of the rest. Similarly for PRPC, PRPC works on a similar concept where they allow you to make object calls, make method calls, from your front end to your back end quite transparently. They use object proxies as well. They expose a router object on the back end and expose its types and then use those types on the front end.

2. Implementing Object Proxies

Short description:

The actual proxying happens on the front end where any call to one of their queries is transpiled by the object proxy into a network call. We are going to implement a simple version of the TRPC library and explain how object proxies work. On the server, we have a Fastify server with a route that accepts a procedure name and parameters, applies them to the procedure, and returns the response. The API object provided by the developer contains the exposed APIs. On the client side, we define an object proxy to access properties and make method calls. We use the get, has, and apply properties of the proxy to handle property access and method calls.

So the actual proxying happens on the front end where any call to one of their queries is actually transpiled by the object proxy into a network call and then the network call executes on the back end to actually return you the result on the front end. So what we are going to do now is we are going to implement a very simple version of the TRPC library or just try to implement a little bit of RPC to the back end ourselves using object proxies. And I'm going to also explain how object proxies work.

This just involves a little bit of code. Firstly, let's look at what our server looks like. So this is a very simple Fastify server. If you go to Fastify's documentation page, this is similar to what you will see on the main page. In fact, this is what I've copied from there. The only route that I've added here is slash RPC. It accepts a procedure name and a set of parameters for that procedure. And it then parses this to this data to string and then it applies those parameters on those procedure and then returns the response to the front end.

Where are we getting the actual procedure from? Let's say we have an API object which the developer provides to the server and this API object contains all of the APIs that the developer actually wants to expose to the client. In this case, we are exposing hello, which just returns world and then another method called sum, which returns the summation of two numbers. And see how we have actually defined the types correctly here. This is because we are going to use the properties of TypeScript to actually pass that type to the client as well.

On the client side now what we are going to do is that we are going to define a very simple object proxy. Have a look at the main method here where I will define a few lines of code which try to access some properties of the object proxy and try to get the results of it. So the first one is that we are trying to destruct a property called hello on an object that doesn't exist yet. Next we are checking for two properties whether they exist on the object and lastly we are trying to make method calls on the object proxy and then console.logging the result of it. So let's quickly define our object proxy here. The syntax for defining object proxies is to use a new proxy to define them. The first parameter it takes is the target object that you want to build it upon. So the standard way would be to use a wrapper over an existing object. In this case we are just going to use the properties of the wrapper so we don't use any existing object so I pass in an empty object.

Next we actually look at all of the properties that a proxy accepts. So they are primarily get, has and apply. These are the properties that you will be mostly looking at while building object proxies. The get property is the dot method so when you are destructing hello or you are doing client proxy dot some or client proxy dot anything else then the get method is called and you can return whatever you want from it. Has method is sort of like has own property which actually checks for whether a property exists in object proxy or not and in this case we are just returning true so these two methods which check whether these two parameters exist in the object property should actually return true even though they don't really exist there and lastly we have apply I don't think we have enough time to go into the depth of it but it is a trap for any method calls that you make on your object proxy. In this case get should be enough we will go into the depth of it as we actually define it.

3. Handling Object Proxy Traps and Errors

Short description:

We have a type error because the property hello does not exist on the empty object. We need to fake the object's structure. By logging the target object and the property being called, we can see the actual output. The error hello is not a function is expected as we haven't defined the code for it. We are interested in the first three lines, which show the property access and the resulting values. By changing the defined value to false, we can print whatever we want. To resolve the error, we should log the args instead of making a function call.

Let me yeah. So we have right now we have a type error here which says property hello does not exist on type empty object that is because it's actually returning a type of empty object as that is the default object that we passed here. But since we are going to create traps on this object proxy we have to now fake what it actually looks like. So let's try to console log on get here and the first parameter that it takes is the target object. In this case our target is an empty object so we just get underscore we don't really need to use that. What we are actually looking for is the property being called here. And then we will console log on property. And when we run this code here, this executes main which executes all of the lines here. We will see the actual output. So we the error that we are getting here is hello is not a function which is expected because it is calling the hello method here. We are going to define the code for that. But what we are more interested in is the first three lines here we are getting property when we are trying to console log. Sorry, when we are trying to access the client proxy property here and then we are going getting true and true for the property. We are getting true there because we have just defined true here if we change it to false and then we save it. This should run again. So that should then print false that just print whatever we want it to. And then this is actually printing the actual property that we are trying to access which is hello in this case. And yeah let's try to solve this error next. Hello is not a function. So this is because we are trying to make a function method call on the properties being written here. So to resolve this quickly we should just console.log args instead. And if we yeah now it returns correctly and it just prints the args being passed here the property and similar for some. It's printing what method we were calling which is some and then the parameters being passed in here which is 3 and 123.

4. Making API Calls and Defining the Correct Type

Short description:

Now we are going to make an API call to our backend and return the data. We use fetch to make the call and pass the parameters correctly. When accessing properties via the calls, it calls the API on the backend. We need to define the correct type by importing the API type from the backend and applying it to the client proxy. The client proxy returns a promise when making a method call. We use the promiseify type to wrap the methods and ensure they return promises. The key takeaways for building ObjectProxies are to embrace, experiment, and explore where they can be found in the real world.

Now we are going to quickly try to make an API call to our back end and return the data based on this what our back end looks like is it accepts a procedure named parameter and a params parameter and based on the procedure name and params that makes the API call. So for hello and params being an empty array it returns world for procedure name being some and the parameters as one and two it returns three and hypothetically for other params it should return the right result as well. So let's quickly do that.

In this case what we do is we just make a fetch call here so fetch to the back end http host 3000. We need to pass in the parameters correctly as well. So I'll just make note of that add async in here. So our procedure name we need url search params. So search params is equal to new URL search params and then add in a procedure name which should be the property and then I believe the args here, params, sorry. So pass in the params as json.stringify args. I think this needs to be a toString and yeah, that resolves that and then we just add this in here into the API call. And then while returning the data we need to do response.json of course. That should return our result. So now when we are running the method again, it should return the result but I believe I am not logging here. So let me have a quick look at what went wrong. So yeah, as we can see here now, when we are trying to access these properties via the calls, this is actually calling the API on the backend. So on the server side, what is happening is that it's accepting the procedure name and params that we are passing in via our URL search params here and then it's parsing them and then returning the actual response via procedure.apply. Now in this case, yeah, that's pretty much it in terms of our object proxy but we are actually missing the actual part still which is actually defining the correct type. So I can actually import the type of API from the backend and then try to apply it to our client proxy here. So let's try to do that. If I do a type of API here, then it should hypothetically apply the correct type to my client proxy here and then if I try to do something like client proxy dot then it should also return the correct type here. But what we are missing is that it doesn't return a promise. In our case, the client proxy actually returns a promise whenever you are making a method call because it is actually making a fetch request. So we define a simple type here called promiseify which extends a record and then, based on the keys for that record, it just applies a promise to the return types of all of the existing methods. Now, in this way, if we wrap our method in this generic that we have just defined, then what we get instead when we are trying to do clientProxy. is that all of the results actually return a promise instead and so we get the correct type on this. And so we don't confuse our users anymore. So that is it for our ObjectProxy. And what are the takeaways? The takeaways here for building your own ObjectProxies are embrace, experiment, and explore. Firstly, embrace ObjectProxies. Try to see, try to find out where you can find ObjectProxies in the real world.

5. Conclusion and Thanks

Short description:

Most ORMs and RPC libraries use ObjectProxies. Experiment, create your own RPC libraries, and share them. Explore the edges of ObjectProxies to push their limits. Thank you for being a good audience and happy coding.

You might be surprised to find that most ORMs might be using ObjectProxies and then a lot of RPC libraries use them as well. Experiment with ObjectProxies. Try to create your own RPC libraries. Share them on the internet and just try to have fun with them.

And then finally, explore. Try to push the edges of ObjectProxies. TRPC or Rocket RPC my library wouldn't have been created if you, the people behind them wouldn't have tried to explore the edges of ObjectProxies and what you can do with them. Same for Prisma and a lot of other libraries out there.

So yeah, thank you. Thanks for this talk. Thanks for being a good audience. And happy coding.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
Top Content
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
Remix Conf Europe 2022Remix Conf Europe 2022
23 min
Scaling Up with Remix and Micro Frontends
Top Content
Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.
React Summit 2023React Summit 2023
23 min
React Concurrency, Explained
Top Content
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar
Remix Conf Europe 2022Remix Conf Europe 2022
37 min
Full Stack Components
Top Content
Remix is a web framework that gives you the simple mental model of a Multi-Page App (MPA) but the power and capabilities of a Single-Page App (SPA). One of the big challenges of SPAs is network management resulting in a great deal of indirection and buggy code. This is especially noticeable in application state which Remix completely eliminates, but it's also an issue in individual components that communicate with a single-purpose backend endpoint (like a combobox search for example).
In this talk, Kent will demonstrate how Remix enables you to build complex UI components that are connected to a backend in the simplest and most powerful way you've ever seen. Leaving you time to chill with your family or whatever else you do for fun.
JSNation Live 2021JSNation Live 2021
29 min
Making JavaScript on WebAssembly Fast
Top Content
JavaScript in the browser runs many times faster than it did two decades ago. And that happened because the browser vendors spent that time working on intensive performance optimizations in their JavaScript engines.Because of this optimization work, JavaScript is now running in many places besides the browser. But there are still some environments where the JS engines can’t apply those optimizations in the right way to make things fast.We’re working to solve this, beginning a whole new wave of JavaScript optimization work. We’re improving JavaScript performance for entirely different environments, where different rules apply. And this is possible because of WebAssembly. In this talk, I'll explain how this all works and what's coming next.
React Summit 2023React Summit 2023
24 min
Debugging JS
Top Content
As developers, we spend much of our time debugging apps - often code we didn't even write. Sadly, few developers have ever been taught how to approach debugging - it's something most of us learn through painful experience.  The good news is you _can_ learn how to debug effectively, and there's several key techniques and tools you can use for debugging JS and React apps.

Workshops on related topic

React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Top Content
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Top Content
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn
React Day Berlin 2022React Day Berlin 2022
86 min
Using CodeMirror to Build a JavaScript Editor with Linting and AutoComplete
Top Content
WorkshopFree
Using a library might seem easy at first glance, but how do you choose the right library? How do you upgrade an existing one? And how do you wade through the documentation to find what you want?
In this workshop, we’ll discuss all these finer points while going through a general example of building a code editor using CodeMirror in React. All while sharing some of the nuances our team learned about using this library and some problems we encountered.
TestJS Summit - January, 2021TestJS Summit - January, 2021
173 min
Testing Web Applications Using Cypress
WorkshopFree
This workshop will teach you the basics of writing useful end-to-end tests using Cypress Test Runner.
We will cover writing tests, covering every application feature, structuring tests, intercepting network requests, and setting up the backend data.
Anyone who knows JavaScript programming language and has NPM installed would be able to follow along.
Node Congress 2023Node Congress 2023
63 min
0 to Auth in an Hour Using NodeJS SDK
WorkshopFree
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:- User authentication - Managing user interactions, returning session / refresh JWTs- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.
Table of contents- A quick intro to core authentication concepts- Coding- Why passwordless matters
Prerequisites- IDE for your choice- Node 18 or higher
React Summit US 2023React Summit US 2023
96 min
Build a powerful DataGrid in few hours with Ag Grid
WorkshopFree
Does your React app need to efficiently display lots (and lots) of data in a grid? Do your users want to be able to search, sort, filter, and edit data? AG Grid is the best JavaScript grid in the world and is packed with features, highly performant, and extensible. In this workshop, you’ll learn how to get started with AG Grid, how we can enable sorting and filtering of data in the grid, cell rendering, and more. You will walk away from this free 3-hour workshop equipped with the knowledge for implementing AG Grid into your React application.
We all know that rolling our own grid solution is not easy, and let's be honest, is not something that we should be working on. We are focused on building a product and driving forward innovation. In this workshop, you'll see just how easy it is to get started with AG Grid.
Prerequisites: Basic React and JavaScript
Workshop level: Beginner