Sessions

Just like in our previous section, we have the following abstract data access patterns for Session models via our repository:

async toggleLikeSession(sessionId: string) {
  // Implement me!
}

sessionById(sessionId: string): Observable<Session> {
  // Implement me!
}

listSessions(): Observable<Session[]> {
  // Implement me!
}

Likes

Let’s start by implementing likes. Open the Amplify data access strategy in the src/app/repository/amplify/index.ts file and write the following code:

async toggleLikeSession(sessionId: string) {
  const user = await this.user();
  if (user.isAnonymous) {
    throw new Error('Not signed in');
  }
  const session = await performGraphqlOperation<Session>(
    queries.getSession,
    { key: sessionId }
  );
  const likes = new Set(session.likes ?? []);
  if (!likes.has(user.username)) {
    likes.add(user.username);
  } else {
    likes.delete(user.username);
  }
  try {
    await performGraphqlOperation<Session>(
      mutations.updateSession,
      {input: {
        key: sessionId,
        likes: Array.from(likes)
      }}
    );
  } catch (err) {
    console.error(err);
  }
}

This method allows us to add or remove a like for a particular Session if we are signed in. It simply mutates the Session by its ID and updates the set of usernames contained in likes.

Real Time Read Operations

sessionById(sessionId: string): Observable<Session> {
  this.unsubscribe(this.subscriptions.sessionById);
  const res = subscribe<Session>(subscriptions.updatedSession);
  this.subscriptions.sessionById = res.subscription;
  return merge(
    from(performGraphqlOperation(queries.getSession, { key: sessionId })),
    res.observable
  ).pipe(utils.keyToId());
}

Using AppSync, we have more control over which models we would like to subscribe to. Because we will be updating our Session detail view whenever a like is added or removed, we want to subscribe for changes. Here we use a utility method, subscribe, that creates an RxJS Observable from an AppSync GraphQL Subscription. It returns an object that contains two things:

  1. The subscription, so we can control when we subscribe or unsubscribe.
  2. The Observable that receives the server-side updates.

We also want to retrieve the session by its ID as soon as this method is called, so we merge the two Observables: one for the real time updates, and one for the immediate request for the Session data by its ID.

Listing Conference Sessions

listSessions(): Observable<Session[]> {
  return from(performGraphqlOperation<Session[]>(queries.listSessions))
    .pipe(utils.keysToIds());
}

By default our repository groups the conference sessions to aid in the display of the data within the frontend application. Since our migrated Firestore data landed in DynamoDB, each migrated object has a primary key, which has a slightly different shape than what was in Firestore. Therefore we use a utility method to transform the key field from AppSync and DynamoDB to the model’s id field, which originally was defined from Firestore.

This is an example of decoupling the underlying data access pattern with the frontend presentation of the data.