Skip to main content

Working with Hugo

Working with Hugo project couldn't be easier. With this guide, we'll take you through it step-by-step.

NOTE

If you want to follow along and haven't created your own Hugo project, you can check out our Hugo starter repo here. This repo contains all the information you need to build and run the example in this doc.

Connecting your project

To connect to your project, you need your API Endpoint and an API key.

If you haven't created a project yet, follow this guide to do so.

If you already have a project, your API Endpoint will be in the admin UI. In the home tab, look for the "Useful Snippets" section.

Copy the API Endpoint and save it somewhere secure.

To get an API Key, you'll have to create one. Read our docs on creating an API Key here.

Hugo has many ways to import data into its files and templates, but one of the simplest is with JSON. Add a JSON file to the data directory at the root of your project, and you'll be able to access that data in your templates with Hugo's variables.

The code to request data from a JSON file looks like this:

example.html
<h1>{{.Site.Data.YourJSONFileNameHere.SomeProperty}}</h1>

To fetch data from your project's GraphQL API, we'll write a JavaScript file that makes an API call to your project, then creates JSON files from the response.

Protecting your project information

To protect your endpoint and API key from accidental exposure, we'll use environment variables. To use environment variables, you'll have to install the dotenv node package.

Go to the terminal in your project directory and enter npm i dotenv. With this installed, you can add environment variables to the script we're about to make.

Now in the root of your project directory, create a file called .env.

Inside, you will define your API Key and project endpoint as variables like this:

.env
GRAPHQL_ENDPOINT=YourProjectEndpointHere
GRAPHQL_API_KEY=YourApiKeyHere

Now you're ready to import data from your project's GraphQL API into your Hugo project.

Creating the import script

Installing the required node modules

To add the GraphQL API to your Hugo build process, you need to run Hugo with NPM. If you don't already have it, check out this resource for installing and configuring NPM.

In the root directory of your Hugo project, run the npm init command to generate a package.json file.

Now create a JavaScript file in any directory of your project. You may name it what you want. For the purposes of this example, we'll create a buildscripts folder in the root directory, and add a file to it called graphqlapi.js

We are going to write a JSON file to the data directory of our Hugo project, so we'll next need to import Node's fs module.

graphqlapi.js
const fs = require('fs');

Next, we'll make a request to our project's API endpoint to get the data we want. This request can be any valid query you've defined in your project Schema. Read our Schema docs for more information.

If you don't want to expose your endpoint to the public, you can hide it in an environment variable.

If you want to use environment variables, install dotenv and add the code to configure it to the top of your script as shown below:

graphqlapi
require('dotenv').config();
const fs = require('fs');

You'll also need the node-fetch package to make fetch requests from your node script. Enter npm i node-fetch in your console. Then require it at the top of your script.

graphqlapi
require('dotenv').config();
const fs = require('fs');
const fetch = require('node-fetch');

Making requests to the GraphQL API

You will need to make an asynchronous function for your code to run the fetch request to your project's API endpoint. Within this function, add a try/catch block to wrap your async code. Then use a string template to construct your API endpoint, using your project's ID as an environment variable. An example is below.

Since we want this function to run immediately and only once, we're going to use an IIFE.

graphqlapi
require('dotenv').config();
const fs = require('fs');
const fetch = require('node-fetch');

(async () => {
try {
const graphQLEndpoint = process.env.GRAPHQL_ENDPOINT;

} catch (err) {
console.log("GraphQL API pre build script failed.", err);
}
})()

Next, we'll make a POST request to your API endpoint, and store the result. In the headers, set your Authorization to be a string template with the word Bearer, a space, and then your API key. In the body, you need to have a property of query in order for your project's GraphQL API to process your request. In the query property, pass a string that contains your query.

For this example, we'll assume you're accessing an ecommerce database.

graphqlapi
const result = await fetch(process.env.GRAPHQL_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.GRAPHQL_API_KEY}`,
accept: "application/json",
},
body: JSON.stringify({
query: `query{
getProductList{
items{
name
price
}
}
}`,
}),
}
);

Almost done. Convert the response from your request to JSON, then stringify the JSON.

graphqlapi
const resultJSON = await result.json();
const jsonString = JSON.stringify(resultJSON);

Finally, write this jsonString to a file in the data folder of your Hugo project. If anything fails here, you should throw an error so your overall try/catch block can catch it.

graphqlapi
fs.writeFile("./data/graphqlapion", jsonString, (err) => {
if (!err) {
return console.log("Pre build script succeeded!");
}

throw err;
});

If this process fails anywhere, the whole build will fail. This is because, as we mentioned earlier, you're going to integrate the data retreived from this request into your Hugo templates. If that data is not available at build time, the build will fail. This is why it's important to catch your possible errors here and either console.log() them or otherwise note them where they can be found.

Here is the entire script:

graphqlapi
require('dotenv').config();
const fs = require("fs");
const fetch = require("node-fetch");

(async () => {
try {

const result = await fetch(process.env.GRAPHQL_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.GRAPHQL_API_KEY}`,
accept: "application/json",
},
body: JSON.stringify({
query: `query{
getProductList{
items{
name
price
}
}
}`,
}),
});
const resultJSON = await result.json();
const jsonString = JSON.stringify(resultJSON);

fs.writeFile("./data/graphqlapion", jsonString, (err) => {
if (!err) {
return console.log("Pre build script succeeded!");
}

throw err;
});
} catch (err) {
console.log("Pre build script failed!", err);
}
})();

Accessing your project data from Hugo templates

You can define your hugo templates using .html files in the layouts directory of your project. Create or open the file you want to edit. If you're using the Hugo starter project, open home.html.

For a detailed breakdown of Hugo's templating language, please visit the Hugo docs.

To access the data received in the example code above, you would need a line of code similar to this:

homepage.html
{{$Products := index .Site.Data.graphqlapi.data.getProductList.items}}

The above code creates a $Products array that contains all the items returned by the getProductList query. To access the data in the array and actually display it on our page, we'll need to use Hugo's range function:

homepage.html
<ul>
{{ range $product := $Products }}
<li>
{{ $product.name }}: {{ $product.price }}
</li>
{{ end }}
</ul>

Now let's put it all together in a Product List template. Here's what the full file should look like:

homepage.html
{{$Products := index .Site.Data.graphqlapi.data.getProductList.items}}

<h1>Products List:</h1>
<ul>
{{ range $product := $Products }}
<li>
{{ $product.name }}: {{ $product.price }}
</li>
{{ end }}
</ul>

The final page should look like this:

Adding your script to your build process

To add the GraphQL API to your build process, you need to run Hugo with NPM. If you don't already have it, check out this resource for installing and configuring NPM.

In the root directory of your Hugo project, run the npm init command to generate your package.json file. Then open up the file in your favorite editor. We're going to edit the scripts section. Your package.json file may look like this:

package.json
{
"name": "hugo-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"preinstall": "npx npm-force-resolutions",
"start": "hugo server",
"build": "hugo -D",
"deploy": "hugo --minify"
},
"author": "",
"license": "ISC"
}

To start your Hugo server, configure the "start" property of the "scripts" object to be a string containing whatever console commands you need to run your dev server. To keep it simple, you could set that value to "hugo server", which is the default command.

Building your site is similar, except you'll edit the "build" property and set it to whatever commands you need to run to build. For simplicity, we'll use "hugo -D", the default command; but you should check the Hugo docs for more information on the different commands that are available to you.

The command you'll enter into the terminal to run your server is npm run start. To build your site, npm run build. This is equivalent to hugo server and hugo -D, except that NPM manages it, which allows us to specify pre- and post-scripts that need to be run.

In your package.json, in the scripts object, add two properties:

package.json
    "prestart": "node ./buildscripts/graphqlapi",
"prebuild": "node ./buildscripts/graphqlapi",

The "prestart" and "prebuild" properties allow you to specify commands that must be executed immediately whenever npm run start or npm run build are called. These commands are executed prior to the commands defined in "start" and "build" respectively.

For this example, the commands we wish to execute before running our main "build" and "start" scripts is node, which should quickly execute a script we've created to pull information from your project's GraphQL API. You can name the script anything, and place it in any directory. Learn more about creating your script here.

Your Hugo build is ready

If you haven't already, be sure to read our docs on connecting services to your project. You'll need a service to unlock the full power to integrate multiple external APIs into one elegant GraphQL API. After that, check out our docs on customizing your schema, so you can craft your own queries and mutations.

Still need help? Get in touch with us.