# Database in the Browser, a Spec

## Introduction
How will we build web applications in the future?

If progress follows its usual strategy, then whatever is difficult and valuable to do today will become easy and normal tomorrow. I imagine we'll discover new abstractions, which will make writing Google Docs as easy as the average web app is today.

This begs the question — what will those abstractions look like? Can we discover them today? One way to find out is to look at all the schleps we have to go through in building web applications and see what we can do about it.

## Client

### A. Data Plumbing
The first job we have is to fetch information and display it in different places. For example, we may display a friends list, a friends count, a modal with a specific group of friends, etc.

The problem we face is that all components need to see consistent information. If one component sees different data for friends, it’s possible that you’ll get the wrong "count" showing up or a different nickname in one view versus another.

To solve for this, we need to have a central source of truth. So, whenever we fetch anything, we normalize it and plop it in one place (often a _store_). Then, each component reads and transforms the data it needs (using a _selector_). It’s not uncommon to see something like:

```javascript
// normalise [posts] -> {[id]: post}
fetchRelevantPostsFor(user).then((posts) => {
  posts.forEach((post) => {
    store.addPost(post);
  });
});

// see all posts by author:
store.posts.values().reduce((res, post) => {
  res[post.authorId] = res[post.authorId] || [];
  res[post.authorId].push(post);
  return res;
}, {});
```

### B. Change
The next problem is keeping data up to date. Say we remove a friend — what should happen?

We send an API request, wait for it to complete, and write some logic to "remove" all the information we have about that friend. Something like this:

```javascript
deleteFriend(user, friend.id).then((res) => {
  userStore.remove(friend.id);
  postStore.removeUserPosts(friend.id);
});
```

But, this can get hairy to deal with quickly: we have to remember every place in our store that could possibly be affected by this change. It’s like playing garbage collector in our heads. Our heads are not good at this.

One way folks avoid it is to skip the problem and just re-fetch the whole world:

```javascript
deleteFriend(user, id).then((res) => {
  fetchFriends(user);
  fetchPostsRelevantToTheUser(user);
});
```

Neither solution is very good. In both cases, there are implicit invariants we need to be aware of (based on this change, what other changes do we need to be aware of?) and we introduce lag in our application.

### C. Optimistic Updates
The problem you may have noticed with B., was that we had to _wait_ for friendship removal to update our browser state.

In most cases, we can make the experience snappier with an optimistic update — after all, we know that the call will likely be a success. To do this, we do something like:

```javascript
friendPosts = userStore.getFriendPosts(friend);
userStore.remove(friend.id);
postStore.removeUserPosts(friend.id);
deleteFriend(user, id).catch((e) => {
  // undo
  userStore.addFriend(friend);
  postStore.addPosts(friendPosts);
});
```

### D. Reactivity
And data doesn’t just change from our own actions. Sometimes we need to connect to changes that other users make. For example, someone could unfriend us, or someone could send us a message.

To make this work, we need to do the same work that we did in our API endpoints, but this time on our websocket connection:

```javascript
ws.listen(`${user.id}/friends-removed`, friend => {
  userStore.remove(friend.id);
  postStore.removeUserPosts(friend.id);
});
```

## Server

### E. Endpoints
Much of backend development ends up being a sort of glue between the database and the frontend.

```javascript
// db.js
function getRelevantPostsFor(userId) {
  db.exec('SELECT * FROM posts WHERE ...');
}

// api.js
app.get('relevantPosts', (req, res) => {
  res.status(200).send(getRelevantPosts(req.userId));
});
```

This is so repetitive that we end up creating scripts to generate these files. But why do we need to do this at all? They are often coupled very closely to the client anyway. Why can’t we just expose the database to the client?

### F. Permissions
Well, the reason we don’t is because we need to make sure permissions are correctly set. You should only see posts by your friends, for example. To do this, we add middleware to our API endpoints:

```javascript
app.put("user", auth, (req, res) => {
  ...
}
```

### G. Audits, Undo / Redo
At some point, we get requirements that blow up complexity for us.
For example, say we need to support "undo / redo" for friendship actions. A user deletes a friend, and then they press "undo" — how could we support this?

To solve this, we’d evolve our data model. Instead of a single friendship relation, we’d have "friendship facts"

```javascript
[
  { status: 'friends', friend_one_id: 1, friend_two_id: 2, at: 1000 },
  { status: 'disconnected', friend_one_id: 1, friend_two_id: 2, at: 10001 },
];
```

## Current Solutions
Wow, that’s a lot of problems. It may seem bleak, but if you just look a few years back, it’s surprising how much has improved. After all, we don’t need to roll our own racks anymore. Many great folks are working on solutions to these problems. What are some of them?

### 1) Firebase
Firebase has done some of the most innovative work in moving web application development forward. The most important thing they got right was a **database on the browser.**

### 2) Supabase
Supabase is trying to do what Firebase did for Mongo, but for Postgres. If they did this, it would be quite an attractive option, as it would solve Firebase’s biggest problem: query strength.

### 3) GraphQL + Hasura
GraphQL is an excellent way to declaratively define data you want from the client. Services like Hasura can take a database like Postgres, and do smart things like give you a GraphQL API out of it.

## Future
Now the question: what will the evolution of these tools look like?
In some ways, the future is happening now. I think Figma, for example, is an app from the future: it handles offline mode, undo/redo and multiplayer beautifully.
