Skip to content

Commit

Permalink
fixup! Add Quickstart page content
Browse files Browse the repository at this point in the history
  • Loading branch information
GregHolmes committed May 13, 2024
1 parent 13e0502 commit d66e696
Showing 1 changed file with 56 additions and 18 deletions.
74 changes: 56 additions & 18 deletions content/livesync/quickstart.textile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ languages:
- javascript
---

TODO: Introduction to Quickstart
LiveSync facilitates broadcasting realtime updates from backend databases to frontend clients at scale. Leveraging Ably's Pub/Sub Channels, LiveSync ensures that data updates are propagated reliably and in order to all connected clients in realtime.

This quickstart will provide a brief introduction into the LiveSync components integrated into a realtime web application enabling people to comment on a post. Following this, you'll follow instructions to get this demo application running locally.

h2(#breakdown). Understanding the implementation

Each component used within this section is based on the "working demo application":https://github.com/ably-labs/models/examples/livecomments that you'll have running locally by the end of this quickstart.

h3(#model). The model

The @model@ is a data model represntation of a specific part of your frontend application. In the @livecomments@ example, the "model":https://github.com/ably-labs/models/blob/main/examples/livecomments/lib/models/hook.ts#L25-L55 is a specific post, defined by its unique post ID, @post:${id}@.

```[javascript]
const modelsClient = new ModelsClient({
ably,
Expand All @@ -30,6 +36,8 @@ h3(#model). The model

h4(#sync). sync

The sync function is used by the Models SDK to fetch the latest data from your backend. The SDK will automatically call this function when it is initialized, and when the SDK detects that the latest data is no longer available on the Ably channel. There are two objects returned within the response, these are the "@sequenceId@":/livesync/models#sequence-id, which is used to identify the point in the stream of change events that corresponds to the current version of the database's state. The second object is the data relevant to the expected data model, in this instance it's the current state of the post defined by its @id@. In this working demo the sync function called is "@getPost@":https://github.com/ably-labs/models/blob/main/examples/livecomments/lib/models/hook.ts#L12-L23.

```[javascript]
export async function getPost(id: number) {
const response = await fetch(`/api/posts/${id}`, {
Expand All @@ -49,6 +57,8 @@ h4(#sync). sync

h4(#merge). merge

When a message is received on the channel, the merge function is called to merge that new data model state into the existing model state. The merge function should return the updated model state with the new comment merged in. In this working demo, the "merge":https://github.com/ably-labs/models/blob/main/examples/livecomments/lib/models/mutations.ts#L40-L71 function uses the event name to determine whether the comment sent is a new, an updated, or a deleted comment.

```[javascript]
export function merge(existingState: PostType, event: OptimisticEvent | ConfirmedEvent): PostType {
const state = cloneDeep(existingState);
Expand All @@ -58,7 +68,16 @@ export function merge(existingState: PostType, event: OptimisticEvent | Confirme
const newComment = event.data! as Comment;
state.comments.push(newComment);
break;
// editComment and deleteComment here.
case 'editComment':
const editComment = event.data! as Comment;
const editIdx = state.comments.findIndex((c) => c.id === editComment.id);
state.comments[editIdx] = editComment;
break;
case 'deleteComment':
const { id } = event.data! as { id: number };
const deleteIdx = state.comments.findIndex((c) => c.id === id);
state.comments.splice(deleteIdx, 1);
break;
default:
console.error('unknown event', event);
}
Expand All @@ -71,6 +90,8 @@ export function merge(existingState: PostType, event: OptimisticEvent | Confirme

h4(#subscribe). Subscribe

In the Models SDK, you subscribe to a model to receive its updated state whenever changes occur. Incoming messages on a channel trigger the merge function which integrates the message content into the existing state. With the current working demo, the "subscribe function":https://github.com/ably-labs/models/blob/main/examples/livecomments/components/post.tsx#L32-L38 updates the current state of the post with the new state.

```[javascript]
const onUpdate = (err: Error | null, post?: PostType) => {
if (err) {
Expand All @@ -84,8 +105,13 @@ h4(#subscribe). Subscribe

h3(#backend). The backend

There are two parts of the backend that are key for the LiveSync integration in the livecomments demo. These two are the @sync@ function and the @outbox entry@.

h4(#sync-function). Sync function

The sync function in the backend is an endpoint of a @GET@ request, which returns the "@sequenceId@":/livesync/models#sequence-id, used to identify the point in the stream of change events that corresponds to the current version of the database's state. This "endpoint":https://github.com/ably-labs/models/blob/main/examples/livecomments/app/api/posts/%5Bid%5D/route.ts#L5-L18 also returns the current state of the data relevant to the expected data model, in this instance it's the current state of the post defined by its @id@.


```[javascript]
export async function GET(request: NextRequest, { params }: { params: { id: string } }) {
try {
Expand All @@ -105,6 +131,8 @@ h4(#sync-function). Sync function

h4(#outbox-entry). outbox entry.

The outbox entry is an entry of an update to one of the models within the database. In "this example":https://github.com/ably-labs/models/blob/main/examples/livecomments/lib/prisma/api.ts#L129-L139 it's a record of a comment being added to, updated with, or deleted from a post. When a post is added, updated, or deleted a record of this is also added to the outbox table so that the ADBC can pick this up and publish the update to the specified Pub/Sub channel.

```[javascript]
export async function withOutboxWrite(
op: (tx: TxClient, ...args: any[]) => Promise<Prisma.outboxCreateInput>,
Expand All @@ -121,17 +149,25 @@ h4(#outbox-entry). outbox entry.

h2(#quickstart). Quickstart

h3(#step-0). Create an Ably account
In this quickstart you will cover all the steps required to get an existing demo running locally on your machine.

h3(#step-0). The project

"Sign up":https://ably.com/signup for a free account to get your own API key. Ensure that your API key includes the subscribe and publish "capabilities":/auth/capabilities.
Clone the models repository (https://github.com/ably-labs/models/) and navigate to the @livecomments@ directory within @examples@. The @livecomments@ directory houses the project used for this working demo.

h3(#step-1). Update environment variables
h3(#step-1). Create an Ably account

"Sign up":https://ably.com/signup for a free account to get your own API key. Ensure that your API key includes the @subscribe@ and @publish@ "capabilities":/auth/capabilities.

h3(#step-2). Update environment variables

Set up environment variables by copying them from the template:

```
cp env.example env.local
```

Update your `env.local` file with the following:
Update your @env.local@ file with the following:

```
POSTGRES_URL="postgres://postgres:postgres@localhost:5432/postgres"
Expand All @@ -146,44 +182,46 @@ NEXT_PUBLIC_ABLY_API_KEY=<YOUR_ABLY_API_KEY>
NEXT_PUBLIC_ABLY_CHANNEL_NAMESPACE=<SOME_NAMESPACE>
```

- Replace `<SOME_SECRET>` with some random string.
- Replace `<YOUR_ABLY_API_KEY>` with your Ably API Key
- Optionally replace `<SOME_NAMESPACE>` with a string value used as a [namespace](https://faqs.ably.com/what-is-a-channel-namespace-and-how-can-i-use-them) for the model's Ably channel.
- Replace @<SOME_SECRET>@ with some random string.
- Replace @<YOUR_ABLY_API_KEY>@ with your Ably API Key
- Optionally replace @<SOME_NAMESPACE>@ with a string value used as a [namespace](https://faqs.ably.com/what-is-a-channel-namespace-and-how-can-i-use-them) for the Pub/Sub channel being used by the model.

Export the environment variables in your shell session:

```[sh]
export $(grep -s -v "^#" env.local | xargs) # export environment variables
```

h3(#step-2). Running Docker container
h3(#step-3). Running Docker container

Within the @docker-compose.yaml@ file, there are two services, @adbc@ and @postgres@. The @adbc@ service is the Ably Database Connector (ADBC) service, which will listen to updates persisted to the outbox table in your database and publish them to the Pub/Sub channel.

TODO: describe this runs the Database connector and the database.
The @postgres@ service is a postgres database created and run within a Docker container to assist in running this demo as quick as possible.

Now spin up a PostgreSQL database and an instance of `adbc` which publishes change events written to the outbox table over Ably channels.
Spin up these two Docker containers by running the following command:

```[sh]
docker compose up --build -d
```

h3(#step-3). Install dependencies
h3(#step-4). Install dependencies

Now we can create the necessary tables in the database and create some seed data:
Install the required dependencies for the web application, including your Models SDK. Then create the necessary database tables and create some seeded data:

```[sh]
pnpm install # first install dependencies
pnpm install
pnpm run db
```

h3(#step-4). Run local web server
h3(#step-5). Run local web server

You can now start the example app:
Run the web application locally by running the following command:

```[sh]
pnpm run dev
```

h3(#step-5). Try it out!
h3(#step-6). Try it out!

Open the app on [localhost:3000](http://localhost:3000).

Expand Down

0 comments on commit d66e696

Please sign in to comment.