Skip to content

Sync Function API

Chris Anderson edited this page Jun 3, 2013 · 8 revisions

The sync function is the core API you'll be interacting with on the Sync Gateway. For simple applications it may be the only server-side code you need to write. For more complex applications it will still be a primary touch point for managing data routing and access control.

Learn about channels and user authentication here and read this page for a detailed example.

Default function

If you don't supply a sync function we'll use this as a default:

function (doc) {
  channel(doc.channels);
}

Sync Function Arguments

The sync function has three arguments, which allow it to be used for validation as well as data routing.

function (doc, oldDoc, userCtx) {
  // your code here
}
  • doc

    The first argument is the document that is being saved. This will match the JSON that was saved by the mobile client and replicated to Sync Gateway. There are no metadata or other fields added, although the _id and _rev fields are available.

  • oldDoc

    If the document has been saved before, the revision that is being replaced will be available here. In the case of a document with conflicting revision, the provisional winning revision will be passed as the oldDoc parameter. Read more about Conflict Detection and Mangement.

  • userCtx

    This is an object that contains details about the currently authenticated user. It has these fields:

    • name

      This is the name of the user, which will be unique across the database, and which should not change over time. A common use for this field is to ensure that a field like doc.author is the same as userCtx.name so that it's not possible to impersonate another user by writing a document which lists someone else as the author. If the user is not authenticated, the name field will contain "" -- an empty string.

    • roles

      This field is an array of strings which the administrator can associate with a user. So for instance you can add the role "manager" to some of your users, and then grant access to some channels to all managers without having to grant it to all of them individually. In general granting channel access to roles instead of individual users will result in Sync Gateway maintaining more parsimonious internal data structures, giving improved performance. The reason roles is available here, is so you can also use role membership to control what kind of documents a user can write.

    • channels

      This field allows you to peek at the channels a given user can read from. Because the sync function determines which channels a document lands in, it can also prevent the user from writing to channels they can't access (if you choose.) Note that by default a user can write to any channel, even those they can't read.

Sync Function Calls

From within the sync function you create changes in the Sync Gateway configuration via callback functions. Each call manages a small amount of configuration state. It is also tied back to the document which initiated the call, so that when the document is modified, any configuration made by an old version of the document is replaced with configuration derived from the newer version. Via these APIs, documents are mapped to channels. They can also grant access to channels, either to users or roles. Finally, you can reject an update completely by throwing an error. The error message will be returned to the synchronizing client, which will log it or potentially display it to the user.

Validation via throw()

The sync function can prevent a document from persisting or syncing to any other users by calling throw() with an error object. This also prevents the document from changing any other gateway configation. Here is an example sync function which disallows all writes to the database it is in.

function (doc) {
  throw({forbidden : "read only!"})
}

The error object may be either a forbidden error (corresponding to an HTTP 403 error code) or an unauthorized error (corresponding to HTTP 401 error). The forbidden error should be used if the user is already authenticated, and they account they are syncing with is not permitted to modify or create the document. The unauthorized error should be used if the account is not authenticated. Some user agents will trigger a login workflow when presented with a 401 error.

A quick rule of thumb: most of the time you should use the throw({forbidden : "your message here"}) as most applications will require users to be authenticated before any reads or writes can occur.

Map a document to a channel with channel("my-channel")

The channel call routes the document to the named channel. It accepts either a string channel name, or an array of strings, if the document should be added to multiple channels in a single call. The channel function can me called zero or more times from the sync function, for any document. The default function (listed at the top of this document) routes documents to the channels listed on them. Here is an example that routes all "published" documents to the "public" channel, and all "unpublished" documents to a drafts channel specific to the channel author.

function (doc, oldDoc, userCtx) {
  if (doc.published) {
    channel("public");
  } else {
    channel("drafts-" + userCtx.name);
  }
}

Grant access to a channel, to a user

The access call grants access to channel to a given user or list of users. It can be called multiple times from a sync function. If the document that granted access is deleted, the access is revoked. Note that revoking access to a channel will not delete the documents which have already been synced to a user's device.

The access call takes two arguments, the user (or users) and the channel (or channels). These are all valid ways to call it:

  access("jchris", "mtv")
  access("jchris", ["mtv", "mtv2", "vh1"])
  access(["snej", "jchris"], "vh1")
  access(["snej", "jchris"], ["mtv", "mtv2", "vh1"])

Here is an example function which grants access to a channel for all the user's listed on a document:

function (doc, oldDoc, userCtx) {
  if (doc.members && doc.channel_name) {
    access(doc.members, doc.channel_name);
  }
  // we should also put this document on the channel it manages
  channel(doc.channel_name)
}

Grant access to a channel, to all users with a given role

TODO: this is not yet implemented

Add a role to a user's metadata

TODO: this is not yet implemented

Putting it all together

The best resource to see a full example is the Chat App Data Model article.

Clone this wiki locally