Options
All
  • Public
  • Public/Protected
  • All
Menu

⌚️ Asynchronous Client

In some cases, you may want to wait for your Apollo client to do some initial asynchronous setup (for example reloading a persistent cache or getting a user token) before you can make your client available to your app.

import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client/core';

const cache = new InMemoryCache();

const link = new HttpLink({
  uri: 'https://api.spacex.land/graphql',
})

let client;

export async function getClient() {
  if (client)
    return client;

  // Wait for the cache to be restored
  await persistCache({ cache, storage: localStorage });

  // Create the Apollo Client
  client =
    new ApolloClient({ cache, link });

  return client;
};

The most straightforward way to do this is first instantiate your client, and only afterwards load your components using dynamic import():

import { getClient } from './client';
(async function init() {
  window.__APOLLO_CLIENT__ = await getClient();
  await Promise.all([
    import('./components/connected-element.js'),
    import('./components/connected-input.js'),
  ]);
})();

If for whatever reason you'd like to load your component files eagerly, set the noAutoSubscribe property on your components, then you can import a promise of a client and wait for it in connectedCallback, calling subscribe when ready.

import { ApolloQueryMixin } from '@apollo-elements/mixins/apollo-query-mixin';

import UserSessionQuery from './UserSession.query.graphql';

import type {
  UserSessionQueryData as Data,
  UserSessionQueryVariables as Variables,
} from '../schema';

import { getClient } from './client';
import { formatDistance } from 'date-fns/esm';

const template = document.createElement('template');
template.innerHTML = `
  <h1>👋 </h1>
  <p>
    <span>Your last activity was</span>
    <time></time>
  </p>
`;

template.content.querySelector('h1').append(new Text(''));
template.content.querySelector('h1').append(new Text('!'));
template.content.querySelector('time').append(new Text(''));

class AsyncElement extends ApolloQueryMixin(HTMLElement)<Data, Variables> {
  noAutoSubscribe = true;

  query = UserSessionQuery;

  get data() {
    return this.#data;
  }

  set data(value: Data) {
    this.#data = value;
    this.render();
  }

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.append(template.content.cloneNode(true));
  }

  async connectedCallback() {
    super.connectedCallback();
    // asynchronously get a reference to the client
    this.client = await getClient();
    // only then start fetching data
    this.subscribe();
  }

  render() {
    const lastActive = this.data?.userSession.lastActive;

    const [waveNode, nameNode] =
      this.shadowRoot.querySelector('h1').childNodes;
    const [timeNode] =
      this.shadowRoot.querySelector('time').childNodes;

    nameNode.data = this.data?.userSession.name ?? '';
    timeNode.data =
      !lastActive ? '' : formatDistance(lastActive, Date.now(), { addSuffix: true });
  }
};

customElements.define('async-element', AsyncElement);
import { ApolloQuery, customElement, html } from '@apollo-elements/lit-apollo';

import UserSessionQuery from './UserSession.query.graphql';

import type {
  UserSessionQueryData as Data,
  UserSessionQueryVariables as Variables,
} from '../schema';

import { getClient } from './client';
import { formatDistance } from 'date-fns/esm';

@customElement('async-element')
class AsyncElement extends ApolloQuery<Data, Variables> {
  noAutoSubscribe = true;

  query = UserSessionQuery;

  async connectedCallback() {
    super.connectedCallback();
    // asynchronously get a reference to the client
    this.client = await getClient();
    // only then start fetching data
    this.subscribe();
  }

  render() {
    const name = this.data?.userSession.name ?? ''
    const lastActive = this.data?.userSession.lastActive;

    const time =
      !lastActive ? '' : formatDistance(lastActive, Date.now(), { addSuffix: true });

    return html`
      <h1>👋 ${name}!</h1>
      <p>
        <span>Your last activity was</span>
        <time>${time}</time>
      </p>
    `;
   }
};
import { ApolloQuery, customElement, html } from '@apollo-elements/fast';

import UserSessionQuery from './UserSession.query.graphql';

import type {
  UserSessionQueryData as Data,
  UserSessionQueryVariables as Variables,
} from '../schema';

import { getClient } from './client';
import { formatDistance } from 'date-fns/esm';

function getTime(userSession): string {
  const lastActive = userSession?.lastActive;
  return (
      !lastActive ? ''
    : formatDistance(new Date(lastActive), Date.now(), { addSuffix: true })
  );
}

@customElement({
  name: 'async-element',
  template: html<AsyncElement>`
    <h1>👋 ${x => x.data?.userSession.name}!</h1>
    <p>
      <span>Your last activity was</span>
      <time>${x => getTime(x.data?.userSession)}</time>
    </p>
  `
})
class AsyncElement extends ApolloQuery<Data, Variables> {
  noAutoSubscribe = true;

  query = UserSessionQuery;

  async connectedCallback() {
    super.connectedCallback();
    // asynchronously get a reference to the client
    this.client = await getClient();
    // only then start fetching data
    this.subscribe();
  }
};
import { useQuery, component, html } from '@apollo-elements/haunted';

import UserSessionQuery from './UserSession.query.graphql';

import type {
  UserSessionQueryData as Data,
  UserSessionQueryVariables as Variables,
} from '../schema';

import { getClient } from './client';
import { formatDistance } from 'date-fns/esm';

function AsyncElement(el) {
  const { client, data } =
    useQuery<Data, Variables>(UserSessionQuery, { noAutoSubscribe: true });

  useEffect(async ({ host }) => {
    if (host.client) return;
    // asynchronously get a reference to the client
    host.client = await getClient();
    // only then start fetching data
    host.subscribe();
  }, [client]);

  const name = data?.userSession.name ?? ''
  const lastActive = data?.userSession.lastActive;

  const time =
    !lastActive ? '' : formatDistance(lastActive, Date.now(), { addSuffix: true });

  return html`
    <h1>👋 ${name}!</h1>
    <p>
      <span>Your last activity was</span>
      <time>${time}</time>
    </p>
  `;
};

customElements.define('async-element', component(AsyncElement));
import { client, query, define, property, html } from '@apollo-elements/hybrids';

import { getClient } from './client';
import { formatDistance } from 'date-fns/esm';

import UserSessionQuery from './UserSession.query.graphql';

import type {
  UserSessionQueryData as Data,
  UserSessionQueryVariables as Variables,
} from '../schema';

function getTime(userSession): string {
  const lastActive = userSession?.lastActive;
  return (
      !lastActive ? ''
    : formatDistance(new Date(lastActive), Date.now(), { addSuffix: true })
  );
}

async function connect(host) {
  // asynchronously get a reference to the client
  host.client = await getClient();
  // only then start fetching data
  host.subscribe();
}

define('async-element', {
  // use 'connect' to gain access to connectedCallback
  // because of how Hybrids queues connectedCallbacks,
  // set this before you set `query`
  noAutoSubscribe: property(true, connect),
  client: client(null),
  query: query<Data, Variables>(UserSessionQuery),
  render: ({ data }) => html`
    <h1>👋 ${data?.userSession.name}!</h1>
    <p>
      <span>Your last activity was</span>
      <time>${getTime(data?.userSession)}</time>
    </p>
  `
})