Offline-first for every application

 

Replicache is the best way to build blazingly responsive offline-first apps.

 

Benefits

Instant User Actions. By executing all reads and writes against a persistent local cache, apps built with Replicache respond immediately to user actions — online, offline, or anything in between.

Realtime, built-in. Want realtime UI updates? Call replicache.subscribe() and wire that to your UI. That's it. Whenever the underlying data changes, due to either local or server-side changes, the subscription will fire and the view will refresh.

Works with any backend. You have an existing service, and an existing client. Keep those! Replicache sits between them and makes it easy to add offline-first goodness to your existing app.

Dramatically easier conflict resolution. Merging the effects of conflicting histories is super hard. Instead, Replicache rewinds offline operations and replays them when you come back online. Sort of like git rebase for your application state. It's the easiest way to deal with conflicts that is...

Correct. Replicache is designed to provide Causal+ Consistency, which is one of the strongest consistency models possible in an offline-first system. Our design has been reviewed by independent distributed systems expert Kyle Kingsbury of Jepsen.

How it Works

Overview

Replicache is an on-device cache with a server-side replica.

Changes are allowed at either replica. Changes made on the server side are pushed to the client when connectivity allows, and vice versa.

Replicache provides a programming model that makes it easy to reason about and resolve conflicting changes, and ensures that device state always ends up consistent with server state.

①  Read Data

let sub = replicache.subscribe(
  async (tx) => (await tx.scan("/todo/")).filter(
    (item) => item.text.includes("Dog"))
);
sub.onchange = () =>
  this.setState({
    todos: sub.result,
  });                 
@ObservedObject let sub = replicache.subscribe {
  $0.scan("/todo/").filter { $0.text.contains("Dog") }
}
var body: some View {
  List {
    ForEach(sub.results()) { result in 
      TodoRow(result)
    }
  }  
}
final Subscription sub = replicache.subscribe(tx -> {
  return tx.scan("/todo/").filter(
    item -> item.text().contains("Dog"));
});
  
final Observer<JSONObject> observer = new Observer<JSONObject>() {
  @Override
  public void onChanged(@Nullable final JSONObject result) {
    // Update the UI, in this case, a table.
    tableView.setData(result);
  }
};

sub.observe(this, observer);

Read directly from the local replica with zero network delays using query().

Easily build realtime applications using subscribe(). Whenever the underlying data changes — either due to remote or local changes — the affected views refresh automatically and consistently.

②  Write Data

const createTodo = replicache.register("/create-todo",
  async (tx, args) => {
    const {id, text, complete} = args;
    await tx.put(`/todo/${id}`, {text, complete});
  });
createTodo({id: uuid(), text: this.state.todoText, complete: false});                    
let createTodo = replicache.register("/create-todo",
    { (tx: WriteTransaction, args: Any) -> Any {
  tx.put(#"/todo/\#(args["id"])"#, args);
});
createTodo(["id": UUID().uuidString, "text": todoText, "done": false]);
Mutator mutator = replicache.register("/create-todo",
    (WriteTransaction tx, JSONValue args) -> {
  tx.put(String.format("/todo/%s", args.get("id")), args);
});
mutator("/create-todo", JSON.createObjectBuilder()
  .add("id", id.string())
  .add("text", todoText)
  .add("done", done).build());

To make changes on the client, register a mutator.

Replicache executes the mutator immediately against the local cache. Subscriptions re-fire, and views are instantly updated with the pending change.

③  Upstream Sync

POST /replicache-batch HTTP/2
Host: myservice.com

{
  "clientID": "CB94867E-94B7-48F3-A3C1-287871E1F7FD",
  "mutations": [
    {
      "id": 7,
      "name": "createTodo",
      "args": {
        "id": "AE2E880D-C4BD-473A-B5E0-29A4A9965EE9",
        "title": "Fix the car",
        "complete": false
      }
    },
    {
      "id": 8,
      "name": "toggleComplete",
      "args": {
        "id": "5C2F21E8-A9CC-4DA8-91D6-97D2D1F7CECF",
        "done": true
      }
    }
  ]
}
HTTP/2 200 
Content-Type: application/json

[
  {
    "id": 8,
    "error": "specified todo not found"
  }
]

Batches of pending write transactions are sent to the /replicache-batch endpoint on your service as connectivity allows. These requests are delayed, but otherwise normal. Your service defensively checks for conflicts, and ignores, modifies, or rejects the request as normal.

④, ⑤  Downstream Sync

POST /replicache-device-view HTTP/2
Host: myservice.com
Authorization: Bearer 51857000befac83d338df7661d00d81011d7fb10

HTTP/2 200
Content-Type: application/json

{
  "lastMutationID": 6,
  "/todo/39B224C1-1DBE-48D2-B89D-1DC4BA9821AA": {
    "text": "Take out the garbage",
    "done": false
  },
  "/todo/77173E44-C620-429C-9CD1-16548D58A94A": {
    "text": "Feed the dog",
    "done": true
  }
}

The server replica requests the latest offline state from your service's /replicache-client-state endpoint. Just return all data that should be in a user's cache (up to 20MB of JSON) every time. Replicache computes a minimal diff and pushes it to the device.

The Repicache client rewinds the cache to the point before sync started, integrates the diff, and then replays any remaining unacknowledged changes on top.

The final state is revealed to the UI atomically, subscriptions re-fire, and the UI refreshes.

Who's Behind This?

Aaron Boodman

Hi, I'm Aaron Boodman (I'm the big one).

I've been working on sync on and off for over fifteen years, including major projects at Google and my last startup.

I pushed eject on Silicon Valley last year and moved my family out to Oahu to try and better balance life, work, and family.

Fritz Schneider

I'm Fritz Schneider.

In previous lives I worked at Google (twice), and on various startups, including the last one with Aaron.

I live on Maui, a few rocks over from Oahu, which makes us, officially, a  ✨distributed company✨.

Erik Arvidsson

👋 Erik Arvidsson here.

I've worked at Google (Chrome, Blink, V8, Gmail), then Attic Labs with Aaron and Fritz, and most recenly at Quip.

I live in San Francisco with wife, kids and cats.

We're building a small, sustainable software partnership. No VC financing, no rocket ship growth expectations. Just high-quality software, sold at a fair price.

Price

Replicache is licensed using the Business Source License. The BSL balances the needs of users — development transparency and avoidance of lock-in — with the need to create a sustainable business.

After two years, each Replicache release automatically reverts to the Apache 2 Public License, and becomes free forever.

🛹 🚴🏽‍♀️ 🏎 🚀
Clients < 100 < 10k < 100k
Price Free $1k/mo $5k/mo $20k/mo
Support Basic Basic Prioritized Prioritized

Limits

  • Additional charges may apply for more than 600 syncs/day/client, or more than 100k clients total, on our hosted Diff Server.
  • The Client View is currently limited to 5 MB Brotli-compressed JSON (if this is a deal-breaker for you please let us know).

Get Started

Replicache is now available in beta for web applications. Kick the tires and let us know what you build! If you love it as much as we do, you'll be able to roll it out in production in Q1 of 2021.

Get Started
Or join the wait list for production access.