Skip to content

πŸš€πŸŒ› Use the Launch Platform πŸ‘©β€πŸš€πŸ‘¨β€πŸš€

License

Notifications You must be signed in to change notification settings

anoblet/apollo-elements

Β 
Β 

Repository files navigation

Rocket Ship in Angle Brackets

πŸš€ Apollo Elements πŸ‘©β€πŸš€

πŸš€ Custom elements meet Apollo GraphQL 🌜

πŸ‘©β€πŸš€ It's one small step for a dev, one giant leap for the web platform! πŸ‘¨β€πŸš€

Maintained with Lerna Contributions Welcome Actions Status Test Coverage Demo Online

πŸ““ Contents

πŸ“‘ API Docs

If you just want to see the API Docs, check them out for all our packages at apolloelements.dev

πŸ€– Demo

#leeway is a progressive web app that uses lit-apollo to make it easier for you to avoid doing actual work. Check out the source repo for an example of how to build apps with Apollo Elements. The demo includes:

  • SSR
  • Code Splitting
  • Aggressive minification, including lit-html template literals
  • CSS-in-CSS ( e.g. import shared from '../shared-styles.css';)
  • GQL-in-GQL ( e.g. import query from './my-component-query.graphql';)
  • GraphQL Subscriptions over websocket

Lighthouse Scores: 98 (performance), 100 (accessibility), 93 (best practises), 100 (SEO), 12/12 (PWA)

πŸ“¦ Packages

Apollo Elements offers packages based on a variety of underlying web component authoring libraries. You can pick the one that suits your project in order to keep your app sizes small.

These base classes extend from LitElement, so you can quickly get up and running creating declarative front-ends with Apollo GraphQL.

npm i -S @apollo-elements/lit-apollo
<!-- index.html -->
<script type="module" src="app.bundle.js"></script>
<script nomodule src="app.bundle.system.js"></script>
<apollo-app>
  <script type="application/graphql">
    query {
      helloWorld {
        greeting
        name
      }
    }
  </script>
</apollo-app>
// app.js
import gql from 'graphql-tag'
import { ApolloQuery, html } from '@apollo-elements/lit-apollo';
import { client } from './apollo-client';
import { render } from ''

customElements.define('apollo-app', class ApolloApp extends ApolloQuery {
  render() {
    const { data, error, loading } = this;
    return (
        loading ? html`<what-spin></what-spin>`
      : error ? html` <h1>😒 Such Sad, Very Error! 😰</h1> <div>${error.message}</div>`
      : html`<div>${data.helloWorld.greeting}, ${helloWorld.name}</div>`
    );
   }
});

These base classes extend from GluonElement, a simplified wc library that uses lit-html for templating while keeping component state and lifecycle concerns 'close to the metal'.

npm i -S @apollo-elements/gluon
import gql from 'graphql-tag'
import { ApolloQuery, html } from '@apollo-elements/gluon';
import { client } from './apollo-client';

customElements.define('apollo-app', class ApolloApp extends ApolloQuery {
  get template() {
    const { data, error, loading } = this;
    return (
        loading ? html`<what-spin></what-spin>`
      : error ? html` <h1>😒 Such Sad, Very Error! 😰</h1> <div>${error.message}</div>`
      : html`<div>${data.helloWorld.greeting}, ${helloWorld.name}</div>`
    );
   }
});

A set of objects you can roll into your hybrids to make it easier to connect to your Apollo cache.

npm i -S @apollo-elements/hybrids
import { ApolloQuery, queryFactory, define, html } from '@apollo-elements/hybrids';
import gql from 'graphql-tag';

export const ConnectedElement = {
  ...ApolloQuery,
  query: queryFactory(gql`query { hello }`),
  render: ({data}) => html`<div>${data.hello}</div>`
};

define('connected-element', ConnectedElement);

These custom elements fire polymer-style *-changed events when the Apollo cache updates their state. They extend directly from HTMLElement so they're small in size, and their notifying properties make them perfect for use in Polymer templates.

npm i -S @apollo-elements/polymer
// app.js
import { client } from './apollo-client.js'
import '@apollo-elements/polymer/apollo-query.js';
window.__APOLLO_CLIENT__ = client
import { PolymerElement, html } from '@polymer/polymer';
import '@apollo-elements/polymer/apollo-query.js';
import '@polymer/paper-card/paper-card.js';

customElements.define('my-app', class MyTemplate extends PolymerElement {
  static get template() {
    return html`
    <apollo-query data="{{data}}" variables="[[variables]]">
      <script type="application/graphql">
        query User($id: ID!)
          user(id: $id) {
            name
            picture
          }
        }
      </script>
    </apollo-query>

    <paper-card heading="[[data.name]]" image="[[data.picture]]"></paper-card>
    `;
  }

  static get properties() {
    return {
      variables: {
        type: Object,
        value: () => ({id: ''});
      }
    }
  }

  async connectedCallback() {
    super.connectedCallback();
    const { id = '' } = await getToken();
    this.variables = { id };
  }
}

These custom element class mixins give you all the features you need to connect your components to your Apollo cache without imposing a specific component library.

npm i -S @apollo-elements/mixins
import { ApolloQueryMixin } from '@apollo-elements/mixins/apollo-query-mixin.js';

customElements.define('vanilla-query', class VanillaQuery extends ApolloQueryMixin(HTMLElement) {
  get data() {
    return this.__data;
  }

  set data(data) {
    this.__data = data;
    this.shadowRoot.innerText = `${data.helloWorld.greeting}, ${data.helloWorld.name}`;
  }  
});

πŸ—ž Bundling

Apollo Elements uses Object.fromEntries and object spread syntax, so you may need to transpile and polyfill those features in these packages.

Since Apollo client cannot be imported directly into the browser, you must transpile and bundle apollo-client in order to use it in your app. We recommend using Rollup for this. Your rollup.config.js might look something like this:

import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
  input: [
    'src/components/app-shell/app-shell.js',
    'src/components/app-view1/app-view1.js',
    'src/components/app-view2/app-view2.js',
    'src/components/app-view404/app-view404.js',
  ],

  output: [{
    dir: 'build/modern',
    format: 'es',
    sourcemap: true,
  }, {
    dir: 'build/nomodule',
    format: 'system',
    sourcemap: true,
  }],

  plugins: [

    // REQUIRED to roll apollo-client up
    resolve({
      browser: true,
      jsnext: true,
      module: true,
    }),

    commonjs({
      namedExports: {
        // Necessary to roll apollo-link-state up.
        // until graphql-anywhere 5.0
        'graphql-anywhere/lib/async': ['graphql'],
        // Needed to roll up apollo-cache-persist
        'apollo-cache-persist': ['persistCache']
      }
    }),

  ]
}

Alternatively, you might bundle and export your Apollo client separately, then import it into your browser-friendly component modules.

😎 Cool Tricks

πŸ“œ Inline Query Scripts

You can provide a GraphQL query string in your markup by appending a GraphQL script element to your connected element, like so:

<apollo-app>
  <script type="application/graphql">
    query {
      helloWorld {
        name
        greeting
      }
    }
  </script>
</apollo-app>

πŸ‘·β€β™‚οΈ Maintainers

apollo-elements is a community project maintained by Benny Powers.

Contact me on Codementor

About

πŸš€πŸŒ› Use the Launch Platform πŸ‘©β€πŸš€πŸ‘¨β€πŸš€

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 98.6%
  • Other 1.4%