Hasura Actions using Netlify Functions for Custom Logic

In this guide, we will show how to write your own Hasura actions using Netlify functions (with your own custom business logic or a third-party API integration). But first, definitions.

What are Hasura actions?

Hasura provides support for integrating our own business logic or third-party APIs (usually REST APIs) with the help of “Hasura Actions”. Learn more about Hasura Actions.

What are Netlify functions?

Netlify functions are AWS’s serverless lambda functions that allow you to define your business logic and functions as an API and expose them with an endpoint. Click here to learn more about netlify functions.

How to write your own Hasura Actions using Netlify functions?

This guide will go through the process in three parts, feel free to skip to the part that is more interesting for you.

  • Part I: Create a Function in an app
  • Part II: Define the functions with logic
  • Part III: Add the function to Hasura using Actions

Note: Hasura Actions can also be used to add REST endpoints to Hasura’s GraphQL schema.

Part I: Create a Function in an app

To create a function in the app, install the netlify-cli package from npm (preferably as global).

npm install -g netlify-cli

Once the netlify-cli is installed, go to the project’s root folder, and link your Netlify site to the folder. Click here to learn more about linking sites in Netlify.

netlify link

Once linked, type the following command to create a function.

netlify functions:create

You will see a list of predefined templates. Choose the “hello-world” template.

The function will be created in <project-root>/netlify/functions/hello-world/hello-world.js

Part II: Define the functions with logic

Here is an example of a Netlify function for Hasura's Actions, which checks if an email is present in the DB without exposing all the users to the client.

const axios = require("axios");

const hgeEndpoint = "https://example.hasura.app/v1/graphql";
const adminSecret = "adminSecret";

const handler = async (event) => {
  let request;
  try {
    request = JSON.parse(event.body);
  } catch (error) {
    // Make sure you add code and message to errors. These will be shown in the hasura console errors.
    let response = {
      status: false,
      code: "api/parse-error",
      message: "error",
      error: { message: "cannot parse input" },
    };
    return { statusCode: 400, body: JSON.stringify(response) };
  }

  if (!request.input.email) {
    // Make sure you add code and message to errors. These will be shown in the hasura console errors.
    let response = {
      status: false,
      message: "Please send email",
      code: "input/undefined",
      error: { message: "Please send email" },
    };
    return { statusCode: 400, body: JSON.stringify(response) };
  }

  let query = {
    query: `
      query check_user($email: String) {
        users(where: {email: {_eq: $email}}) {
          id
          email_id
        }
      }
    `,
    variables: { email: request.input.email },
  };

  try {
    let result = await axios.post(hgeEndpoint, query, {
      headers: { "x-hasura-admin-secret": adminSecret },
    });
    if (result.data.users.length > 0) {
      let response = {
        status: true,
        message: "success",
        user: data.data.users,
      };
      return { statusCode: 200, body: JSON.stringify(response) };
    } else {
      // Make sure you add code and message to errors. These will be shown in the hasura console errors.
      let response = {
        status: false,
        code: "user/not-found",
        message: "No user found",
        error: { message: "No user found" },
      };
      return { statusCode: 400, body: JSON.stringify(response) };
    }
  } catch (error) {
    let response = {
      status: false,
      code: "api/fetch-error",
      message: error.message,
      error: { message: error.message, detail: error.toString() },
    };
    return { statusCode: 500, body: JSON.stringify(response) };
  }
};

module.exports = { handler };

NOTE: Add the code and message to the response body.

These are displayed in Hasura’s GraphQL errors (whenever the API is returning an error in Hasura, they follow the GraphQL error standard).

The error in Hasura is displayed like this:

Error state in Hasura GraphQL
Error state in Hasura GraphQL

Part III: Add the function to Hasura using Actions

Once the custom business logic is created as an API using Netlify functions, add it to Hasura using Actions as follows:

Creating an Action
Creating an Action

Note: You can also modify the permission of the actions if you’re using role-based permissions.

Try out the Actions using Hasura’s GraphQL API Explorer

The success state on Hasura’s API Explorer from the Netlify functions will look like this:

Success state of an action
Success state of an action

The error state on Hasura’s API Explorer from the Netlify functions will look like this:

There are two error states

  • When the user is not found with the requested email, you’ll see the following (User not found in DB error)
  • When the email is not sent along with the request, you’ll see the following (Validation Error)
Input not given / undefined error
Input not given / undefined error

About the Author

Matheswaaran S is a  full stack developer & Tech Enthusiast. You can learn more about what he does on his website, and connect with him on Twitter or Linkedin.

Blog
16 Nov, 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.