Cloudinary Blog

Dynamically Fetch Data in Jamstack Sites With Hasura

By Obinna Ekwuno
How to Dynamically Fetch Data in Jamstack Sites With Hasura

The Jamstack web-development architecture yields many benefits, including high performance and security. Plus, thanks to its APIs, which function as software as a service (SaaS), no back-end infrastructure is required for web applications.

The rise of platforms as a service (a type of SAAS) has rewarded the front end with a lot of autonomy. Even though Jamstack front ends are mostly associated with static site generators like Next.js, Gatsby, and Nuxt.js, I strongly believe that any application that contains a self-sustainable front end and that leverages APIs for specific purposes qualifies as a Jamstack front end. So, it can be a simple project coded with HTML, CSS, and logic.

Many front-end developers relish building stand-alone applications but must answer several questions at the outset, such as where to get the related data and how to authenticate it. Above all, if a PostgreSQL database already exists, how to connect it to the new Jamstack front end? Hasura.io is a silver bullet of a solution.

Understanding Hasura

As defined on GitHub, Hasura is a superfast GraphQL server that “gives you instant, real-time GraphQL APIs over Postgres, with webhook triggers on database events and remote schemas for business logic.” You want to give the front end all the autonomy it needs, e.g., where to source data and how to structure and handle that data.

Note
Data in Jamstack applications can usually originate from any source, e.g., one or multiple content management systems, assuming that you’re using a framework like Gatsby or connecting to an existing database like Postgres with Hasura.

Below is a demo that shows you how to use Hasura to connect and fetch data to an application built with HTML.

Setting Up Hasura

  1. Create a Hasura account and sign in.
  2. Click Try a free database with Heroku.

Sign up for Hasura

Next, click the DATA tab at the top and then click Create Table.

GraphQL Schema

Assign types to your parameters, that is, define the data you want your queries to return on the front end and the data types.

GraphQL data

Connecting Hasura to Applications With Utility Functions

Now connect the database to the sample application with the following:

  • Two environment variables, which link your Hasura account and database with your application so that you can query for data from there.
  • A utility function, which enables interactivity in the application.
  • A serverless function. For an explanation of this function type, see the post Serverless Functions Deliver Dynamism to Static Sites.

Do the following:

  1. Log in to Hasura cloud and go to https://cloud.hasura.io/projects.
  2. Note the values of the two environment variables: HASURA_API_URL and HASURA_ADMIN_SECRET.

    Copy to clipboard
    HASURA_API_URL = xxxxxx
    HASURA_ADMIN_SECRET = xxxxxx
  3. Create a functions directory in the root of the application with a folder called utils.js, in which in turn create a file called hasura.js that contains the code below. Be sure to replace HASSURA_API_URL and HASURA_ADMIN_SECRET with their values.

Copy to clipboard
const fetch = require('node-fetch')
async function query({query,variables = {}}) {
    const result  =await fetch(process.env.HASURA_API_URL,{
        method: 'POST',
        headers: {
            'Content-type':'application/json',
            'X-Hasura-Admin-Secret': process.env.HASURA_ADMIN_SECRET,
        },
        body:JSON.stringify({query,variables}),
    })
    .then((response)=> response.json());

    //To do: check for errors and show errors
return result.data

}
exports.query = query

The above code posts queries to your Hasura workspace with the fetch API and then exports a query function with which you can extract data from your database.

Querying for Data in Components

Finally, connect a query to the application with a Netlify serverless function. In the functions directory, create a file called add-project.js with the code below, which imports the query function that enables you to write the GraphQL query generated in Hasura.

Copy to clipboard
const { query } = require("./util/hasura");
exports.handler = async (event) => {
    const result = await query({
        query: `
        query {
          projects {
              id
              project
              tagline
              title
                }
            }
        `
    })
    return{
        statusCode:200,
        body:JSON.stringify(result),
    };

};

Extracting Data in a Form With the Serverless Function

You can now use the serverless function to get data from a form. See the code below. Once the body is posted, it sends several parameters (id, title, etc.) to the serverless function for a response.

Copy to clipboard
async function handleSubmit(event) {
    event.preventDefault(); // stops the submit from firing before the function runs 
  const data = new FormData(event.target); //formdata API
  const result = await fetch('/.netlify/functions/add-project', {
    method :'POST',
      body: JSON.stringify({
        id: data.get('id'),
        title: data.get('title'),
        tagline: data.get('tagline'),
        project: data.get('project'),
      }),
  }).then((response) => {
      document.querySelector(
        '.message',
        ).innerText = `Response: ${response.status}`;
    });
}

Getting Uniform Images From the Form

For a consistent display, you’d want the images in the forms to have the same resolutions before being stored in your Cloudinary account’s Media Library in Cloudinary-generated URLs. Toward that end, leverage the Cloudinary capability called incoming transformations, which modifies images during or after their upload.

Do the following:

  1. From your Cloudinary account’s dashboard, grab your Cloudinary name, API key, and API secret and store them as environment variables in a file called .env (see the example below) in the project’s root directory.

    Copy to clipboard
    CLOUDINARY_NAME=my-project
    CLOUDINARY_API_KEY=xxxxxxxxxxxxxxxxxx
    CLOUDINARY_API_SECRET=xxxxxxxxxxxxxxxxxxxxx
  2. Create a utility function with the code below. Be sure to replace LOUDINARY_NAME, CLOUDINARY_API_KEY, and CLOUDINARY_API_SECRET with their values.

Copy to clipboard
const cloudinary = require("cloudinary").v2;
const dotenv = require("dotenv");
dotenv.config();

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET
});

// When doing a signed upload, you'll use a function like this one:
exports.handler = async event => {
  const { project } = JSON.parse(event.body);
  const res = await cloudinary.v2.uploader.upload(project, { width: 2000, height: 1000, crop: "limit" },
  function(error, result) { console.log(result, error); });
  return {
    statusCode: 200,
    body: JSON.stringify(res)
  };
};

This function adds a preset, which limits the dimensions of an uploaded image to a maximum width of 2,000 pixels and a maximum height of 1,000 pixels.

You can apply more transformations to the uploaded images or generate assets derived from the originals with other Cloudinary features, such as eager transformation.

Opening the Door to Dynamism

With Hasura, you can build numerous dynamic capabilities for Jamstack sites. Let your creativity soar!

Recent Blog Posts

Our $2B Valuation

By
Blackstone Growth Invests in Cloudinary

When we started our journey in 2012, we were looking to improve our lives as developers by making it easier for us to handle the arduous tasks of handling images and videos in our code. That initial line of developer code has evolved into a full suite of media experience solutions driven by a mission that gradually revealed itself over the course of the past 10 years: help companies unleash the full potential of their media to create the most engaging visual experiences.

Read more
Direct-to-Consumer E-Commerce Requires Compelling Visual Experiences

When brands like you adopt a direct–to-consumer (DTC) e-commerce approach with no involvement of retailers or marketplaces, you gain direct and timely insight into evolving shopping behaviors. Accordingly, you can accommodate shoppers’ preferences by continually adjusting your product offering and interspersing the shopping journey with moments of excitement and intrigue. Opportunities abound for you to cultivate engaging customer relationships.

Read more
Automatically Translating Videos for an International Audience

No matter your business focus—public service, B2B integration, recruitment—multimedia, in particular video, is remarkably effective in communicating with the audience. Before, making video accessible to diverse viewers involved tasks galore, such as eliciting the service of production studios to manually dub, transcribe, and add subtitles. Those operations were costly and slow, especially for globally destined content.

Read more
Cloudinary Helps Minted Manage Its Image-Generation Pipeline at Scale

Shoppers return time and again to Minted’s global online community of independent artists and designers because they know they can count on unique, statement-making products of the highest quality there. Concurrently, the visual imagery on Minted.com must do justice to the designs into which the creators have poured their hearts and souls. For Minted’s VP of Engineering David Lien, “Because we are a premium brand, we need to ensure that every single one of our product images matches the selected configuration exactly. For example, if you pick an 18x24 art print on blue canvas, we will show that exact combination on the hero images in the PDF.”

Read more
Highlights on ImageCon 2021 and a Preview of ImageCon 2022

New year, same trend! Visual media will continue to play a monumental role in driving online conversions. To keep up with visual-experience trends and best practices, Cloudinary holds an annual conference called ImageCon, a one-of-a-kind event that helps attendees create the most engaging visual experiences possible.

Read more