Building URL shortener using React, Apollo and GraphQL — Part II

Written by pjausovec | Published 2017/12/29
Tech Story Tags: react | javascript | graphql | tutorial | reactjs

TLDRvia the TL;DR App

Photo by stock.tookapic.com

Table of Contents

Part II: Creating short URLs

In the part of this series we will create a React component and corresponding queries for creating Links. We will also implement the client-side hashing function and create a GraphQL subscription to automatically refresh the list of links as soon as a new one is created.

The code for this project is available on GitHub if you want to use it as a starting point. Note that the code might not be in-sync as I will be updating it as I am writing the remaining parts of this series.

Link Component

Let’s create a file src/components/CreateShortLink.js and define the UI for creating short links. The component has two inputs, one for the URL and one for a description, and a button that actually creates the link. Both values are stored in component state and can be retrieved when create button is clicked.

The main logic for creating the links with be in the createShortLink function where we will call a GraphQL mutation to create a link.

Create the GraphQL mutation

Similarly as we’ve done with the queries, we need to create a mutation and then wrap the component with graphql container.

In the previous post, we have already used a GraphQL mutation for creating links:

mutation CreateLinkMutation {createLink(description:"First link from GraphQL",url:"http://example.com",hash:"somehash") {id}}

This time, instead of hardcoding the values, we need a way to provide the values to the mutation using. To declare variables in a mutation (or a query), you simply prepend a $ sign to the name of the variable like this: $myvariable.

Let’s take the above mutation and refactor it to use variables:

mutation CreateLinkMutation($url: String!, $description: String!, $hash: String!) {createLink(url: $url,description: $description,hash: $hash) {id}}

Inside the parenthesis next to the mutation name we declared the required string variables — later on, we will remove the hash variable as we want to create the hashes using a serverless function. These values are then passed to the createLink mutation and when mutation executes, it returns us the id of the create short link.

And just like with the queries, we can reference this mutation when we wrap our component in the graphql container like this:

To access and call the mutation, Apollo injects the name of our mutation to the props objects — we just need to call it and provide the values for those three variables. Before we do that though, we need to implement the hashing function we will use to create the short links. I am not going into the actual hashing algorithm, but we are going to use a unique id (in our case we will use the number of links) and convert it to a base 62 encoding to get the hash digits. Finally, we translate those digits into a unique hash string. This is the resource and explanation I used to implement the algorithm below.

Since we need to pass an itemCount to the hashing algorithm, we need a way to get that using GraphQL. Luckily for us, Graphcool automatically implements aggregation queries for all types in the schema. The aggregation queries are named following this format _all[TYPE_NAME]Meta where TYPE_NAME is the name of your type (Link in our case). A query to get the count of all links would look like this:

query GetLinkCountQuery {links: _allLinksMeta {count}}

Notice the links name? This is how you can name the result of the query, so you can reference to it in the code. Without that, the name of the prop would be _allLinksMeta .

There are two ways we can obtain the link count — we can follow the same pattern we did when executing the queries in the previous post — basically, execute the query, check for errors and if query is done loading and then return. The second option is that we could execute the query on demand when we actually need the value. We are going with the latter in this case, just to demonstrate how to do that.

So, instead of using graphql container to wrap the component, we can use withApollo container that injects the Apollo client instance as a prop to the wrapped container. Similarly as it injects the queries and mutations.

Let’s bring this all together — the hashing code, import withApollo from react-apollo, add the link count query and take care of the component wrapping:

Now that everything is in place, we can write the code that’s going to get the values we entered, get the link count, create a hash and finally run the mutation to create a new short link.

Next, let’s update the render method in App.js and add the CreateShortLink to it:

At this point, you can navigate to http://localhost:3000 to see it in action:

Creating new links

Note that it’s not perfect as we need to reload the page manually each time we add a new link. We could use the GraphQL subscriptions to handle this.

Create a GraphQL subscription

In addition to queries and mutations, GraphQL also supports subscriptions. With subscriptions, you can push data from the server to any clients that want to listen. A common scenario for subscriptions is notifying clients about certains events, such as creation or deletion of an object or updated fields.

Install the dependencies first:

$ yarn add apollo-link-ws subscriptions-transport-ws

Next, we create a web socket link and use the Subscriptions API endpoint for our Graphcool service (run graphcool info to get to the endpoints). With both links set, we are going to use split and getMainDefinition functions from Apollo to check what type of operation is being used (e.g. a query, mutation or a subscription) and return the correct link. Here’s how the split function looks like (explained in more details here):

import { WebSocketLink } from 'apollo-link-ws';import { getMainDefinition } from 'apollo-utilities';

.....

// Set up subscriptionconst wsLink = new WebSocketLink({uri: `wss://subscriptions.us-west-2.graph.cool/v1/xxxxxxxx`,options: {reconnect: true}});

const httpLink = new HttpLink({ uri: 'https://api.graph.cool/simple/v1/xxxxxxxxx' });

// Splits the requests based on the query type -// E.g. subscriptions go to wsLink and everything else to httpLinkconst link = split(({ query }) => {const { kind, operation } = getMainDefinition(query);return kind === 'OperationDefinition' &&operation === 'subscription';},wsLink,httpLink,);

Then we can use this link variable when creating the Apollo Client instance:

const client = new ApolloClient({link,cache: new InMemoryCache(),});

At this point everything should still work they way it did before — we need to create and actually use the subscription in the LinkList component where we are showing all links.

Here is the query for subscription that triggers each time a new link is created:

As with mutations and queries, we provide a name for our subscription and define the filter we want — in our case, we want to get back all fields when new object is created.

We are going to use a function called subscribeToMore on the allLinksQuery prop to subscribe to updates. This method takes two arguments: first one is the subscription query we defined above and the second one is an updateQuery function that gets called each time a new link is created. This function takes the previous state and data that was sent from the subscription and we need to merge the new data with the previous state and return the updated state. Let’s implement this in the componentDidMount function in the LinkList:

One last thing we need to do to make this work correctly is to update the fetchPolicy to network-only when querying for link count in the CreateShortLink.js. Since we are using the subscription we don’t have to refresh the page anymore to see the updates — not refreshing the page also means that each time we execute the link count query will get the cached number and our hashes will be incorrect. The updated line is in bold:

const linkCountQuery = await this.props.client.query({query: GET_LINK_COUNT_QUERY,** fetchPolicy: 'network-only',**});

Network-only option tells Apollo to not use the cached values and always re-query. We will use the same policy once we add authentication.

Ok, let’s try this out now! Go to http://localhost:3000 and see that as soon as you add a link, the list of links gets updated automatically.

Subscriptions in Action!

Thanks for Reading!

You can follow me on Twitter and GitHub. If you liked this and want to get notified when other parts are ready, you should subscribe to my newsletter!


Published by HackerNoon on 2017/12/29