Web Development Blog

Server-side metadata with Node and Breeze

I recently came across a library called Breeze, which (to sum up very briefly) is a client side JavaScript library that allows entity based data management; a kind of front-end ORM.

A solution to complex client-server data management

If you haven’t heard of Breeze, I’d really recommend checking out the introductory documentation before continuing as it explains the core concepts much better than I could hope to.

I only have some cursory experience with it in the form of a toy app I’m writing to try it out and so I can’t promise that everything said here is completely accurate or an example of best-practice.

And then I hit a problem…

One thing that I came across whilst looking through the available NodeJS examples is that all of the metadata is defined on the client. That’s all well and good when you’re dealing with a tightly coupled client-server application, but what if you want your server to be the backend for a multitude of different (but related) client applications?

Even when considering an application that consists of a single client component, I would still argue that the entity definitions that make up the resultant metadata should live on the server, rather than the client.

When it comes to having Breeze communicate with and fetch metatdata from a NodeJS backend, I couldn’t find anything in the otherwise verbose documentation explaining how to go about doing this. Luckily, I only floundered for a short while until I found a simple and straightforward way of handling this.

RTFM

Before continuing, I suggest having a read through the Breeze documenation on writing metadata by hand. It’s focussed on defining the metadata on the client application, however the process is very similar when doing the same on the server and so you should be at least familiar with the general process.

First of all, a quick recap on how you would write metadata by hand on the client application.

  • Write entity definitions
  • Create a MetadataStore
  • Use the Breeze MetadataHelper to add the entities to that store.
  • ???
  • Profit

When it comes to defining the metadata on the server, the process is largely similar but with some caveats and a few extra steps.

First of all, we need to make Breeze available to our Node application, then we can follow the above steps of using the MetadataHelper to add entitiy definitions to a MetadataStore. Finally, we’ll need to create an endpoint that returns the result of calling myMetadataStore.exportMetadata(). That method will return an object that Breeze clients can consume and populate their own MetadataStores.

Making Breeze available to your Node application

This part is nice and simple. The breeze-serverside NPM module allows you to import both Breeze and the MetadataHelper object into your Node application.

$ npm install --save breeze-serverside

Now, from our Node app we can do the following:

// metadata.js

var breeze = require('breeze-serverside'),
    namespace = 'my.Model',
    keyGen = breeze.AutoGeneratedKeyType.Identity,
    metadataHelper = new breeze.config.MetadataHelper(namespace, keyGen),
    store;
    

// Entity definition(s)
function defineCustomer() {
    return {
    	name: 'Customer',
        
        dataProperties: {
        	_id: {
            	type: 'MongoObjectId',
                isPartOfKey: true
            },
            
            name: {
            	type: 'String',
                required: true,
                max: 100
            }
        }    
    }
}

// Add definition(s) to a Metadata store
store = new breeze.MetadataStore();

metadataHelper.addTypeToStore(store, defineCustomer());

// Export the generated metadata
module.exports = store.exportMetadata();

Then, when creating your endpoint (in this case, using Express):

// routes.js
// assume `app` refers to an already created Express instance

app.get('/breeze/Metadata', getMetadata);

function getMetadata(request, response, next) {
    // Include the metadata we created above
    var metatdata = require('./metadata');
    
    // Set any response headers...
    
    // Send a JSON encoded representation of the metadata
    response.send(JSON.stringify(metadata));
}

The above example is purposefully terse but you can see a more robust implementation of this here.

…and it still doesn’t work

There’s one final piece to this puzzle… If you try to run the above, Node will crash and burn as we try to add entities with an ID of type ‘MongoObjectId’. Breeze doesn’t know what that is!

If you’re not using Mongo for your back-end, then you can safely ignore this next part (though you should probably choose something else as an entity ID…).

In short, we need to tell Breeze what a ‘MongoObjectId’ is so it can handle our entities properly.

I’m sure there’s a nicer way of doing this, but I struggled to find a more workable solution. In the depths of the Breeze MongoDB adapter I found where this elusive ‘MongoObjectId’ is defined. Unfortunately, we can’t include this directly into our Node app and as far as I can see, there is no NPM module containing it.

So, my less-than-elegant solution to this (hopefully temporary) problem is to use almost the same snippet of code from the MongoDB adapter to define it directly in the application.

For example:

// register-mongo-object-id.js
// This would run before `metadata.js` and `routes.js`.

var breeze = require('breeze-serverside'),
    ObjectID = require('mongodb').ObjectID;
    
function registerMongoObjectID() {
    function fmtOData(val) {
        return val === null ? null : "'" + val + "'";
    }

    function getNextObjectID() {
        return new ObjectID();
    }

    breeze.DataType.MongoObjectId = breeze.DataType.addSymbol(
        {
            defaultValue: '',
            fmtOData: fmtOData,
            getNext: getNextObjectID
        }
    );
}

module.exports = registerMongoObjectId;

Now, when adding our entity definitions using the MetadataHelper, Breeze will know just what a ‘MongoObjectId’ is and how to handle it.

Famous last words…

Now, you can configure your Breeze client applications to fetch metadata from this endpoint and it should all just work….

Final thoughts

Hopefully this has been a help to somebody. I think Breeze is fantastic and hope I get to use it in some real projects, though it would be nice for it get some more love in terms of examples and documentation for getting it working with technology outside of the .NET stack.

Have you worked with Breeze and NodeJS? If so, how did you handle exposing metadata to clients?

Either way, it would be great to hear your thoughts on this as I may well be completely off the mark. ;)

Comments

comments powered by Disqus