Photo taken by me @ San Juan de Gaztelugatxe. (CC BY-NC-SA 3.0)

How I made a validation library using ES6 Proxy

sorodrigo
HackerNoon.com
Published in
5 min readDec 29, 2017

--

Proxy support in major browsers is here, time to unleash all the power it brings.

TLDR; If you just want to check out the code, you can find it on the github link below. If you just want to see the validator in action go to the end.

Full disclosure: I started writing this blog post on Aug 28, then I just got caught up with stuff and left it unfinished. Now I’m back to finish what I started.

I’ve wondered for a while now what was this Proxy thing I kept stumbling upon on MDN. I’ve read the description and the API many times. However, it wasn’t very clear to me what were the use cases where I could take advantage of it.

Anyway one weekend I made some time to dig deeper into it, read a bunch of blog posts, and decided to experiment a little bit.

So what’s a Proxy in the first place?

If you ask the Mozilla crowd they’ll probably say that:

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

In other words, you can make stuff happen when an interaction with an object occurs. This is possible thanks to traps, but we’ll talk more about this in a sec.

To create a new Proxy you must provide a target and a handler. The target — as MDN states — can be any sort of object (i.e. object, array, function, or proxy). This is the one that will suffer the final side effects (if any) of the interaction with the proxy.

The handler is another object that defines traps. Traps are functions, set to act when an operation is performed on a proxy. Depending on the operation one trap or another is called. I like to think of traps as some sort of lifecycle hooks. If you’ve worked with a frontend framework (i.e. React), this will be nothing new. For all others — really? — a lifecycle hook is a callback that’s called in a certain point on the lifecycle of …aaaa component?, thingy?, whatevs.

Where was I? Of course, proxy traps. So for example, if you want to execute some code when a component is mounted, you’d need to implement the componentDidMount hook right? right? Well, with proxies you can do (almost) the same thing!

Implement the construct trap inside the handler and every time a new instance of the proxied-object is created, your code will execute!

Say you want to add a hook that executes when a property of the target is:

  • assigned? Use the set trap.
  • read? Use the get trap.
  • removed? Use the deleteProperty trap.
  • married? I don’t think there’s one, but hey! Who knows!

Validating objects with proxies

While I was documenting myself on proxies and their usage, I stumbled upon an example that used proxies for validation. The implementation seemed pretty straightforward and I figured it could make a pretty common use case — so what the hell — I started writing a small library.

Because I’m not up for reinventing the wheel I decided on using an existing library for all the validations. This allowed me to concentrate on the ES6 Proxy part and also have a wider, better, well tested and maintained validation. All thanks to validator.js; if you use a JS validation lib odds are you’re using it. If you’re not, go check it out here.

Build process

My first step as usual was to set up the build process. For this I chose Rollup.js. The config is quite simple and it was purposely made for building code for distribution.

Design decisions

As we’ve seen before, to create a Proxy we need two things: a handler, and a target.
That’s cool and all but what about validation? If we’re validating props inside an object we’ll need a validation schema. This validation schema should map each property with a set of rules that the assigned value should follow.

So what do we know so far? We need a handler, a target, and a validation schema.

But hey guess what!? That awesome lib we chose to handle validations, it also supports sanitizing. So why not add it to the mix? This can be handled in the same way as the validations, but we’ll need to provide a separate schema, a sanitizing schema.

Two schemas, the handler and the target.

A validator.js wrapper

I don’t really remember why I started writing this part of the code first, but I suspect it was because it was more familiar.

Knowing that I’d have a set of rules against which I should test an assigned value; my first step was to make a function that performed the tests and returned a result. The test can be either successful or not. Just returning whether the test passed or not won’t cut it. Proper validation entails telling the user what went wrong, meaning that an error object should also be returned.
Sanitizing is way easier in this sense, I just needed to apply the sanitizing rules to the input and return the final result.

Getting my hands dirty

Ok so the validation is all setup and running, let’s proxy the shit out of this.

What we need is a function that receives a validation schema, optionally also a sanitizing schema, and a target. And in turn returns a proxy object.

This means that the user will need to pass the schemas every time he wants to create a validated object. This can get real awkward, real fast. So why not split it into two steps?

We’ll create a factory function that creates the handler object and in turn returns another factory function that creates the proxy object.

Factorynception

So in other words what we have is a function that creates validators, and each validator creates a validated-proxied-object.

As you can see the actual implementation of the proxy is very small.

Using proxy-validator

Say we’re creating a contacts list and we want to validate some fields, how would we go about this? Easy enough, we define the corresponding ContactValidator schemas and we create our withValidation proxy.

More about proxies

Sindresorhus recently released a cool proxy observer:

You can find more about ES6 proxies in the following posts:

--

--

sorodrigo
HackerNoon.com

Reader. Developer. Student. RatPack-ish. Engineer @vizzuality.