Protect Your JavaScript Applications from Api Data

Sanitizing API data for your applications

Photo by James Pond on Unsplash

In this article I am going to talk about one of the most important things you can do when developing a client-side application.

You need to sanitize your API data before passing it into your application.

Basically you need to take the JSON you get from the API and fill in any missing data with default values. The biggest culprit is when you are working with array data. For security reasons, some APIs won’t return a property if there is no data for that property which will cause your data to be undefined . Also some APIs will return a single object instead of an array if there is only one item, which makes it painful when expecting consistent data types.

I’ve worked with a lot of new and legacy APIs and I can say passing raw data from an API directly into your application is a bad idea so be proactive and not reactive. I’ve created a library to handle these situations. First, lets see how we can handle this without my library:

It is painful to setup all your models this way.

The example below uses my library sjs-base-model and does the same thing as above. Notice how the PersonModel extends BaseModel and calls an update method in the constructor. This takes care of matching up the properties on the data with the class properties. It also handles creating the LocationModel and CarModel if there is data for those properties.

Let’s break it down. Behind the scenes BaseModel takes the data that is passed in and matches it to the properties on the class. This is facilitated via the update method that exists on the BaseModel class. When there isn’t any data, the BaseModel uses what you have set for initial/default values.

Let’s look closer at how the model creations work for these two:

address = LocationModel;

cars = [CarModel];

Notice how we are assigning a reference to the models and we are not instantiating them. Creating the models will happen in the update method of the BaseModel. If address has data then it will create a LocationModel otherwise it will be assigned null . For the cars property, if there is array data or a single object an array of CarModel‘s will be created and if the data is undefined then an empty array will be assigned. One caveat is all models need to extend BaseModel, so LocationModel and CarModel both extend BaseModel.

That’s it! A simple solution to a potential messy problem. No need for conditional logic throughout your app checking for undefined data. Now you can be confident you are working with expected data.

BaseModel Best Practice

It’s bad practice to add too much code within the constructor, so the way I always setup my models with BaseModel is to override the update method and do my extra stuff in there. The example below has an update method on the PersonModel and we call super.update(data) right away. This is called inheritance and it allows us to override and/or add extra functionality to a class we are extending. Notice the new fullName property and how we update the value after the super.update is call.

The beauty of this is now you can call the update method after instantiation:

You only need to pass the data you want to update when using a model that extends BaseModel the rest of the data on the model will stay the same.

For example, if you wanted to update the nested address model you can do the following:

person.update({firstName: 'John', address: {city: 'Springfield'}});

Pretty super kickass awesome if I don’t say so myself.

BaseModel with TypeScript

Lets full type our model with TypeScript.

One thing to notice is the use of as any when assigning the model reference to the property. This is so the compiler doesn’t complain because TypeScript is expecting an instantiated model and not a typeof model. Notice LocationModel as any; and [CarModel as any];

In the above example you might notice the readonly in front of the property name. You can remove this but I typically use it with api data models because I don’t want anyone altering the data. Plus, in case I need to send the data to another api, it will be in the same format as a received it.

Conclusion

sjs-base-model alleviates the pain of initializing data the API might not send, and also makes it easier to maintain consistent data types throughout your application.

Check out my repo for more information:

Please give this article 5, 10, 50 claps and/or follow me if want to see more.

This article is related to another one I wrote: