Stencil.js with Stencil-Apollo

maor zigel
Israeli Tech Radar
Published in
7 min readNov 15, 2020
Photo by Dan Meyers on Unsplash

My name is Maor Zigel and I’m a front-end developer at heart, working at Tikal.

Last month I participated in a hackathon that, in light of Covid-19 changing the way we work, concentrated on creating development environment tools for remote work. My teammates and I decided it was a great opportunity to try out some new techs!

We decided to use Stencil.js on the front end part and we also decided to work with GraphQL on the back end.
That meant that for the client to be able to communicate with the server we opted to use apollo-client (that’s why we used Stencil-Apollo).

What is Stencil.js?

Stencil is a small, lightweight library, designed for building pure Web Components that you can use anywhere! (No matter what framework you are using, or none at all).

Stencil has a simple & small API for building Web components, and it has some cool features like hot reload, we’ll talk about that later on.

Stencil uses Angular-like Decorators to wrap things up for simplicity (Don’t worry, we will talk about most of them in this article) and a simple set of component life-cycle functions very similar to React.
Stencil uses Typescript and JSX so we’ll be writing TSX files (like React on Typescript).

Simple example

as you can see in the following example:

To create the component we are using a decorator @Component({…}) that is very similar to Angular. It allows us to provide options like tag, styleUrl, and even shadow, for the component to be fully shadow dom inside (a topic for another article).
For the full list of options available on the Component decorator, go here.

Then like we do when coding in React, we create the class, use the @Prop decorator to define our Inputs and finally, we use the render method for our template rendering like all other React JSX components.

What about lifecycle methods?

I’m glad you asked!
Just like other popular frameworks (Angular, Vue, and React to name a few), Stencil also provides a set of component lifecycle hooks we can use, for example:

In the snippet above, we are using some of them:

  • connectedCallback — Called when the element is attached or moved in the DOM (do not confuse with onInit)
  • disconnectedCallback — Called when the element is detached from the DOM (do not confuse with onDestroy)
  • componentWillLoad — Called when the element is about to first rendered (Equivalent to Angular’s OnInit)

Those are probably the ones you will use the most, for a complete list of the hooks, you can take a look here.

What about State and change detection?

In the previous examples, we could see the decorators @Prop and @State. They provide us with a way to declare our properties as inputs and state detection triggers. When our component gets new Input externally or change a state property internally, our component will rerender, by running the render() function again.
Let’s take a look at the props we used:

@Prop() myName: string = 'No Name';
@State() time: number = Date.now();
@State() content: any;
  • myName is a prop that our component listens to for changes, so if the parent component will change it, that change will trigger the render() function.
  • time and content are inner state properties who’s values get updated by our component internally and so, will also trigger the render() function on change.

Fun facts about props in Stencil:

  • It can also inject services
  • Has a cool feature that can give you 2-way prop data binding (which can be useful in some cases): @Prop({ reflect: true })

What is Apollo?

Apollo is a layer on top of GQL that provides an extra set of tooling and optimizations.

It is very useful in the microservices world, where you have lots of small responsibility apps (like in our project) and you want a centralized place to hold the data and communication between them on top of a centralized database.

How we connect Stencil and Apollo

We created a gateway app that implements the Apollo server and as the frontend app developer, I had to implement the GQL communication layer over Apollo.

In order to do so, I started looking for a library that made it easier to send queries to the Apollo server from the Stencil app. That’s when I came across @apollo/client. This package provides the basic layer for us to communicate with the Apollo server layer.

However, my problem was that 95% of its docs are focused on React and the official site didn’t mention anything about Stencil.js.

That wasn’t good enough, so I continued my search until I found the stencil-apollo package.
This package contains the @apollo/client and adds a couple of extra helpers on top, like apollo-boost graphql graphql-tag and more… (We will explain each of them in a sec).

The only problem was that the docs were not very comprehensive, but I managed to overcome it and build a simple Stencil app that could query an Apollo server so here are the results.

About stencil-apollo dependencies

stencil-apollo uses the following packages and I will explain what they are used for:

  • apollo-client — the basic layer that actually communicates with the server
  • graphql and graphql-tag — those give us the syntax to write our queries with graphql syntax and a special tag for it
  • apollo-boost — this gives us some wrapper around the basic apollo-client package (some defaults and inner packages). We will use its ApolloClient class.

How to use stencil-apollo

In order to get it to work, we have to wrap our high order component with the apollo-provider tag:

Notice that we are creating a client connection:

client = new ApolloClient({
uri: 'http://localhost:4000/graphql'
});

And we wrap the entire app-root content with the apollo-provider like so:<apollo-provider client={this.client}>…</apollo-provider>

Now we can use this connection in our inner components

Now in order to use it as a wrapper component for the query results, we can do the following:

Note that we used the apollo-query tag in order to pass our query to the apollo server and get a response we can render

<apollo-query query={gql`QUERY HERE`} renderer={RENDER FUNCTION} /> 

The query is a GraphQL syntax query as you can see in the example and the render function is a function with 2 parameters:
({ data, loading }) => {...} the data param is the query result and the loading param will be true until we get the response from the server — data will be null. (You can place a spinner during this time) until then data will be null.

When the data is returned from the apollo server, the function is called again with loading value = false and data as the result of the query (you can see this used in the example above).

How to call apollo query from code

In some cases, we will need to query the apollo server and, based on the response, our flow will change — so the render is dynamic and not always rendered like in the previous example.

Below I will show you how to directly call an Apollo query and use the response to do something other than rendering with the apollo-query tag.

In this example, we wanted to check whether the user is currently logged in or not and then decide if to redirect them to the login page:

Hold tight and we will explain exactly what's going on here:

  1. We defined the Router prop: @Prop() history: RouterHistory; We used Stencil’s DI mechanism to inject the router service instance
  2. We define a State prop so it will update our view when changed @State() showList: boolean;
  3. We define our Apollo client:
    client = new ApolloClient({ uri: ‘http://localhost:4000/graphql' });
  4. We use our components’ componentWillLoad lifecycle hook to start querying our server on component load, before the component starts to render (most of the cases we will use this hook to do such initialization stuff like we are using the ngOnInit call in Angular)
  5. Now we are querying the Apollo Server by using the ApolloClient.query function:
    this.client.query({ query: QUERY HERE }).then((result) => {...})
  6. We check the result for the users’ logged-in state — if he is not we will use the router to redirect him to the login page:
    if (!result.data.currentUser) { this.history.push(`/login`, {});}
  7. until the server responded we are hiding our data in the render function:
    {this.showList && <div class=”connectors-list”>...</div>}

And there you go… a fully functional flow that uses Apollo to query stuff and do some logic with it…

How to call apollo functions from code

As you can see in the example below:

Very similar to the previous example but the only difference is that we are using the ApolloClient.mutate function (to actually change the data) that accepts the mutation parameter and the variables parameter:

this.client.mutate({ mutation: mutationQuery, variables: Inputs})

Final Thoughts and Bonuses

My overall experience with Stencil: It seems like a combination of Angular, React, and Vue syntax-wise but in the overall feeling it is more like React class-based components. The only difference is that the final product is a small bundle of Web Components.

Stencil is very lightweight and fun to play around with… the learning curve is very fast for a React programmer because of the TSX part and a bit less fast for only Angular based programmers but it shouldn’t be a problem for a good programmer to work with and learn

BTW Stencil has a very cool feature of Hot Reload in dev mode… (like Flutter that is in my opinion a very good framework but this is for a whole article — Maybe when I will have time in the future).

The Hot Reload feature means that when you save your changes the app is not restarting but only rerendering the changed components so you can continue where you stopped and just see the changes immediately

It’s important to note that it can cause you some inconsistency issues when you are adding @State props to your component which makes a lot of sense because it only rerenders the component and doesn’t recompile it so you will have to hard reset your watch…

are adding @State props to your component witch make a lot of sense because it only rerenders the component and not recompile it so you will have to hard reset your watch…

I enjoyed using Stencil-Apollo. I was very straight-forward and easy to work with.

Hope to see you and the next articles… love you guys! gg wp!

--

--