Home Pokégraph - Evolving querying with GraphQL - 3/3
Post
Cancel

Pokégraph - Evolving querying with GraphQL - 3/3



Continuing with the series, let’s conclude with this final article. While the first post was focused on the modeling of the domain and second post focused on how to perform GraphQL queries with the backend, this will cover how I made it shine in the frontend with Vue 3 and Apollo.

Disclaimer: I’m not a UI/UX expert, but I did my best to keep the styling properly scoped and structured with my limited CSS skills =p


Vue, The Progressive JavaScript Framework


Vue is an open-source javascript framework for building web applications, released at the beginning of 2014. It’s a very straightforward, powerful, and progressive framework. I’ve been using it personally and professionally for a while and can certainly attest to how it allows me to produce very clean and well-structured applications with little effort.

It is currently in version 3.x and has been improving with many nice features, proving that its community does solid work in maintaining and ensuring a good development experience.

For this project, I initially started it using the classic Options API. However, I fully refactored it to use the new Composition API and its native support for Typescript. From what I’ve learned, this is the definitive way to use Vue.

The Apollo library is another crucial piece to make Vue communicate with our now up-and-running GraphQL server, allowing me to integrate GraphQL in this Vue app easily. For more advanced features, check their official documentation.


Components hierarchy


Keeping up the initial structure built by Vue CLI, I organized the Pokégraph with the following structure:

  • components
    • MainDevice
      • AttributesDisplay
      • AvatarDisplay
      • GenderRatioDisplay
      • JoystickControl
      • LegendaryDisplay
      • LoadingSpinner
      • StatsDisplay
      • TypeDisplay
    • SearchBox
  • composables
  • graphql
  • models

App .vue

Before we start, notice how simple the code becomes with Composition API. I got rid of all the setup(), methods(), watch() in all components and all I needed to enable this simplicity was:

 <script lang="ts" setup>

Now, going to App.vue, the application component entry point, I placed the child-components SearchBox and MainDevice in the template and you’ll see them all detailed.

Script

In the script section, I start running the useQuery function from @vue/apollo-composable to perform two queries defined here; the first to list all Pokemon and display the count of all discovered monsters; the second to search for one in specific, in which I set its number within the reactive const named variables.

const { result: listPokemonResult, loading } = useQuery(listPokemon);
const { result: pokemonByNumberResult } = useQuery(pokemonByNumber, variables);

A fairly simple component to search Pokémon by number. Besides, it shows the Pokémon number and because it works via two-way binding, you can type the value in the field and press enter to trigger @keyup.enter, calling a method that will emit a component event to the parent component. This technique allows us to make components with one another.

MainDevice

The primary device is a Pokédex itself. It’s effortless in logic because all the complexity was split into smaller components that use its prop that receives the ViewModel from the server and then passes it to the inner components. To make sense of it, I created the PokemonViewModel interface, so further on, I can cast the result into it for some components that need the clarity of a strongly typed object.

The template uses a static image of a Pokédex as the background of a div, with some clickable buttons well positioned within its joystick cross. When you click on each, you can goUp, goLeft, goRight and goDown. These methods will emit the onPokemonNumberChanged with a calculated Pokémon number to the App component, which will update its reactive variable, fetching the data from the server every time it changes. For emitting events throughout the application, I’m using a nice library called mitt.

If you read the first post of this series, you will notice the model structure that allows us to navigate traversal through a Pokémon evolution or pre-evolution path using the U1p and Down arrows respectively.

The MainDevice template section is composed of the following components:

  • AttributesDisplay: It just a the Height and Weigth of the Pokémon on the top left of the device display.
  • GenderRatioDisplay: Also positioned on the left side, this displays the proportion of males and females of the Pokémon species in a progress bar-like style.
  • LegendaryDisplay: A complimentary label positioned above the avatar indicates when a Pokémon is legendary. Only for a few ^^
  • AvatarDisplay: It displays the image of our Pokémon in a squared container, if any image was set. I used some Transitions to give a nice effect to it.
  • TypeDisplay: As you probably know at this point, a Pokémon might be classified up to 2 types, so this component displays the badges for each type accordingly and with dynamic background colors listed in this PokemonTypes enum:
  • JoystickControl: Invisible clickable buttons positioned right over the joystic cross of the device.
  • StatsDisplay: Displays the Pokémon base stats on the right blue panel. It resembles a calculator and, indeed it does the sum of all stats into the total computed property.


Subscriptions


Composables

In the context of Vue applications, a “composable” is a function that leverages Vue’s Composition API to encapsulate and reuse stateful logic.

I placed them in the composables folder. One for watching Apollo subscriptions, and another for using Vue Toast to pop-up fancy notifications. You can learn more about Composables here .

Back to the App.vue and in the onMounted lifecycle hook, I’m calling the composable watchSubscriptions() method that will run useSubscription() from @vue/apollo-composable and watch some messages comming from the server, which can be onListedPokemonResult, onInsertedPokemonResult or onUpdatedPokemonResult. These messages are sent when performing querying or mutations.

To get all this working, I had to configure the ApolloClient with the WebSocketLink against a wss uri. All this was set in the main.ts file:


Final thoughts


Building this project was very fun for me. I learned more about GraphQL and its capabilities. I confirmed why it is so widely spread as an alternative to reduce endpoint complexity for fetching large amounts of data with flexibility.

Although I just populated the database with the first 151 pocket monsters, I’m sure it would Catch ‘Em All effortlessly. On top of that, Vue shows once more that it’s a solid framework, ready to progress and supporting practically any technology, yet making everything look simple.

Thank you for reading until now. If it helped you and you liked the project, please don’t forget to give a ⭐ to the repository.


Check the project on GitHub



This post is licensed under CC BY 4.0 by the author.