Writing to the database with methods

In Meteor, methods are the main way to trigger persistent changes to data.

Recap of optimistic UI

As you may know, every method can be run on the client and the server to achieve optimistic UI. This means you can run a simulation on the client so that the UI seems fast, and then run the full method against the database to do the actual write. Meteor does all of the heavy lifting for you to make sure that the UI ends up representing the real change to the database, rolling back the simulation on the client if necessary. Read more here: Optimistic UI with Meteor

If you define a method only on the server, no simulation will run, and it will act as a regular RPC.

Automatic optimistic UI

Here are some methods from the Todos example app:

// Defined in a file that is loaded both on client and server
Meteor.methods({
  '/todos/delete': function (todoId) {
    Todos.delete().where({id: todoId}).run();
  },
  '/todos/setChecked': function (todoId, checked) {
    Todos.update({checked: checked}).where({id: todoId}).run();
  },
  '/todos/setText': function (todoId, newText) {
    Todos.update({text: newText}).where({id: todoId}).run();
  }
});

As you can see, these methods are all very simple operations, and are written entirely using the Knex query builder.

If you use the subset of Knex operations that are implemented for Meteor's client-side cache, you can get automatic optimistic UI, which is where the same code works on client and server. So in this case, you don't need to do anything special for the UI to update instantly and then get patched with the result from the server.

For this to work, the columns you are updating inside the method need to be the same in the client-side cache and in the tables in the server-side database. Read more here: Publishing data to the client.

Small differences in the simulation

In this case, we are operating on data that has an extra column on the client. When we published rows from the lists table, we also added an extra field called incomplete_count, which was generated by doing a join on the todos table. Since the client doesn't know how to do this join, we need to manually update that field on the client. That update is automatically rolled back and replaced with the correct value from the server when the server roundtrip is finished.

Meteor.methods({
  '/lists/addTask': function (listId, newTaskText) {
    Todos.insert({
      list_id: listId,
      text: newTaskText,
      checked: false
    }).run();

    if (this.isSimulation) {
      // The imcomplete_count column only exists on the client, since it is
      // generated from a join/aggregate on the server. We need to update it
      // manually
      Lists.increment("incomplete_count", 1).where("id", listId).run();
    }
  }
});

Totally separate simulation or no simulation

In some cases, the schema might be so different on the server and the client that you need to write a totally separate simulation, or have no simulation at all.

// XXX write this

Transactions

One huge benefit of using Postgres over MongoDB is the ability to have transactions.

// XXX how do you use them