Modelling Cloudant data in TypeScript
Using TypeScript classes in your code and saving their JSON representation in a Cloudant database.
TypeScript is programming language that is a super-set of JavaScript written by Microsoft. TypeScript code compiles to various flavours of JavaScript to run in the browser, in Node.js or in concert with frameworks such as React, Angular, Vue.js etc. Programming in TypeScript brings you the luxury of:
- type checking for function parameters, return values etc
- default and optional values for function parameters
- interfaces, for defining the shape of objects being passed in and out of functions
- classes with constructors, inheritance & access modifiers
- enumerations
- iterators
- module import/export
- and lots more
Ultimately your TypeScript code is transpiled into JavaScript, targetted for your destination platform, so it can’t do any more than JavaScript. The advantage of TypeScript code is that you get to apply greater rigour in your code, have access to lots of “grown up” programming features and a receive lots of help in the code editor.
In the above example, the red line indicates that there is no ‘postcode’ property of the Address
class. Catching mistakes like this in the editor saves lots of time later, chasing down errors and figuring out why the code isn't doing what is expected.
Typescript is open-sourced and Microsoft’s popular VS Code editor is itself written in TypeScript!
Data modelling with Cloudant
Let’s say we want to store employee records in Cloudant. As Cloudant stores JSON objects, we can use plain JavaScript objects in our code that are transformed to JSON with JSON.stringify
:
To take advantage of the features of TypeScript, it would be better if we could use classes instead of generic objects. We could build:
- a Date object to represent the time the employee joined.
- an Address object with custom methods that formats address lavels.
- a TagsCollection object with methods that allow new tags to be added and prevents duplicates.
Ideally, an Employee class containing these building-block classes would have a usuable JSON representation that could be stored in Cloudant. In addtion, the JSON string could be returned to a concrete class after retrieving data from the database.
There is a solution to this, so let’s start with a custom Address class.
Address class
We can create a custom TypeScript class that embodies a postal address and geo-location:
It’s worth explaining that a TypeScript Interfaces define the forms of objects — the attributes and types that make up an object. They are useful when defining functions that take objects as parameters — this allows the TypeScript compiler to check the form of the object at compile-time and allows your editor to check your code as you type.
This Address object is pretty simple. It has a number of public attributes that define the address, a getLabel
function that returns a postage label and a static fromObject
function that can resurrect an Address object from a generic object that conforms to the iAddress interface.
A simple class like Address that only uses JavaScript primitive types turns into a sensible form of JSON — an object which looks like the iCloudantDate interface:
We’ll see how to incorporate this into our Employee class later, but first let’s look at how we can use a similar technique to model a date.
The CloudantDate class
In this blog post, I discussed various ways of representing a date/time in Cloudant JSON. I concluded that it’s good to have the constituent date/time parts (day, month, year) present in the object to allow Cloudant Query to access them and a “seconds since 1970” integer or an ISO-8601 string for sorting purposes.
To represent a JavaScript Date object in our Employee class, we’re going to create a wrapper class called CloudantDate which has a trick up its sleeve:
The CloudantDate class stores its value in a standard JavaScript Date class and like Address, implements a static fromObject
function to allow a class to be reconstituted from an plain object. CloudantDate's real party piece is overridng the toJSON
function, which is used by JSON.stringify
to turn an instance of the class into JSON.
If we create a CloudantDate object and JSON.stringify
it, the resultant string contains all the date and time pieces broken out:
This allows us to have dates that are useable Date
classes in our code and have the constituent parts broken down in the Cloudant database, where they are accessible to the Cloudant indexing engine.
TagCollection class
Our final storage class is TagCollection which stores an array of strings. It has a couple of helper functions (add
and remove
) and also overrides the toJSON
function to ensure that only an array of strings appears in the JSON:
Assembling the Employee class
Now we have all the pieces in place, we can create a top level “Employee” class that uses our custom classes:
We have a base class that handles the Cloudant-specific fields (_id
, _rev
etc) which we can reuse across all of our Cloudant storage classes:
We can then use the Employee class in our own code and pass instances of Employee to the official Node.js Cloudant library to be stored in the database:
The Cloudant library converts the our custom object to JSON, which invokes our custom toJSON
overrides:
The Cloudant document can be restored into an Employee class later, if we use our static Employee.fromObject
method to reconstitute the class from its JSON form:
Typescript conclusions
Writing in TypeScript adds some of the rigour of writing Java code to JavaScript, including strong typing and classes with inheritance & access control. This allows the developer to build neat, consistent code with the code editor advising at every turn. TypeScript is transpiled, whether your target is a minified package for a web app, a server-side Node.js module or a native Electron app.
Overriding the toJSON
function in your custom TypeScript classes allows you to use classes in your code and pass them to the Cloudant Node.js library to be saved in a form of JSON of your choice.