<link rel="stylesheet" href="/_merged_assets/_static/search/noscript.css">
Apollo Elements Apollo Elements Guides API Blog Toggle darkmode

Cool Tricks: Code Splitting

Code splitting or lazy-loading is the process of writing an app such that the user only loads the code they need when they need it, instead of loading the entire app in one huge bundle up front.

Imagine your single-page app has two kinds of users: regular users and admins. The admins have access to different pages than the regular users, and those pages have whole complicated state trees which are irrelevant to regular users.

It doesn't make sense to force regular users to download, parse, and compile the code for those pages which they'll never see and can't use. Instead, we can lazily load those components using dynamic import(), and register their state trees after-the-fact.

TypePoliciesMixin

To facilitate this, import the TypePoliciesMixin so you can lazy-load portions of your client-side state store along with their UI components.

Let's say your client side router loads a different component depending on the user role:

// router.ts
export const router = new Router({
  routes: [{
    path: '/profile',
    async onEnter(context) {
      if (context.user.roles.includes('admin'))
        await import('../components/admin-profile');
      else
        await import('../components/profile');
    },
  }]
})

And let's say you want to apply different type policies depending on the type of user:

// src/components/user/typePolicies.ts
export const UserTypePolicies = {
  User: {
    fields: {
      fullName(_, { readField }) {
        return `${readField('firstName')} ${readField('lastName')}`;
      }
    }
  }
};

export const AdminTypePolicies = {
  User: {
    fields: {
      role() {
        return 'admin';
      }
    }
  }
};

import TypePoliciesMixin from the mixins package to easily register type policies on a component. For example, in src/components/profile/profile.ts you can use UserTypePolicies

<p>HTML apps should set all their type policies on the <code>apollo-client</code> element</p>
<apollo-client>...</apollo-client>
<script>
  import('./typePolicies')
    .then(({ UserTypePolicies }) => {
      document
        .currentScript
        .getRootNode()
        .querySelector('apollo-client')
        .typePolicies = UserTypePolicies;
    });
</script>
import { ApolloQueryMixin, TypePoliciesMixin } from '@apollo-elements/mixins';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

export class ProfilePage extends TypePoliciesMixin(ApolloQueryMixin(HTMLElement))<typeof UserQuery> {
  query = UserQuery;

  typePolicies = UserTypePolicies;
}

customElements.define('profile-page', ProfilePage);
import { ApolloQuery, customElement } from '@apollo-elements/lit-apollo';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

@customElement('profile-page')
export class ProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
  query = UserQuery;

  typePolicies = UserTypePolicies;
}
import { ApolloQuery } from '@apollo-elements/fast/bases/apollo-query';
import { customElement } from '@microsoft/fast-element';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

@customElement({ name: 'profile-page' })
export class ProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
  query = UserQuery;

  typePolicies = UserTypePolicies;
}
import { useQuery, useEffect, component, html } from '@apollo-elements/haunted';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

function ProfilePage({ client }) {
  const { client, data } = useQuery(UserQuery);

  /**
   * There's no TypePoliciesMixin for haunted,
   * but you can use the `useEffect` hook to do the same
   */
  useEffect(() => {
    client.cache.policies.addTypePolicies(UserTypePolicies);
  }, [client]);

  return html`...`;
}

customElements.define('profile-page', component(ProfilePage));
import { useQuery, useEffect, c } from '@apollo-elements/atomico';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

function ProfilePage({ client }) {
  const { client, data } = useQuery(UserQuery);

  /**
   * There's no TypePoliciesMixin for atomico,
   * but you can use the `useEffect` hook to do the same
   */
  useEffect(() => {
    client.cache.policies.addTypePolicies(UserTypePolicies);
  }, [client]);

  return <host>...</host>;
}

customElements.define('profile-page', c(ProfilePage));
import { query, define } from '@apollo-elements/hybrids';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';

define('profile-page', {
  query: query(UserQuery),
  __typePolicies: {
    /**
     * There's no TypePoliciesMixin for hybrids,
     * but you can use this one-line function to do the same
     */
    connect(host) {
      host.query.client.cache.policies.addTypePolicies(UserTypePolicies);
    }
  },
});

And you can lazy-load that same code for use in src/components/admin-profile/admin-profile.ts.

<p>HTML apps should set all their type policies on the <code>apollo-client</code> element</p>
<apollo-client>...</apollo-client>
<script>
  import('./typePolicies')
    .then(({ UserTypePolicies }) => {
      document
        .currentScript
        .getRootNode()
        .querySelector('apollo-client')
        .typePolicies = UserTypePolicies;
    });
</script>
import { ApolloQueryMixin, TypePoliciesMixin } from '@apollo-elements/mixins';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

export class AdminProfilePage
extends TypePoliciesMixin(ApolloQueryMixin(HTMLElement))<typeof UserQuery> {
  query = UserQuery;

  typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}

customElements.define('admin-profile-page', AdminProfilePage);
import { ApolloQuery, customElement } from '@apollo-elements/lit-apollo';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

@customElement('admin-profile-page')
export class AdminProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
  query = UserQuery;

  typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}
import { customElement } from '@microsoft/fast-element';
import { ApolloQuery } from '@apollo-elements/fast/bases/apollo-query';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

@customElement({ name: 'admin-profile-page' })
export class AdminProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
  query = UserQuery;

  typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}
import { useQuery, useEffect, component, html } from '@apollo-elements/haunted';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

function AdminProfilePage() {
  const { client, data } = useQuery(UserQuery);

  /**
   * There's no TypePoliciesMixin for haunted,
   * but you can use the `useEffect` hook to do the same
   */
  useEffect(() => {
    client.cache.policies.addTypePolicies(UserTypePolicies),
    client.cache.policies.addTypePolicies(AdminTypePolicies),
  }, [client]);

  return html`...`;
}

customElements.define('admin-profile-page', component(AdminProfilePage));
import { query, define } from '@apollo-elements/hybrids';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';

import { UserQuery } from './User.query.graphql';

define('admin-profile-page', {
  query: query(UserQuery),
  __typePolicies: {
    /**
     * There's no TypePoliciesMixin for hybrids,
     * but you can use this one-line function to do the same
     */
    connect(host) {
      host.query.client.cache.policies.addTypePolicies(UserTypePolicies),
      host.query.client.cache.policies.addTypePolicies(AdminTypePolicies),
    }
  }
});

When using Apollo*Controllers, take inspiration from the haunted and hybrids examples and call addTypePolicies on the controller's cache when the host connects.