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? 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.

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
  2. Note the values of the two environment variables: HASURA_API_URL and HASURA_ADMIN_SECRET.

    Copy to clipboard
    HASURA_API_URL = 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: {
            'X-Hasura-Admin-Secret': process.env.HASURA_ADMIN_SECRET,
    .then((response)=> response.json());

    //To do: check for errors and show errors

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 {


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(; //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) => {
        ).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
  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");

  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

Partner news: Cloudinary-Getty Images Integration

Supported by intelligent automation, Cloudinary serves as an effective conduit between media asset management and delivery so you can take maximum advantage of assets, compress workflows, and build and coordinate engaging and inspiring customer experiences. Through Cloudinary’s Digital Asset Management (DAM) solution, which employs the company’s innovative image and video APIs, creative and marketing teams can benefit from them, as well as from many AI-powered and automated capabilities. As a result, you can transform, optimize, and deliver media at scale on an intuitive UI.

Read more
Why Audio in Video Matters

Many content creators and consumers tend to regard video as visuals, but that’s only part of the experience. Immersive video content includes strong audio. Just like in a movie, the audio for video content comprises many components: the narrator or subjects, the background music that sets the mood and draws viewers in, sound effects, and so forth.

Read more

For Developers: the HTML <picture> Element Explained

By Amarachi Amaechi
For Developers: the HTML <picture> Element Explained

We all know the good ol', tireless <img> element, which has been a long-time go-to for inserting graphics into webpages. Time doesn’t stop, however, and neither do technological advancements. So, let’s get you up to speed with the element’s modern alternative: the <picture> element.

Read more