GraphQL, Laravel, and Vue Part 2: Frontend

Brandon Shar
Dec 4, 2018 · 17 min read

Welcome back! If you’re new to our voyage and, or don’t have a GraphQL api set up, I’d recommend you start here where we set up our GraphQL backend in Laravel with the Lighthouse package. If you just want to go through the front end, feel free to scroll down to “Install Vue Apollo” and check out the example repo at that commit.

As a reminder, this tutorial is not meant to give you a deep level of expertise, but more to give you a tour through setting up GraphQL in your own apps and the power that it brings. If you’re still reading, I’ll assume you’re ready to dive in to the front-end, so let’s go!

When we left off, we were busy basking in the glow of our shiny new GraphQL API: querying products and shoppingCarts, and adding the former to the latter. But, as great as graphiql is, we probably don’t want to ship that UI directly to the end user… Do we? No, we probably don’t. So let’s integrate a frontend with our api!

Just as before with Laravel, I’m going to assume you have some VueJS knowledge. If you don’t, you’re likely to get a bit overwhelmed as I bring in all of the components in one step to save us some exposition time. If you are unfamiliar, Vue is an excellent javascript framework to learn. I’d recommend starting with the VueJs docs. If you’re a frequent Laravel user, Laracasts also has some great entry level Vue tutorials that use Laravel as an entry point.


Everyone here who wants to be still here? Awesome, let’s install some things.

I’m going to assume that everyone who is here came from my last tutorial, but I will try to call out the minor differences you might see if you aren’t using the Vue setup that comes bundled with Laravel. All of this will work just as well if you fired up Vue via the unbelievably excellent Vue CLI v3 UI (my preferred world). If you use yarn instead of npm this will also work fine by just substituting your usual yarn commands in place of the npm commands I use.


Install Vue-Apollo fa6a2da2ccfb8a74a6502892d460f418b3530cbd

First off, if you’ haven’t already run npm install in your project, you should do so now.

Now let’s install a few packages.

npm install vue-apollo apollo-boost graphql graphql-anywhere

Vue Apollo is the package we’re really after and it also requires apollo-boost and graphql for its default configuration. Graphql-anywhere is another great package full of utility functions that will help us with our query fragments later. You’ll recognize it when we import the filter method in a bit.


Configure Vue Apollo dcde8ed217e660f2b8def0041658f2f672008844

All of the configuration we’ll need will take place in resources/js/app.js. If you’re not using Laravel, these will go wherever you’re instantiating Vue. When in doubt, check the Vue Apollo docs. We’ll need to import Apollo, give it just a bit of configuration, and attach it to our Vue instance. This is probably easiest to see from the diff, so let’s take a look at that:

Now that we’re all configured, let’s get some data from the server!


Set up Shopping Cart Page 3f5b4b733183dab07e57c1f1823f495cd6afd245

Let’s start by creating a new view at resources/views/shopping-cart.blade.php to serve as our entry point to our Vue application. It will inherit from our app layout and just render our main component. We’ll also add a corresponding route to reach it.

# routes/web.php
Route::view('/shopping-cart', 'shopping-cart');
# resources/views/shopping-cart.blade.php
@extends('layouts.app')
@section('content')<div class="container">
<div class="row justify-content-center">
<shopping-cart-page />
</div>
</div>
@endsection

For those of you not in Laravel, this will just be your main component.

If we hit this route in our browser at /shopping-cart, we get a console warning about an unregistered component. It’s right! So let’s make our component and get it registered. Let’s run npm run watch so that we can write javascript without thinking about about transpiling and replace the example-component registered in resources/js/app.js with a component we’ll call ShoppingCartPage.

We should have a line of code in resources/js/app.js that looks like this:

Vue.component('shopping-cart-page', require('./components/ShoppingCartPage.vue'));

Now, our console should be pretty frustrated as that component doesn’t exist yet, so let’s create it at the path we specified!

Here’s our first component!

Let’s break it down a bit, starting with the query.

That query itself should look very familiar to the queries you wrote in the earlier section when testing out your api. We’re giving it a name ShoppingCartPage, which can be anything, but is the name of the component it’s in by convention. Within the query itself, we’re asking for the currently logged in user, their id and shopping cart, that shopping cart’s products, etc. We’re also asking for the array of products and some information about them.

This query is being passed into the gql method that transforms our string into graphql nodes for the server to read. The first time I saw this syntax, I was very confused and spent a lot of time coming up with the right words to google. It turns out, those words are “tagged template literals” if you’d like to know more, but the gist of using it here is that you can create functions that accept backticked strings and use this syntax.

Let’s look next at the template and the ApolloQuery component serving as the root node. ApolloQuery is a renderless component that handles grabbing the data requested from our query and passing it back to us via a scopedSlot.

If you haven’t seen $options before in a template, it lets us grab data directly from the exported Vue object. Storing data here gives us two advantages: Vue won’t waste its resources setting up reactivity hooks on data that won’t change AND it allows us to access these properties from outside of the component.

The scopedSlot passes us an object with a result field containing another object with data, loading, and error, which are fairly self-explanatory.

If you’re unfamiliar with higher order components and scopedSlots, I’d recommend both Vue’s docs and Adam Wathan’s writings on the subject. The gist of it for our purposes is that it’s a slot that the wrapping component can provide data to.

We’re then taking those values and just dumping out data to the screen as soon as loading is false.

If we hit the route now (for me, that’s http://laravel-vue-graphql-tutorial.test/shopping-cart), we should see some dumped out data!

Congratulations, we did it! Queue the celebra- well, hang on a second. Sure the documentation from the last section is awesome. And the fact that future developers have an easy way to tell the exact structure of the endpoint and don’t even have to look at that awesome documentation sometimes is really great, but is this really that much better than REST? Are those two things enough to move to a new standard? Well, luckily, you don’t have to decide that yet, because we have some more power to show you!


Style and Component It Up aa6c04c8a2193697b356ba582eec345596820cbf

Have you ever watched one of those cooking shows where they show you a series of steps, put the food in the oven, and then immediately pull out a finished one so we don’t have to wait a while for something that’s not super necessary to get to the point? We’re going to do a bit of that here.

Let’s break the app down into components and (with the help of my wife @heatherakshar) style them up a bit!

Here’s the general structure:

And here’s what it looks like now!

To jump to our styled point, I’d recommend just checking out this commit with git checkout aa6c04c8a2193697b356ba582eec345596820cbf. If you’d prefer to copy paste, you can always grab the code and see the diffs at the current commit:

Take a few minutes to familiarize yourself with the code. I hope it’s pretty straightforward though, and similar to what you’d have done.

If you’re unfamiliar with the usage of v-bind, it just destructures objects and passes them as individual props. So saying <component :name=”person.name” :age=”person.age” /> can be the same as saying <component v-bind="person" />.

The main thing I’d like to note here is that we have no events or state in any of these components. They are purely powered by the props being passed down to them by the container component that requested all of the data. This is a really great way to design components. It makes it easy to reuse them, debug them, and reason about them.

But, it’s lacking something isn’t it? Some context for the presentational components would be great. Sure we have props, so we know what data is coming in, but if we want more or different data we have to figure out where it’s coming from and modify it there. And what if you change that data, but another component relied on the data being the way it was before? Not only is there not a good way to know, but your pull request won’t really even highlight it since the change is being made (in some cases) several components up the tree.

Let’s see if we can solve this concern by using another feature of GraphQL and introduce something called fragments.


Convert to Fragments: ba7126f3c755cc642a29f403d9758d4226774d94

Our current query in the ShoppingCartPage is a single query all written as a string. GraphQL also supports an alternative way of writing queries by composing them out of fragments. Let’s walk through creating one with our products list to see how it works.

Let’s start by identifying the part of the query that the ProductsList is concerned with (bolded)

query ShoppingCartPage {
me {
id
shoppingCart {
id
total
totalBeforeSavings
savings
products {
id
name
price
savings
quantity
}
}
}
products {
id
name
price
savings
}

}

Now, we can introduce it as a fragment on ProductsList.vue:

...
<script>
import gql from 'graphql-tag';
...
export default {
...
fragment: gql`
fragment ProductsList on Query {
products {
id
name
price
savings
}
}
`,

}
</script>
...

Let’s break down what we just added. Similar to our query we wrote before, we’re adding a fragment key right on to our exported Vue object. The value of this key is a string being passed into the gql function. Instead of the query keyword to start it, we used the fragment keyword. The ProductsList name, just like before, is the same as the component it’s in (by convention). The on Query is a necessary bit of syntax that lets the fragment know what its parent component is. Since we’re putting it directly on the base GraphQL query, we use Query.

Now, ProductsList can dictate what GraphQL data it needs to properly render. We can include that fragment in our main query by replacing the bolded segment from where we identified the query with …ProductsList and then interpolating the fragment at the bottom of the query string (after the closing } but before the closing backtick with ${ProductsList.fragment}. Here’s what the new query should look like:

query: gql`
query ShoppingCartPage {
me {
id
shoppingCart {
id
total
totalBeforeSavings
savings
products {
id
name
price
savings
quantity
}
}
}
...ProductsList
}
${ProductsList.fragment}
`

Now ProductsList can tell the main query that it wants a list of products with certain fields and the top level query doesn’t have to be concerned with it anymore. Now, if someone decides that the ProductsList component needs another piece of information from the API, we can just add it directly on the component where it’s being used. This concept is called co-locating fragments.

You may have noticed though, that we just introduced the same problem to ProductsList. The ProductsList only cares about having a list of products, it doesn’t know what information about products it needs! So let’s solve it with another fragment.

We can introduce an additional fragment within ProductButton that will be on the Product type (instead of the query type before) that specifies which individual fields we want.

fragment ProductButton on Product {
id
name
price
savings
}

Then, we can import that fragment into ProductsList so that the ShoppingCart container still gets the whole query. Now we’re getting somewhere!

// In ProductsList.vue
fragment: gql`
fragment ProductsList on Query {
products {
id
...ProductButton
}
}
${ProductButton.fragment}
`,

It’s good practice to always make sure the id is requested, even if the child component doesn’t need it. Fragments are all merged together by gql, so we don’t have to worry about requesting things twice.

Now, when a developer wants to edit this ProductButton component and decides they need more, less, or different data, they don’t have to wonder where’s it coming from or who else might be using it, they can just make the change right here in the component!

There’s just one last thing we should do here, as a best practice. Suppose we have two components like PersonDetails and PersonButton. Each of these components has a fragment that requests some data from the Person type which is queried on their shared parent, PersonContainer. PersonDetails requests both name, and age while PersonButton only requests name. As we know, both of these fragments are brought into the main query on the parent, it’s run once, and then the parent v-binds the returned person object onto each component. Pretty straightforward sounding use case.

Now suppose you get a change request to add the age to PersonButton. Well, PersonButton is already getting the age passed to it, even though another fragment is the one requesting it, because it’s being returned in the main query. Since it already has the idea, we can just go ahead and use it. And that will work fine… until one day, perfectly reasonably, a developer decides that PersonContainer no longer needs the age field and deletes it from its template and its fragment. Now with no age field being requested, PersonButton is broken and we have no idea why.

To prevent this situation, we can use filter from the graphql-anywhere library. Filter is a method that accepts two arguments: a fragment and a data object and then returns ONLY what the fragment requested. With filter, we never have to worry about passing the right data, diving deep into the returned objects, or relying on some field that we didn’t request! Let’s see how much nicer it makes our v-bind statements.

In ShoppingCartPage, we just need to import filter with import {filter} from ‘graphql-anywhere’;, register it on our component with: methods: {filter},, then replace our ProductsList v-bind with:

v-bind="filter($options.components.ProductsList.fragment, data)"

A little wordy since we have to go through the components object, but it brings some nice functionality! After importing and registering it in our ProductsList component, we can similarly replace the ProductButton v-bind with:

v-bind="filter($options.components.ProductButton.fragment, product)"

If you’re feeling a bit overwhelmed by this fragments concept, you’re not alone. I was too when it was first introduced to me. When I started using it though, I bought in quick. The ability to co-locate the data requirements of each query within the component, while still keeping them as pure presentational components, and only making one api call? Count me in!

Let’s repeat this pattern for our other components! If you get stuck, want a hint, or just want to skip ahead, check out the diff here:

Now this is getting pretty nice! Only one concept is missing… our mutations! You know, that thing that actually makes our app do something!


Create Mutations db21e2c44522308f75f0db8fa2e6d97aefecd95f

We’re now in the home stretch of our app! Let’s add our addProductToShoppingCart mutation to the ProductButton so that it can add a product to our shopping list. We’re going to use another wrapping component, this time called ApolloMutation that accepts a mutation and some variables and exposes another scopedSlot.

Let’s take a look at our new component and dig into some of the changes.

We’ll start at the top, and the first thing you’ll probably notice is the new ApolloMutation wrapping component. As mentioned above, it accepts a mutation (that we define just like our query from before) and an object full of variables to pass to the mutation. In our case, we want to pass the product’s id as productId (since that’s what GraphQL is looking for). Don’t be afraid to refer back to Graphiql if you need a refresher on our mutation docs, that’s what it’s there for!

The ApolloMutation component then exposes us a scopedSlot with an object containing a mutate key and a loading key (among other things like error, but these are the ones we’re concerned with). The mutate key is a function that should be called whenever we want to fire the mutation. The loading key is a boolean that tells us…. yeah you probably guessed it, whether the mutation is loading. We want to fire the mutation anytime our button is clicked and we don’t want people to click while it’s loading, so we can just add all of these props and events directly to our button!

Down in the script tag, you’ll see that we imported some components and added a mutation. The mutation itself looks a bit like our query, though it has a little extra ceremony about it. The first line mutation productButton($productId: ID!) { names our mutation and clarifies its signature so we know what variables we might be passing it. In this case, since it’s a required param (note the !), it’s a variable we have to pass it. What follows is the actual mutation, the same as it looks in Graphiql, using the $productId variable to represent the data we’re going to pass in.

Our payload looks exactly like our queries we’ve seen before, and guess what? Our fragments come in handy here, too! Thanks to fragments, we can request exactly what new data our presentational components need.

But what’s the point in doing that? And why are we being so careful about adding the id field to everything? And shouldn’t we be emitting an event to let something know we updated and have new data…?

Whoa.

How…

What?

Even the totals?

Let’s break down what just happened.

If you received a console error like Unexpected token < in JSON at position 0, make sure you’re still logged in!

Apollo includes something called the InMemoryCache. It’s an in-browser caching mechanism Apollo uses to ensure that we don’t have to hit the GraphQL API for the same data over and over. (Because GraphQL uses POSTs to handle its data, it doesn’t have the built in caching that REST does.)

Where there is a cache, there’s hopefully some cache invalidation. Vue Apollo provides some nice interfaces to allow you to modify the cache manually when you modify data, but Apollo’s magic really comes out when you let it do its thing.

Whenever you return data from a mutation that includes both an ID and at least the same fields as something in the cache, Apollo will update the cached data automatically. So by using our fragments in the mutation payload, Apollo knows to automatically update our cached shoppingCart data with the returned data. This update will change the data in our props and trigger Vue to re-render.

If you’ve ever been caught emitting constantly, trying to keep track of state during updates, making extra REST calls defensively, reloading pages from the server, and reading the Vuex docs wondering if it’s for you (and nice as Vuex is, hoping it isn’t!), I hope this is blowing your mind the same way it blew mine.

And we still haven’t actually added any internal state to a single one of our components.

That bears repeating one more time, we have all of this data reactivity without a single stateful component in user-land. You know the best way to manage state across an entire app of components? Have less of it.

Let’s add on our second mutation so that we can delete items as we wrap this up. Hopefully you’re able to get most of the way there, but when creating this repo, I ran into an annoying circular dependency issue that I’d like to point out so you don’t stuck the same way I did.

Inside the newly created DeleteProductButton, you’ll notice the odd usage of require and beforeCreate. This is necessary because we’re importing the grandchild of this component into itself and creating a circular dependency. According to the Vue docs, this is the best way to avoid it, so here we are.

Here’s the code for this final segment:



I’m waiting because I assume (and hope) you’re currently adding and removing products with a slack-jawed expression. At least, that’s what I did when I first started using GraphQL and Apollo.

Is this app impressive on the surface? No, not really. What makes it impressive is that you know how little code you wrote. How not a single one of your components has a data property to track state. How little you have to keep track of from component to component as this app evolves.

Obviously, as your app grows you’ll find reasons to introduce state for user experience, but thanks to GraphQL and Apollo, you won’t need to for your data, the main thing you work with every day.

I spend a lot of time in the React world, where you have your choice of stateful or stateless components and each has a separate syntax so it’s more obvious which is being used. Thanks to Apollo and GraphQL, I’m now actually somewhat surprised when I come across a stateful, class-based component, and this is over an entire in-production SaaS app. Before? I didn’t even know React had stateless components, that’s how little I used them.

For me, having stateless components (Vue or React) also imposes a bit of discipline to keep them that way. Simple, single-purpose components (just like we’ve practiced for so long on the backend!) are good! Easy to reason about and use, easy to debug, easy to test.


I hope this was instructive and gave you a starting point to incorporate GraphQL and Apollo into your own apps (or just know more about GraphQL and not change a thing in your own apps). I recognize that I covered topics fast and skipped over a lot of details, things like GraphQL best practices, more tricks with fragments, using arguments in queries, paginating, and on and on and on.

If you found this a productive read, I’d be happy to dive in to anything of those subjects in depth in a subsequent blog post. You can comment here, reach out to me on twitter at @brandonshar (warning, I retweet and like political things), or just open up an issue or comment on the Github sample code.

Thanks to you for reading and a huge thanks to all of the open source libraries and authors that make this code possible!

Most notably for this tutorial thanks to: Evan You for VueJS, Guillaume CHAU for Vue Apollo (and Vue CLI’s UI, just because I want to call out how great it is again), Taylor Otwell for Laravel, and Benedikt Franke / Christopher Moore for Lighthouse. And of course, thanks to the ton of packages and their contributors that these libraries depend on.

Thanks again for reading and find me on twitter at:

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade