For technical questions, please go to StackOverflow with the tag “breeze”. StackOverflow is a fantastic site where thousands of developers help each other with their technical questions.

We monitor the [breeze] tag on the StackOverflow website and do our best to answer your questions.
The advantage of StackOverflow over the GitHub Wiki is the sheer number of qualified developers able to help you with your questions,
the visibility of the question itself, and the whole StackOverflow infrastructure (reputation, up- or down-vote, comments, etc).

Client side Requirements:

Standard Breeze requirements:

Breeze.debug.js or Breeze.min.js

Q.js

Ajax library - usually jQuery

Additional MongoDB requirement:

Breeze.dataservice.mongo.js - a Breeze dataservice adapter that handles all of the MongoDB -specific client-side work involved in communicating with the MongoDB–backed service. Currently available on github and in the Zza! Node/MongoDB sample.

Server side requirements

breeze-mongodb from npm - a node package that handles all of the server-side details of communicating between a Breeze client and a MongoDB-backed service.

npminstallbreeze-mongodb

Basics

A Breeze client side application talking to a MongoDB backend will, for the most part, be indistinguishable from the same app written to talk to a Entity Framework/SQL database backend. Most of the differences that do exist will be because of differences in how your data structures are modeled. This is discussed in more detail later.

You will write Breeze specific code in your Node server application to handle the following types of operations.

Querying

Saving

Provide metadata.

Breeze requires Metadata to describe each of the entity types that it manages. This metadata can either be returned from a server as a result of a request for the “Metadata” resource or it can be specified directly on the client via a call to any number of MetadataStore methods.

Breeze provides an npm install that will assist in writing the code for each of these operations.

Mapping MongoDB to Breeze

Breeze maps MongoDB documents as follows: ( See the Breeze Metadata page for more information on this topic).

Top level MongoDB documents are represented as Breeze Entities.

Embedded sub-documents are represented as Breeze ComplexTypes.

A MongoDB document is a JavaScript object. Many of its properties will be simple scalar properties such as “name” and “quantity”. But some properties can return sub-documents (objects) or arrays of simple data types (e.g., strings and numbers).

Breeze represents sub-documents as ComplexTypes. A property could return a single instance of a ComplexType (isScalar=true) or an array (isScalar=false) of such instances.

A property that returns one or more simple data types is a Breeze DataProperty, In MongoDB, that property returns either a scalar (isScalar=true) or an array (isScalar=false) property.

Breeze/MongoDB - Server side processing

For the remainder of this document we show examples of custom server-side code that implement querying, saving and retrieving metadata. All of these examples are shown in the context of a node express application. Please look here to learn more about express .

varexpress=require('express');varapp=express();varroutes=require('./routes');// refs a routes.js file where most of our code will be written.app.use(express.bodyParser());app.use(app.router);app.use(errorHandler);

Almost all of the Breeze/MongoDB code shown in these examples is assumed to be part of a “routes.js” file. Below is the beginning of such a file that opens a MongoDB database called “MyNorthwindDatabase”.

Assume a MongoDB server is running with access to this database.

routes.js

varmongodb=require('mongodb');// MongoDB support package varbreezeMongo=require('breeze-mongodb');// Breeze MongoDB support packagevarfs=require('fs');// Access to the local file system.// Connect to a database.varhost='localhost';varport=27017;vardbName='MyNorthwindDatabase';vardbServer=newmongodb.Server(host,port,{auto_reconnect:true});vardb=newmongodb.Db(dbName,dbServer,{strict:true,w:1});db.open(function(){});

// route definitions begin here …

Querying with MongoDB

Let’s start on the breeze client which makes a query request to the server. Then we’ll see how routes.js redirects that request to the proper method for query processing.

Client side

Tell Breeze that you’re using MongoDB and everything else is standard Breeze. Put the following line somewhere in your application bootstrapping logic:

breeze.config.initializeAdapterInstance("dataService","mongo",true);

Querying a MongoDB database from a Breeze client involves nothing more than a standard Breeze EntityQuery such as this one:

The real point here is that, in general, you cannot tell by looking at the client side code what backend datastore is behind any Breeze query.

Breeze does not yet support querying a MongoDB sub-document directly although it will return one as part of its parent. The ability to query sub-documents will be added in a future release.

Server side

In order to provide the most basic support for Breeze the minimum necessary requirement is simply that you give Breeze an endpoint and then route Breeze to this endpoint.

The routing could look something like this:

app.get('/breeze/Northwind/Products',routes.getProducts);

Thus a Breeze EntityQuery with “Products” in the EntityQuery.from clause is directed to the getProducts method in the routes.js file.

getProducts illustrates the typical implementation of a query processing method:

exports.getProducts=function(req,res,next){// convert a client OData-style query string in the request to a MongoDB queryvarquery=newbreezeMongo.MongoQuery(req.query);// add custom server-side filtering to the query object here...// execute the MongoDB query with a callbackquery.execute(db,"Products",processResults(res,next));}// Return the query callback function// res is the HTTP response object// next is the Express HTTP pipeline callbackfunctionprocessResults(res,next){// return a function to process the results of the query// here we simply compose a response with the query results as contentreturnfunction(err,results){if(err){next(err);}else{res.setHeader("Content-Type:","application/json");res.send(results);}}}

This is the standard template for most queries. The processResults method can be reused by all of the query methods discussed in this document.

Inside the query method

getProducts composes a query object by parsing the OData-style parameters that the Breeze client has passed in the URL query string and turning them into an equivalent MongoDB query expression. These implementation details are handled automatically by the Breeze MongoQuery class that you imported when you called “require(‘breeze-mongodb’)”.

The Breeze MongoQuery.execute receives three parameters: the database object (db), the name of a MongoDB collection in “MyNorthwindDatabase”, and a callback to process the results returned by MongoDB.

You can further constrain or augment the client query by modifying the Breeze query object before executing it. Continuing with our Products query, we may wish to ensure that no ‘discontinued’ products are ever returned. We’d specify that constraint with the query.filter property.

If the query fails, you can return an exception to the Breeze client as we did above. The response will have a “500-Internal Server Error” status code.

That’s not really the correct response in this case. The server didn’t fail; the client made a bad request. You should probably say so … and you can … by calling next with an error-indicating response object as seen in this revision to getProductsCostingMoreThan.

You can specify a query projection on the server via the query.select property. The following query first applies any client side filters and then strips the results down to the CompanyName and _id properties.

The client may not know that the “customerWithScalarResult” resource name returns a “Customer” entity. The query.resultEntityType property above tells the Breeze client to expect “Customer” entities. Without that advice, the client might have to map in metadata this “customerWithScalarResult” endpoint - and every other unusually-named endpoint - to its corresponding entity type.

Each of the MongoQuery properties mentioned above ‘filter’, ‘select’, ‘options’ corresponds directly to the parameters of the MongoDB collection.find method.

But you may prefer to redirect most (perhaps all) client queries to a single query endpoint as follows:

...afterotherroutes...app.get('/breeze/Northwind/:slug',routes.get);

The “:slug” is a placeholder. An HTTP GET request for an URL such as /breeze/Northwind/Customers or /breeze/Northwind/Employees would be routed to the same routes.get method which might look like this:

These are methods that you write and breeze calls just before saving the data to the MongoDB database.

exports.saveChanges=function(req,res,next){varsaveHandler=newbreezeMongo.MongoSaveHandler(db,req.body,processResults(res,next));// write one or both of the following// saveHandler.beforeSaveEntity = myCustomBeforeSaveEntity;// saveHandler.beforeSaveEntities = myCustomBeforeSaveEntities;saveHandler.save();};

You can define one or both of these methods. Breeze first calls beforeSaveEntity for every entity in the save-set and then calls beforeSaveEntities.

beforeSaveEntities - review and possibly modify the entire collection of entities to be saved (the “save-set”). You can modify or remove any of them. You can add more entities-to-save, potentially of types not included in the original save-set.

Entities in the save-set are each augmented with an entityAspect property. This server-side entityAspect has the following properties:

entityTypeName: The name of the entityType associated with this entity.

entityState: A string indicating the save operation to be performed on this entity; one of the following strings: “Added”, “Modified”, “Deleted”

originalValuesMap: an object hash whose key/value pairs identify the property that changed (key) and the original value of that property (value). The originalValuesMap is only defined for entities with an entityState of “Modified”.

forceUpdate: When true, Breeze updates the database object with every property of a “Modified” entity, not just the properties enumerated in the originalValuesMap.

beforeSaveEntity

Breeze doesn’t define this method; you do. Breeze calls your custom implementation of the beforeSaveEntity interceptor once for each entity in the save-set.

Save Example #1:

Ensure that every new Product we add to the database has at least a $.50 surcharge.

beforeSaveEntities

Your beforeSaveEntities method is granted access to the entire save-set through several public properties on the MongoSaveHandler instance. The MongoSaveHandler instance is the this object within your beforeSaveEntities function.

Pertinent MongoSaveHandler instance properties and methods include:

saveMap: an object hash keyed by EntityType name where the value is an array of entities to be saved for the given EntityType. You can add and remove entities from this map.

qualifyTypeName(entityTypeName): creates a fully qualified EntityType name based on the default namespace associated with this saveHandler. You may need this function to find entities of a particular type in the saveMap; see the example below.

saveOptions: a copy of the Breeze client-side SaveOptions instance that can be optionally provided during the EntityManager.saveChanges call. You can pass arbitrary data from the Breeze client to the server-side saveChanges call in the SaveOptions.tag property.

metadata: an object hash keyed by EntityType name where the value for each key is the metadata associated with that EntityType. There must be a single entry for each EntityType that is referenced within the saveMap. Breeze automatically populates this metadata object for each EntityType in the client’s save-set. A metadata entry has the following properties:

defaultResourceName: The default ResourceName for this EntityType, this is often the MongoDB collection name.

dataProperties: An array of all of the DataProperty specs for this EntityType.

You will need the following methods if you add a new entity to the saveMap during your beforeSaveEntities call.

addToSaveMap(newEntity, entityTypeName, entityState).

registerEntityType(entityTypeName, mongoCollectionName, autoGeneratedKeyType, dataProperties) You must call this method first if you want to add an entity to the savemap and its EntityType is not already one of the saveMap keys.

beforeSaveEntities has an asynchronous signature. Breeze calls your method with a single argument, the callback function to invoke when your beforeSaveEntities has completed its evaluations of and changes to the saveMap.

beforeSaveEntities must be asynchronous because your method will probably make asynchronous calls of its own. For example, when you cannot trust data from the client (and you usually can’t), you should retrieve current data values from the database and compare them with the proposed save-set values from the client. All MongoDB queries are asynchronous.

Saving entities that are not in the client “saveMap”

You can save changes to entities that the client did not include in the saveMap. For example, you might log changes to entities in an Audit table by creating “audit entities” on the server. You’ll want to create them and add them to the “saveMap” with addToSaveMap so that they are saved as part of the “transaction”.

MongoDB doesn’t support real transactions so were using the word loosely, meaning “at the same time as”.

You could also add entities to the “saveMap” in “Modified” or “Deleted” state, meaning that these entities exist in the database and are to be updated or deleted.

If you add a “Modified” entity, you must tell Breeze which properties of the entity to update. If you don’t, breeze won’t update the entity at all! The easy way is to set the entity’s entityAspect.forceUpdate to true; breeze then will update every property.

If instead you want to update only a few specific properties, set the originalValuesMap to an object hash that identifies those properties as shown here:

thing.entityAspect.originalValuesMap={foo:null,// only the property name ('foo') matters ...bar:null// ... not the value.}

Breeze uses the originalValuesMap keys to determine which properties to save to the database; it ignores the original values themselves.