Couchbase Mobile

First steps with PouchDB & Sync Gateway

This week, PouchDB v3.4.0 was released with Couchbase Sync Gateway compatibility.

In this post, we’ll take the existing TodoMVC example and add filtered sync using Facebook authentication. In addition to syncing between Web clients, we’ll change the data model slightly to sync with the existing ToDoLite apps running on iOS and Android:

To follow along, you can open and run the TodoMVC example at commit d9bb961. For convenience, I’ve added the Facebook login code in app.js. When the user logs in, the function startSessionAndSync(accessToken, userId) is called. Now let’s add the code to make it happen!

QuickStart

Make sure to serve the app on port 9000 as we will configure CORS for localhost:9000 later. The simple python command should do:

$ python -m SimpleHTTPServer 9000

Data modelling

TodoMVC and ToDoLite have a slightly different data model. In ToDoLite apps, a user can create multiple lists and share them with multiple users.

First let’s look at a Task document with the expected title property and a list_id referencing the List it belongs to (in this case 123):

{
  "_id": "E9W3-9I2Y-O8W2-6Y4D",
  "type": "task",
  "list_id": "123",
  "title": "A task title"
}

Likewise, a List document also has a title and an owner referencing the user it belongs to:

{
  "_id": "123",
  "type": "list",
  "title": "TodoMVC list",
  "owner": "p:1234567890"
}

TodoMVC however is a single list app. To keep things simple we can insert the List document in code as soon as the user logs in.

Let’s create a new function called migrateGuestToUser to create the List document with the user id and save it to PouchDB:

function migrateGuestToUser(userId) {
  var list = {
    _id: '123',
    type: 'list',
    title: 'TodoMVC list',
    owner: 'p:' + userId
  };
  db.put(list, function(err, result) {
    if (!err) {
      console.log('Successfully saved user list');
    }
  })
}

Note: It’s very important to set the owner field, the sync function will reject the document otherwise.

And we can call it in startSessionAndSync:

function startSessionAndSync(accessToken, userId) {
  migrateGuestToUser(userId);
}

Task documents belong to a list and so they have a list_id property we need to set. Change the addTodo function in app.js to look like below. Notice we set the list_id field to the hardcoded _id of the list we inserted above:

function addTodo(text) {
  var todo = {
    _id: new Date().toISOString(),
    title: text,
    checked: false,
    type: 'task',
    list_id: '123',
    created_at: new Date()
  };
  db.put(todo, function callback(err, result) {
    if (!err) {
      console.log('Successfully posted a todo!');
    }
  });
}

Now we have the appropriate documents conforming to ToDoLite’s data model we can take a look at authentication and replication.

Enabling CORS on Sync Gateway

In this tutorial, we’ll run a local instance of Sync Gateway on localhost:4984 but we’re serving our web app on localhost:9000. At this point we’d get a same origin policy error. But Chris recently added CORS support to Sync Gateway for this purpose. So we don’t have to write one line of server side code :)

CORS allows web apps to access resources on other domains than the origin domain. By enabling CORS on Sync Gateway we’re telling the browser “Yes, the Sync Gateway domain name is allowed to communicate with this web app”. Open sync-gateway-config.json with the extra CORS configuration to enable it on localhost:9000:

{ 
 ...
 "CORS": {
   "Origin": ["https://localhost:9000"],
   "LoginOrigin": ["https://localhost:9000"],
   "Headers": ["Content-Type"],
   "MaxAge": 17280000
 },
 ...
}

Use Sync Gateway 1.1 or later. Start it with the config file:

$ ~/Downloads/sync_gateway sync-gateway-config.json

Back in app.js, update the remoteCouch url accordingly:

var SYNC_GATEWAY_URL = 'https://127.0.0.1:4984/todos/';
var remoteCouch = SYNC_GATEWAY_URL;

Now if we tried to sync the list to Sync Gateway we’d get a 401 Unauthorized error. Let’s fix that by creating a user session with Facebook login.

Authenticating with Sync Gateway

To authenticate with Sync Gateway we can send a POST request to /todos/_facebook with the access token, if we get back a 200 OK, the browser will set the session cookie returned from Sync Gateway for future push/pull replications.

function startSyncGatewaySession(accessToken) {
  var request = new XMLHttpRequest();
  request.open('POST', SYNC_GATEWAY_URL + '/_facebook', true);
  request.setRequestHeader('Content-Type', 'application/json');
  request.onreadystatechange = function() {
    if (request.readyState == 4 && request.status == 200) {
      console.log('New SG session, starting sync!');
      sync();
  };
  request.withCredentials = true;
  request.send(JSON.stringify({"access_token": accessToken}));
}

Note: It’s important to set request.withCredentials = true in a CORS request to save the cookie returned from Sync Gateway for future authenticated requests (push/pull replications).

Call it in startSessionAndSync passing the accessToken we got back from Facebook:

function startSessionAndSync(accessToken, userId) {
  migrateGuestToUser(userId);
  startSyncGatewaySession(accessToken);
}

Wrap up

Now open up your browser, you can test todo items are syncing with another browser window opened or the native apps running TodoLite.

Users can still create tasks without being logged in. But the minute a user logs in, the tasks belong to the user’s list. Providing a guest account feature is one of the many benefits of building for offline-first capabilities.

Notice the TodoMVC list document is displayed as a task in Chrome. That’s because this example was using the allDocs query to display tasks.

In the next post, we’ll use Map/Reduce queries to add the multi-list capability and a profile document as well to share them with other users.

Read more:

Share this article

Author

James Nocentini is the Technical Writer in charge of the documentation for Couchbase Mobile. Previously, he worked as a Developer Advocate and before that as a front-end developer for HouseTrip. He also enjoys writing Android tutorials for raywenderlich.com in his spare time.

9개의 응답

  1. Nuthan Santharam 아바타
    Nuthan Santharam

    Hello James, Thanks for the tutorial. Any idea how to enable CORS on sync gateway for Windows machine?

    the build of commit e8cf146. Can I execute this on a Windows machine?

    When I try to run the sync_gateway with the json config, It throws:

    XMLHttpRequest cannot load https://localhost:4984/todos/_facebook. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://localhost:9000’ is therefore not allowed access.

    Thanks,
    Nuthan.

  2. Florion COIFFÉ 아바타
    Florion COIFFÉ

    I am using Sync Gateway that I build from the current master on Github. Is this supposed to include CORS and PouchDB support ?
    For me it doesn’t, the browsers tells me “No ‘Access-Control-Allow-Origin’ header is present on the requested resource”.

    In my config.json file I added the CORS lines:

    “CORS”: {
    “Origin”:[“https://185.90.50.35:80”],
    “LoginOrigin”:[“https://185.90.50.35:80”],
    “Headers”:[“Content-Type”],
    “MaxAge”: 1728000
    }

    Do you have any idea why ? :)

  3. hamrounez 아바타
    hamrounez

    CORS is not working for me !!!! still No ‘Access-Control-Allow-Origin’
    any solution?

    1. James Nocentini 아바타
      James Nocentini

      Are you using Sync Gateway 1.1? You can follow this tutorial as well to setup CORS/PouchDB and Web Push Notifications https://github.com/couchbasela…. Let me know if it’s still not working for you.

  4. Jonas Schmid 아바타
    Jonas Schmid

    In the video you have a browser opened with a webpage. Is it part of the Sync Gateway? Do you have to install it separately?
    I tried to access it but only got 404s.
    Thanks

    1. Jonas Schmid 아바타
      Jonas Schmid

      Nevermind. The admin interface only accepts connection from 127.0.0.1

  5. Harashikage 아바타
    Harashikage

    Hi James

    CORS is Not working for me.
    ERROR:
    XMLHttpRequest cannot load <host url=””>. Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header contains the invalid value ”. Origin <origin url=””> is therefore not allowed access.

    On sync the Request has the Local url for the ‘ORIGIN’ and gateway as the ‘HOST’. But the Response Header > ‘Access-Control-Allow-Origin’> is Null and the request is throwing a 204 No Content .

    The Sync Gateway was also Configured for Specific ORIGIN / All URLs still having the same Issue.

  6. Corey Quillen 아바타
    Corey Quillen

    CORS errors here too. I’ve posted the following StackOverflow question if anyone has time to take a look:

    https://stackoverflow.com/quest

  7. Hod Greeley, Developer Advocate, Couchbase 아바타

댓글 남기기

Ready to get Started with Couchbase Capella?

Start building

Check out our developer portal to explore NoSQL, browse resources, and get started with tutorials.

Use Capella free

Get hands-on with Couchbase in just a few clicks. Capella DBaaS is the easiest and fastest way to get started.

Get in touch

Want to learn more about Couchbase offerings? Let us help.