InfernoJS meets Apollo in a functional way [part 1]

Roman Zanettin
5 min readFeb 17, 2017

--

Yesterday I’ve published a port of react-apollo for InfernoJS called inferno-apollo together with a starter app.

Today I’d like to give people new to InfernoJS and/or graphQL with apollo a quick intro while using the starter app.

Starter app under the hood

Let’s start talking about the starter app and what’s inside.

The starter app is based on create-inferno-app. It provides a minimal setup to run your InfernoJS app with no configuration needs. Beside that, inferno-apollo is on board as well to add the graphQL logic and incompose to enrich the pure functional components.

On the backend runs a node/express server providing the graphQL wrapper . It serves data from the SWAPI API and runs the graphiQL interface to test your queries and analyze the schema.

Install and run

Okay let’s get started:

git clone git@github.com:zanettin/inferno-apollo-demo.git
cd inferno-apollo-demo
npm install

After the installation was successful run these two commands in separate terminal tabs:

npm run start:server

to start the express server on localhost:4000 and

npm start

to run the frontend on localhost:3000.

Hello graphiQL

Navigate to localhost:4000 and you’ll find the graphiQL interface.

On the left side you can write your query and get also auto-complition support while typing or press alt+ space.
On the right side you’ll find the result and error after running your query (cmd + enter).

To discover the whole schema you can find the “Docs” on the upper right corner. Click on any node to discover its data.

How to query on graphQL

To start, we should fetch all Films and its directors. This would result in a query like this:

{
allFilms {
edges {
node {
title
director
}
}
}
}

Nice! But what are this edges / node thing?
That’s how graphQL solves connections. If more than just one child can be returned, you’ll face this edges / node nesting.

Frontend

Backend works. Now let’s start analyzing the InfernoJS frontend. Start with the index.js file in the /src folder.

Inferno.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('app')
);

Okay. We tell Inferno to render all it’s content into the div with the ID ‘app’. Before we do that, we nest our App component with the ApolloProvider. This provides the graphQL logic throughout the app.

But whats this {client} attribute on ApolloProvider and where is it coming from?
For this, we have to have a loot into /src/shared/graphql/apolloClient.js.

export default new ApolloClient({
networkInterface : createNetworkInterface({
uri : 'http://localhost:4000/graphql',
})
});

In this file we define the network interface and tell the underlaying apollo-client where to find our graphQL backend. In our demo case it’s on the node/express server which runs on localhost:4000. Please note, that there is also a shortcut if your graphQL endpoint is on the same server and listing on /graphql route. For this you could just use:

export default new ApolloClient();

So no further config required.

Now our demo app is ready to query the graphQL backend. Let’s checkout the index.js file in /src/App.

const App = (props) => (
<div className="App">
<div className="App-header">
<Logo width="80" height="80"/>
<h2>Welcome to Inferno with Apollo</h2>
</div>
<FilmList /> </div>
);
export default App;

Here we have our first pure functional component. A function receiving props and returning JSX. Beside the rendering info for the header you see the next component called ‘FilmList’ is placed as child of the App component. So let’s have a look into this file next (/src/Films/index.js).

Since this is a looooong file, we should walk through it in pieces. Starting with the components “render” function:

const List = (props) => (
<div>
<h1>Film-List</h1>
<ul className="List">
{
props.data.allFilms &&
props.data.allFilms.edges &&
props.data.allFilms.edges.map(renderFilm)
}
</ul>
</div>
);

Looks really similar to the App component we’ve seen before. We can imagine by looking at the props, that this component will get ‘data’ passed on props and on data are ‘allFilms’, another object. But where are this coming from? We didn’t define any props in /src/App/index.js where we included the ‘FilmList’ component. This is the result of our graphQL query we define in this component:

const query = gql`
query AllFilms {
allFilms {
edges {
node {
title
director
}
}
}
}
`;

If you compare the JSON like syntax of this query with the structure we’re expecting in the render function, it makes perfectly sense! Except where the key ‘data’ is coming from. This is the default wrapper for the apollo-client. You can also configure this if you don’t like the name ‘data’.

Now it’s time to compose the query with the component:

export default compose(
graphql(query),
)(List);

Okay, that’s it! Nice right?

Do it nice with loading indicator and error handling

Let’s enhance our composition by using some more incompose higher-order-components (HoC). Namely ‘branch’ and ‘renderComponent’. Short intro:

  • branch
    Expects an expression and an a HoC to render. If the expression is true, the HoC will be rendered instead of the original component.
  • renderComponent
    Takes a component and turns it into a higher-order-component (HoC). This is required, because ‘branch’ expects a HoC.

And how does it look in code?

export default compose(  // compose graphQL query to component
graphql(query),
// if something went wrong during apollo client init
// or if we do have a network error, we return
// a branched component and not our list component.
branch(
(props) => !props.data || props.data.error,
renderComponent(Error),
),
// while loading, we want to show a loading screen
// to our users, so we compose another branch
// which checks the loading state
branch(
(props) => props.data.loading,
renderComponent(Loading),
)
)(List);

And that’s it.

If you like this article, please give me a vote and check out the second article explaining the inferno-router and using props to query graphQL.

Hope you enjoyed reading this and hopefully trying some queries on your own. If you have any questions leave me a note or ping me on @roman_zanettin also on twitter.

--

--

Roman Zanettin

frontend lead engineer, JavaScript and functional programming enthusiast, graphQL, love to discover new programming approaches and patterns.