Zza! Zza! (Node/MongoDB)

Zza! is a single page application for ordering pizzas, salads and drinks.

This is the 100% JavaScript version of Zza! written for Node.js running Express with a MongoDB database.

It's built on the BMEAN stack:

Breeze client-side data modeling and data access
Mongo server-side document-oriented database
Express server-side web application framework on node
Angular client-side presentation framework
Node server-side platform for web applications

Breeze works well with Microsoft technologies as we've demonstrated many times. It also works well outside the Microsoft ecosystem. To drive that point home, this sample has ...

no C#
no Web API
no IIS
no Entity Framework
no SQL Server


Download the sample and unzip it..

Within the unzipped directory you will find:

  • a readme.md explaining how to install and run the app
  • the Zza! Mongo database
  • a folder full of HTML, CSS, and JavaScript

You can view, edit, and run the code using the tools of your choice.

Run it!

The readme.md has the details. Just remember the three basic steps:

  1. Start your MongoDB server: mongod
  2. Start the Node/Express server: node server
  3. Launch a browser and navigate to localhost:3000

The app should load on the home screen with a big picture of "Zza" himself. Click the "Order" link in the menu bar and you'll see what's on the menu beginning with pizzas.

Pizza menu

The "Salad" and "Drinks" links switch the view to similar views of those products.

You want the "Holy Smokes" pizza so you click on it. You're taken to a screen where you can pick the size and add toppings. Go for the "Large", pick the "Wheat Crust", and pile on some "BBQ Chicken" from the "Meat" toppings. You're hungry so why not order two of these babies?

The dashboard on the left displays that we're considering a "Holy Smokes" pizza but haven't put it in our cart yet. Click the "Add to Cart" button and it moves up the dashboard into the "Order Summary" panel.

Click the "View Cart" link (or the "Cart" button on the upper-right) and we'll see our order so far:

Go back to "Order" and load up on Salads and Drinks.

Inside the app

A single web page, index.html, frames the story. There's only a tiny bit of layout:

<body class="ng-cloak" data-ng-app="app" data-ng-controller="appController">

    <div data-ng-include="'app/views/shellHeader.html'"></div>
    <div data-ng-view style="margin-top: 50px"></div>
    <div data-ng-include="'app/views/shellFooter.html'"></div>

    <!-- 3rd party javascript libraries -->
    <!-- App javascript libraries -->


Angular.js is running the show. It asynchronously loads header and footer HTML.

In the middle, inside the <div> with the data-ng-view attribute, it dynamically loads a view based on a "route". The view could be the "Home" page with the picture of "Zza"; it could be the "About" page; it could be a product menu page, an order page, or the cart page. It all depends upon the route ... which you see in the address bar:

Pizza menu

In this example, the route identifies the pizza product whose id is "3". Angular uses route information (in public/app/services/routes.js) to download the correct view (a file in the public/app/views directory) and marry it to a controller (a JavaScript file in the public/app/controllers directory).

All the displayed values are data bound either to a controller (e.g., orderProductControler) or to a Breeze entity exposed by the controller (e.g., Product)

BreezeJS on the client

Breeze handles the application's data modeling and data access chores. "Entities" are JavaScript types constructed by a combination of developer code (in public/app/services/model.js) and Breeze metadata (in public/app/metadata.js).

The Breeze EntityManager and related components handle the details of creating, querying, materializing, caching, change-tracking, validating, and saving entity data.

While the controllers could interact directly with the Breeze EntityManager we prefer that controllers make data requests through a facade, here called the dataservice (public/app/services/dataservice). The dataservice provides the controllers with a higher level data abstraction, shielding them from application-specific details of working with Breeze.

The Zza dataservice API offers kind of looks like this:


That's what we mean by "higher level data abstraction". If it reminds you of the Unit-of-Work and Repository patterns ... then you've caught on to what we have in mind.

The Zza! client is server agnostic

The client-side code is virtually unaware of the server technology. Everything you've learned about programming in Angular and Breeze applies without alteration.

To prove that point, the exact same Zza! client - HTML, JavaScript and CSS - runs in front of a .NET server, hosted on IIS, running the Web API, Entity Framework, and SQL Server.

Exactly four files are different:

  1. metadata.js, reflecting the difference between a relational schema and MongoDB document schema.

  2. model.js, for much the same reason

  3. environment.js, a small configuration file

  4. /scripts/breeze.dataservice.mongo.js, a Breeze "dataservice adapter" plug-in that handles low level details of communicating with node and a MongoDB server.

Otherwise, the same client-code runs in radically different environments.

Node and Express

The web application server is written in about 60 lines of JavaScript (server.js), using the Express framework running on the node.js platform.

This server is either delivering static content to the client such as HTML, CSS, image, and JavaScript files or routing client data requests to a data access module called routes.js.

Breeze on the Server

The server-side data management component (routes.js) is developer-written, application-specific code for handling client data requests such as queries and saves.

Here you decide what data request your server will honor and implement the necessary business logic including validations.

This particular Zza! application server handles a small number of requests.

Most are so-called "namedQueries":

var namedQuery = {
    customers: makeVanillaCollectionQuery('Customer'),
    orders: makeVanillaCollectionQuery('Order'),
    orderstatuses: makeVanillaCollectionQuery('OrderStatus'),
    products: makeVanillaCollectionQuery('Product'),
    productoptions: makeVanillaCollectionQuery('ProductOption'),
    productsizes: makeVanillaCollectionQuery('ProductSize'),

    lookups: lookups

These are Breeze-enabled query endpoints that understand the OData query semantics familiar to Breeze client-side developers. For example, a Breeze client query such as:

.where ('name', 'gt', 'Caesar') // products whose name comes after `Caesar`
.orderBy('name')                // sort by product name
.skip(5).take(5)                // skip the first and take the second (pagesize = 5) 

becomes a GET request like this one:

http://localhost:3000/breeze/zza/products?$filter=name gt 'Caesar'&$orderby=name&$skip=5&$top=5

and returns a page worth of JSON product data such as this:

        _id: 31,
        type: "drink",
        name: "Cola",
        description: "Cola",
        image: "cola.jpg",
        hasOptions: false,
        isPremium: false,
        isVegetarian: false,
        withTomatoSauce: false,
        sizeIds: [

Plain old GET endpoints

This OData query facility is made possible thanks to the breeze-mongodb module that you installed with npm.

Your server doesn't have to support OData queries. You can simply return an object and let the Breeze client sort it out.

The lookups endpoint does just that. Its response to the client is a single object whose properties are arrays of the four reference entities (OrderStatus, Product, ProductOption, ProductSize):

function lookups(req, res, next) {                                                       
    var lookups = {};                                                                    
    var queryCountDown = 0;                                                              
    var done = processResults(res, next);                                                


    function getAll(collectionName, entityType) {                                        
        db.collection(collectionName, {strict: true} , function (err, collection) {...}                                                                                                                                                     

With this one call, the client receives all of the reference items it needs to populate comboboxes and drive pricing calculations. The Breeze client automatically shreds the package and stows these entity lists in cache.

It bears repeating that the "magic" in this call is all on the client. The lookups implementation is standard JavaScript with four standard MongoDB queries in the inner getAll function.

Learn more here about the breeze-mongodb module and programming with breeze, node, and MongoDB.

The Zza! Mongo Database

The Zza! MongoDB database is pre-loaded with ample sample data.

100 Customers
513 Orders
1447 OrderItems on those orders
1773 OrderItemOptions (toppings) on those OrderItems

41 Products in the catalog (pizza, salads, drinks)
64 ProductOptions such as pizza toppings
15 ProductSize objects that define sizes ("Large 14") and prices for products of each size
6 OrderStatuses

The Customer and Order... stuff are the transactional data representing the activity of the Zza! pizza parlor.

In a relational database, the Order, OrderItems and OrderItemOptions would be in separate tables, related via foreign keys.

This is a Mongo database with "collections" instead of "tables" and "documents" instead of "rows". A document can have nested sub-documents.

In this database, there is an Orders collection but no OrderItems or OrderItemOptions collections. Instead, each Order has an "items" array of OrderItem sub-documents. Each OrderItem sub-document has an "options" array of OrderItemOption sub-documents.

Here's an illustration with a sample data snapshot in the background:

Breeze metadata can describe complex documents like the Zza! Order document. Root documents - the objects like Order in MongoDB collections - map to Breeze EntityTypes. The sub-documents, which have no existence independent of their parent documents, map to Breeze ComplexTypes.

Breeze already supports scalar (single valued) complex types for relational databases. Relational databases don't have collection properties. Document databases do. So Breeze supports arrays of complex types.

Schema summary

The OrderStatuses, Products, ProductOptions, and ProductSizes are relatively static reference data. The application can't change them. Each has a document in its own MongoDB collection.

So the entire Zza! database consists of seven collections:


On the backlog

This sample is a work in progress. Among the features yet to be implemented:

  • Place the order (and save it to the database)
  • Client- and server-side validation
  • Query and review past orders (and those of other customers)
  • Deliver a delicious, hot pizza to your laptop

Breeze is ready to tackle the first three; we just need a little more time to work on the code. We haven't figured out the pizza delivery system yet. We're thinking about it.