Universal web back-end, offering an application-agnostic API, real-time collaboration, and conflict resolution.
TypeScript JavaScript Other
Switch branches/tags
Nothing to show
Clone or download
Latest commit 1406ef7 Mar 18, 2018
Failed to load latest commit information.
.vscode Be explicit about types. Mar 16, 2018
node Version 1.9.6 Mar 18, 2018
src New syntax for list of chained template functions. Array syntax will … Mar 18, 2018
test New syntax for list of chained template functions. Array syntax will … Mar 18, 2018
web Allow template function to use array of a single predecessor. Aug 28, 2017
.gitignore Publish typings to NPM. Mar 15, 2018
GruntFile.js Clean before build. Mar 18, 2018
LICENSE Initial commit Aug 12, 2015
README.md Cleaned up TOC. Nov 8, 2016
TemplateFunctions.md Documented template functions. Nov 7, 2016
alternatives.md Document alternatives to Jinaga. Sep 2, 2015
bower.json Finished removal of lodash dependency. Oct 18, 2015
callgraph Version 1.3.0: Resend messages when the connection fails. Jan 7, 2016
contributing.md Version 1.8.0. Loading indicator, and perform all joins in Mongo. Sep 8, 2016
mutability.md Use type convention in examples. Link to introduction video. Sep 3, 2015
package-lock.json Clean before build. Mar 18, 2018
package.json Clean before build. Mar 18, 2018
protocol.md Documented protocol, and removed it from the roadmap. May 3, 2016
roadmap.md Documented protocol, and removed it from the roadmap. May 3, 2016
security.md Reflect current implementation in security doc Nov 17, 2015
synchronization.md Published new Jinaga Distributor video. Sep 8, 2015
tsconfig.json Be explicit about types. Mar 16, 2018
tsd.json Finished removal of lodash dependency. Oct 18, 2015



JavaScript data synchronization, front-end and back-end, persistent and real-time.

Make a change in the browser and it is automatically sent to the server, saved to the database, and shared with other users. Jinaga is journaled isolated nodes approaching global agreement.

See an example application: ImprovingU - Course idea voting site.

Get a quick video introduction to Jinaga.


Universal Back End

Install the Jinaga NPM package.

npm install jinaga

Create a server.js.

var JinagaDistributor = require("jinaga/jinaga.distributor.server");
var MongoProvider = require("jinaga/jinaga.mongo");
var url = 'mongodb://localhost:27017/dev';
var port = 8888;

var mongo = new MongoProvider(url);
JinagaDistributor.listen(mongo, mongo, port);

Start Mongo.


Run the application.

node server

Your back end is now running. Spend the rest of your time on the front end.

Front End

Your front end application saves facts whenever the user does something. These facts are persisted to your back end, and shared in real-time with other browsers.

Install the client-side library.

bower install jinaga

Include the script in your page.

<script src="bower_components/jinaga/jinaga.js"></script>
<script src="main.js"></script>

Create facts inside of main.js.

var j = new Jinaga();
j.sync(new JinagaDistributor("ws://localhost:8888/"));

  type: "Task",
  list: {
    type: "TodoList",
    name: "Chores"
  description: "Take out the trash"

This code actually represents two facts. The first is:

var chores = {
  type: "TodoList",
  name: "Chores"

The second is:

var trash = {
  type: "Task",
  list: chores,
  description: "Take out the trash"

The second fact knows about the first. That makes the first fact (chores) a predecessor to the second (trash). The order between these two is known. Chores will always come before trash. But facts with no such relationship can occur in any order.


Now that you can express that chores is a predecessor of trash, you might want to watch for all facts with that same predecessor. These are called successors. For example, you might want to find all of the tasks on the chores list. Provide a template that all desired facts will fit. For example, for a given list, this template will match all tasks on that list:

function tasksInList(l) {
  return {
    type: "Task",
    list: l

When a fact matching the template is found, we want to call a function. We'll watch the template, and call a function when the results change.

function taskAdded(task) {
  // Render the task in the UI

j.watch(chores, [tasksInList], taskAdded);

Now if I add a new task to the list, the taskAdded function will be called.

var dishes = {
  type: "Task",
  list: chores,
  description: "Empty the dishwasher"


// taskAdded is called with the dishes fact.

A callback is only executed once per fact. If you happen to add the same fact again, the callback will not be invoked.

The watch is in effect for the current session. It is destroyed implicitly with the Jinaga instance when no longer in use. If you wish to explicitly stop watching, capture the return value.

var watch = j.watch(chores, [tasksInList], taskAdded);

// Some time later...



Now I want to mark a task completed. Let's capture that as another fact.

  type: "TaskCompleted",
  task: trash,
  completed: true

Let's write a template that matches this fact for a given task.

function taskIsNotCompleted(t) {
  return j.not({
    type: "TaskCompleted",
    task: t,
    completed: true

Now we can write a query so that only the uncompleted tasks match the template.

function uncompletedTasksInList(l) {
  return j.where({
    type: "Task",
    list: l
  }, [taskIsNotCompleted]);

j.watch(chores, [uncompletedTasksInList], taskAdded);

When this query is created, only the existing tasks that have not been completed will be added to the UI. So you will see "Empty the dishwasher", but not "Take out the trash". But what about when a task changes? We want to remove tasks from the UI once they are marked completed. So let's add another callback to the query.

function taskRemoved(t) {
  // Remove the task from the UI

j.watch(chores, [uncompletedTasksInList], taskAdded, taskRemoved);

Now when you complete a task, the taskRemoved function will be called to remove it from the UI.

  type: "TaskCompleted",
  task: dishes,
  completed: true

// taskRemoved is called with the dishes fact.

Facts are immutable, so there is no callback for updating a result. They can only be added or removed. To handle updates, please see Mutablity.