Aug. 27, 2018, 2 p.m.

Code with Hugo: consuming GraphQL from JavaScript, 0-config deployments with GitHub Pages and Parcel

Code with Hugo: Production JavaScript & Node.js Guides

An intro to consuming GraphQL from JavaScript and 0-config deployments using GitHub Pages and Parcel

This week continues with GraphQL, this time in JavaScript.

The second section is about deploying frontend apps for free using Parcel to bundle them and GitHub Pages for hosting

JavaScript GraphQL client requests in Node and the browser using graphql.js

> An example consuming a GraphQL API in JavaScript from Node and the browser using graphql.js

See the example live: https://codewithhugo.com/js-graphql-client-example/.

Full repo: https://github.com/HugoDF/js-graphql-client-example.

Fetching from Node

fetch.js:

const graphql = require('graphql.js');

const graph = graphql('https://graphql-pokemon.now.sh/');

const query = graph(`{
  pokemon(name: "Pikachu") {
    attacks {
      special {
        name
      }
    }
  }
}`);

if (require.main === module) {
  query().then(
    res => console.log(JSON.stringify(res, null, 2)),
    err => console.error(err)
  );
}

module.exports = {
  query
};
$ node fetch.js
{
  "pokemon": {
    "attacks": {
      "special": [
        {
          "name": "Discharge"
        },
        {
          "name": "Thunder"
        },
        {
          "name": "Thunderbolt"
        }
      ]
    }
  }
}

Fetching from the browser

graphql.js is isomorphic, it’ll also run in the browser, we’ll use Parcel to stitch everything together.

By default we fetch using the query from fetch.js, then when the user clicks the Try it button we use the contents of the textarea.

This code wires up the fetch logic with some reading the query from the DOM and updating an output div on completion, client.js:

const { query, graph } =require('./fetch');

const $queryElement = document.querySelector('.query');
const $output = document.querySelector('.output');
const $submitButton = document.querySelector('button');

$submitButton.onclick = () => {
  const queryData = $queryElement.value;
  runQuery(graph(queryData))
}

runQuery(query);

function runQuery (query) {
  query().then(
    res => {
      $output.innerHTML = `<pre><code>${JSON.stringify(res, null, 2)}</code></pre>`;
    },
    err =&gt; {
      $output.innerHTML = `Error: <pre><code>${JSON.stringify(err, null, 2)}</code></pre>`;
    }
  )
}

index.html:

<!DOCTYPE html>

<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="ie=edge" http-equiv="x-ua-compatible"/>
<title>JavaScript GraphQL Client Example</title>
<meta content="JavaScript GraphQL Client example" name="description"/>
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"/>
<style>
    body {
      font-family: -apple-system, BlinkMacSystemFont,
          'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
          'Open Sans', 'Helvetica Neue', sans-serif;
    }
  </style>
</head>
<body>
<p>For full documentation see: <a href="https://graphql-pokemon.now.sh/">https://graphql-pokemon.now.sh/</a></p>
<h2>Input: </h2>
<textarea class="query" style="min-width: 285px; min-height: 150px">
{
  pokemon(name: "Pikachu") {
    attacks {
      special {
        name
      }
    }
  }
}
  </textarea>
<button>Try it</button>
<h2>Output: </h2>
<div class="output"></div>
<script src="./client.js"></script>
</body>
</html>
$ npm install --save-dev parcel
$ npx parcel index.html

Open http://localhost:1234.

Sample GraphQL query result displayed in the browser

To test it out, we can change the textarea content to:

{
  pokemon(name: "Pikachu") {
    attacks {
      fast {
        name
        type
        damage
      }
      special {
        type
        damage
        name
      }
    }
  }
}

And click Try it. Which yields the following:

new GraphQL query output in browser

GraphQL documentation tools

For the hosted GraphQL docs of the pokemon GraphQL API, see https://graphql-pokemon.now.sh/, it opens GraphiQL where you can explore the API, use CTRL + space to show field suggestions, and CMD + enter to expand all nested fields by default. You can also right-click on a field to explore its type etc.

GraphiQL for pokemon GraphQL API

More about GraphQL coming next week in the Code with Hugo newsletter so subscribe if you’re not already.

Build and deploy a Vue/React/Vanilla JS app with Parcel and GitHub pages

Full example of a repo deployed like this: https://github.com/HugoDF/js-graphql-client-example, and see https://codewithhugo.com/js-graphql-client-example/.

Or the repo with the demos: https://github.com/HugoDF/parcel-gh-pages-deploy and see https://codewithhugo.com/parcel-gh-pages-deploy/.

Setting up parcel 📦

npm install --``save-dev parcel

Say you have an index.html and client.js in your root: index.html:

<!DOCTYPE html>

<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="ie=edge" http-equiv="x-ua-compatible"/>
<title>Your site title</title>
<meta content="Your site meta description" name="description"/>
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"/>
</head>
<body>
<div id="app">
<script src="./client.js"></script>
</div></body>
</html>

client.js:

const $app = document.querySelector('#app');
$app.innerText = 'Hello world';

Granted you don’t need to bundle this, but let’s say we do.

With npm 5+ you can run: npx parcel index.html (on old npm, ./node_modules/.bin/parcel index.html).

Go to http://localhost:1234 or run the command with --open option (npx parcel index.html --open), you should see the following:

> Note the ./client.js path that is relative to where the index.html is.

Hello world Parcel

React and Vue single file components etc also work with simple .babelrc include and npm install of respectively babel-preset-react or babel-preset-vue.

We can put the script in package.json:

{
  "scripts": {
    "start": "parcel index.html"
  },
  "devDependencies": {
    "parcel": "^1.9.7"
  }
}

And run it with npm start which will do the same as we did with npx earlier.

Deploying for free 💸

npm install --save-dev gh-pages

In package.json: If you’re building a repo that is not USERNAME.github.io:

{
  "scripts": {
    "start": "parcel index.html",
    "predeploy": "rm -rf dist &amp;&amp; parcel build index.html --public-url YOUR_REPO_NAME",
    "deploy": "gh-pages -d dist"
  },
  "devDependencies": {
    "gh-pages": "^1.2.0",
    "parcel": "^1.9.7"
  }
}

For our example that means:

{
  "name": "parcel-gh-pages-deploy",
  "description": "Deploy a parcel app to GitHub pages",
  "scripts": {
    "start": "parcel index.html",
    "predeploy": "rm -rf dist &amp;&amp; parcel build index.html --public--url /parcel-gh-pages-deploy",
    "deploy": "gh-pages -d dist"
  },
  "devDependencies": {
    "gh-pages": "^1.2.0",
    "parcel": "^1.9.7"
  }
}

If you are building USERNAME.github.io, use the following "``predeploy``" instead:

"predeploy": "rm -rf dist &amp;&amp; parcel build index.html",

❤ GitHub Pages and Parcel.

For any questions let me know on Twitter @hugo__df.

You just read issue #10 of Code with Hugo: Production JavaScript & Node.js Guides. You can also browse the full archives of this newsletter.

Brought to you by Buttondown, the easiest way to start and grow your newsletter.