Create Nuxt.js Universal Apps using GraphQL on Postgres

TL;DR: Server side render websites using Nuxt.js and GraphQL APIs over postgres using Hasura GraphQL Engine. Instant setup. Tutorial/boilerplate -> nuxtjs-postgres-graphql

Building universal applications can be difficult, but Nuxt.js aims to make it easier. Nuxt.js is a performant and modular framework that enables you to develop Universal Vue.js Applications.

What is a Universal Application? A Universal Application, also known as an Isomorphic Application, is an application whose code runs both on the client and server sides.

This article showcases a Universal Application that uses Nuxt.js and Hasura GraphQL Engine. The application is a blog-like application that displays a list of authors and their articles.

Note: The article illustrates the most important parts of the application. You can view the complete code in this GitHub repository.

SSR with Nuxt.js

Nuxt.js is a Vue.js framework that allows you to build server-side rendered (SSR) Vue applications. In a universal app built with Nuxt, the pages are rendered on the server-side and then sent to the client.

Due to its capabilities of supporting both server-side rendering (SSR) and single-page application (SPA), Nuxt.js enables you to build universal applications.

How does it work:

  • Pages: Nuxt is opinionated on the project directory structure. It transforms the .vue files inside the pages directory into application routes. For instance, the file mypage.vue corresponds to mywebsite.com/mypage. In the boilerplate example, you can see how the pages are structured. For more information on the directory structure that Nuxt enforces, read more here.
  • Server-side data fetching: The Nuxt community has built the apollo-module that lets you use the Apollo Client with a GraphQL endpoint.

Project Implementation

In this section, you will see the server-side data fetching in action. You will use the apollo module to fetch the list of blog authors and their articles.

Before going further, create the Nuxt project as follows:

npx create-nuxt-app blog-app

After running the command, Nuxt asks a couple of questions to help you configure the project. Choose these answers:

  • Programming Language - JavaScript
  • Package Manager - npm or yarn (whatever you want to use)
  • Rendering Mode - Universal
  • Deployment Target - any of the two

For the other questions, you can omit them or leave the default answers.

Project Dependencies

For this project, you need a couple of packages such as "apollo", "graphql-tag", and others. See the complete list of dependencies:

  "dependencies": {
    "@nuxtjs/apollo": "^4.0.1-rc.5",
    "apollo-cache-inmemory": "^1.6.6",
    "core-js": "^3.21.1",
    "cross-env": "^7.0.3",
    "graphql-tag": "^2.12.6",
    "nuxt": "^2.15.8"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

Add them to your .package.json file and run npm install.

Configure Apollo Client

The next step involves configuring the Apollo Client so you can perform GraphQL Operations.

With Nuxt.js, you can extend the core functionalities with modules. For this application, you will use the "apollo" module for Nuxt.

Go to the nuxt.config.js file and add the following code:

  modules: [
      "@nuxtjs/apollo"
  ],

  apollo: {
    cookieAttributes: {
      expires: 7,
    },
    includeNodeModules: true,
    authenticationType: "Bearer",
    errorHandler: "~/plugins/apollo-error-handler.js",
    clientConfigs: {
      default: "~/apollo/clientConfig.js",
    },
  },

In the beginning, you are registering the "apollo" module in your project. After that, you configure the "apollo" module:

  • the expires field in the cookieAttributes specifies when the cookie will expire and get removed
  • the authenticationType field configures the auth type of authorized requests
  • the errorHandler sets a global error handler
  • apollo-module in Nuxt accepts a clientConfig property which is used to configure the endpoint, authentication, and other parameters.

Since "/apollo/clientConfig.js" does not exist, the next step is to create it.

Create a new folder named "apollo" and then add a new file inside it called "clientConfig.js". Open the newly-created file and add the following code:

import { InMemoryCache } from "apollo-cache-inmemory";

export default function (context) {
  return {
    httpLinkOptions: {
      uri: "http://localhost:8080/v1/graphql",
      credentials: "same-origin",
    },
    cache: new InMemoryCache(),
    wsEndpoint: "ws://localhost:8080/v1/graphql",
  };
}

In the above code, you configure the endpoints needed to perform the GraphQL queries.

Before moving on, you need to define the error handler as well. Create the file "plugins/apollo-error-handler.js" and add the following code:

export default (error, nuxtContext) => {
  console.log("Global error handler");
  console.error(error);
};

The above plugin is a global error handler, and it's taken from the "@nuxtjs/apollo" documentation.

GraphQL Queries

For this application, you have two queries:

  • one to fetch the authors
  • one to fetch their article

Inside the same folder - "apollo", create a new "queries" folder. After that, create a new file called fetchAuthor.gql in the "queries" folder.

📂 blog-app
 └ 📂 apollo
    └ 📂 queries
        â”” fetchAuthor.gql

Add this query in the "fetchAuthor.gql" file:

query {
  author {
    id
    name
  }
}

Similarly, create a new file named fetchArticle.gql to store the query for fetching articles:

query article($id: Int) {
  article(where: { author_id: { _eq: $id } }) {
    id
    title
    content
  }
}

You will import the above queries whenever you need to use them in a page. With that being said, it's time to build the pages that display the authors and articles.

Fetch Authors

Open the pages/index.vue file and add the following code:

<template>
  <div>
    <h3>Authors</h3>
    <ul>
      <li v-for="item in author" :key="item.id">
        <nuxt-link :to="`/article/${item.id}`">
          {{ item.name }}
        </nuxt-link>
      </li>
    </ul>
  </div>
</template>

<script>
import author from '~/apollo/queries/fetchAuthor'
export default {
  apollo: {
    author: {
      prefetch: true,
      query: author
    }
  },
  head: {
    title: 'Authors of Blog'
  }
}
</script>

In the above code, author is a GraphQL query that fetches the list of authors from the database and updates the author property in Vue.

After that, it loops over the array of authors and displays each author on the page. Clicking on an author takes you to their article.

How does it work?

This GraphQL query is executed on the server and the data is available in <template>, which allows the page to be rendered server-side. The same query is then executed on the client-side, making it universal/isomorphic.

Fetch Article

Create the file pages/article/_id and add the following code:

<template>
  <div v-if="article">
    <div v-for="item in article" :key="item.id">
      <h3>{{ item.title }}</h3>
      <p>{{ item.content }}</p>
      <p>
        <nuxt-link to="/">
          Home page
        </nuxt-link>
      </p>
    </div>
  </div>
</template>

<script>
import article from '~/apollo/queries/fetchArticle'
export default {
  apollo: {
    article: {
      query: article,
      prefetch: ({ route }) => ({ id: route.params.id }),
      variables() {
        return { id: this.$route.params.id }
      }
    }
  },
  head() {
    return {
      title: 'Articles by Author'
    }
  }
}
</script>

Looking at the file name, you can observe the underscore (_). That means the page is dynamic - the "name" of the page comes from the Hasura backend.

If you go back to the index.vue file, you should see:

<nuxt-link :to="`/article/${item.id}`">

The "item.id" comes from the database, which means that each article has a different URL. Nuxt (and Vue) allows you to create dynamic pages for such cases.

Also, here you are using reactive parameters. Observe the following piece of code:

variables() {
    return { id: this.$route.params.id }
}

Whenever the parameter changes - the article id, it will re-fetch the query and thus update the page and show the requested article.

Other than that, it works like the previous query that fetches the authors.

Nuxt.js + Hasura + apollo-module = Universal Apps with GraphQL!

There is a boilerplate and a short tutorial to help you get started quickly! Check it out on Github.

Take it for a spin and let us know what you think. If you have any questions or run into any trouble, feel free to reach out to us on Twitter, Github, or on our Discord server.

This post was originally published on Jan 30, 2019 and updated on April 11, 2022.

Blog
11 Apr, 2022
Email
Subscribe to stay up-to-date on all things Hasura. One newsletter, once a month.
Loading...
v3-pattern
Accelerate development and data access with radically reduced complexity.