Skip to main content

Introduction

The Hasura data provider enables you to build Refine applications with Hasura, an open-source engine that provides instant GraphQL APIs on your databases. It extends the base GraphQL data provider with Hasura-specific features.

Installation

Install the Hasura data provider package:
npm install @refinedev/hasura graphql-tag

Setup

1

Create Hasura project

First, create a Hasura project at hasura.io or deploy your own Hasura instance.
2

Create GraphQL client

Create a GraphQL client with your Hasura endpoint:
import { GraphQLClient } from "@refinedev/hasura";

const client = new GraphQLClient("https://your-hasura.hasura.app/v1/graphql", {
  headers: {
    "x-hasura-role": "public",
  },
});
3

Configure data provider

Configure the Refine app with the Hasura data provider:
import { Refine } from "@refinedev/core";
import dataProvider, { GraphQLClient } from "@refinedev/hasura";

const client = new GraphQLClient("https://your-hasura.hasura.app/v1/graphql", {
  headers: {
    "x-hasura-role": "public",
  },
});

const App = () => {
  return (
    <Refine
      dataProvider={dataProvider(client)}
    >
      {/* Your app content */}
    </Refine>
  );
};

Basic Usage

Get List

Fetch a list of records using GraphQL queries:
import { useList } from "@refinedev/core";
import gql from "graphql-tag";

const POSTS_QUERY = gql`
  query GetPosts(
    $where: posts_bool_exp
    $order_by: [posts_order_by!]
    $limit: Int
    $offset: Int
  ) {
    posts(where: $where, order_by: $order_by, limit: $limit, offset: $offset) {
      id
      title
      content
      created_at
    }
    posts_aggregate(where: $where) {
      aggregate {
        count
      }
    }
  }
`;

const { data } = useList({
  resource: "posts",
  meta: {
    gqlQuery: POSTS_QUERY,
  },
});

Get One

Fetch a single record by ID:
import { useOne } from "@refinedev/core";
import gql from "graphql-tag";

const POST_QUERY = gql`
  query GetPost($id: uuid!) {
    posts_by_pk(id: $id) {
      id
      title
      content
      created_at
      author {
        id
        name
      }
    }
  }
`;

const { data } = useOne({
  resource: "posts",
  id: "uuid-here",
  meta: {
    gqlQuery: POST_QUERY,
  },
});

Create

Create a new record using mutations:
import { useCreate } from "@refinedev/core";
import gql from "graphql-tag";

const CREATE_POST_MUTATION = gql`
  mutation CreatePost($object: posts_insert_input!) {
    insert_posts_one(object: $object) {
      id
      title
      content
    }
  }
`;

const { mutate } = useCreate();

const handleSubmit = (values) => {
  mutate({
    resource: "posts",
    values,
    meta: {
      gqlMutation: CREATE_POST_MUTATION,
    },
  });
};

Update

Update an existing record:
import { useUpdate } from "@refinedev/core";
import gql from "graphql-tag";

const UPDATE_POST_MUTATION = gql`
  mutation UpdatePost($id: uuid!, $set: posts_set_input!) {
    update_posts_by_pk(pk_columns: { id: $id }, _set: $set) {
      id
      title
      content
    }
  }
`;

const { mutate } = useUpdate();

const handleUpdate = (id, values) => {
  mutate({
    resource: "posts",
    id,
    values,
    meta: {
      gqlMutation: UPDATE_POST_MUTATION,
    },
  });
};

Delete

Delete a record:
import { useDelete } from "@refinedev/core";
import gql from "graphql-tag";

const DELETE_POST_MUTATION = gql`
  mutation DeletePost($id: uuid!) {
    delete_posts_by_pk(id: $id) {
      id
    }
  }
`;

const { mutate } = useDelete();

const handleDelete = (id) => {
  mutate({
    resource: "posts",
    id,
    meta: {
      gqlMutation: DELETE_POST_MUTATION,
    },
  });
};

Authentication

Configure authentication with Hasura using JWT or admin secret:

Using JWT

import { GraphQLClient } from "@refinedev/hasura";

const client = new GraphQLClient(
  "https://your-hasura.hasura.app/v1/graphql",
  {
    fetch: (url, options) => {
      const token = localStorage.getItem("token");
      return fetch(url, {
        ...options,
        headers: {
          ...options?.headers,
          Authorization: token ? `Bearer ${token}` : "",
        },
      });
    },
  }
);

Using Admin Secret

import { GraphQLClient } from "@refinedev/hasura";

const client = new GraphQLClient(
  "https://your-hasura.hasura.app/v1/graphql",
  {
    headers: {
      "x-hasura-admin-secret": "your-admin-secret",
    },
  }
);

Real-time Subscriptions

Hasura supports real-time GraphQL subscriptions:
1

Install WebSocket dependencies

npm install graphql-ws
2

Create WebSocket client

import { createClient } from "graphql-ws";

const wsClient = createClient({
  url: "wss://your-hasura.hasura.app/v1/graphql",
  connectionParams: () => {
    const token = localStorage.getItem("token");
    return {
      headers: {
        Authorization: token ? `Bearer ${token}` : "",
      },
    };
  },
});
3

Configure live provider

import { Refine } from "@refinedev/core";
import dataProvider, {
  GraphQLClient,
  liveProvider,
} from "@refinedev/hasura";
import { createClient } from "graphql-ws";

const client = new GraphQLClient("https://your-hasura.hasura.app/v1/graphql");
const wsClient = createClient({
  url: "wss://your-hasura.hasura.app/v1/graphql",
});

const App = () => {
  return (
    <Refine
      dataProvider={dataProvider(client)}
      liveProvider={liveProvider(wsClient)}
      options={{ liveMode: "auto" }}
    >
      {/* Your app content */}
    </Refine>
  );
};
4

Use subscriptions

import { useList } from "@refinedev/core";
import gql from "graphql-tag";

const POSTS_SUBSCRIPTION = gql`
  subscription OnPostsUpdate {
    posts {
      id
      title
      updated_at
    }
  }
`;

const PostList = () => {
  const { data } = useList({
    resource: "posts",
    meta: {
      gqlQuery: POSTS_QUERY,
      gqlSubscription: POSTS_SUBSCRIPTION,
    },
  });

  // Data automatically updates in real-time
  return <div>{/* Render posts */}</div>;
};

Advanced Queries

Filtering with Hasura Operators

import { useList } from "@refinedev/core";
import gql from "graphql-tag";

const POSTS_QUERY = gql`
  query GetPosts($where: posts_bool_exp) {
    posts(where: $where) {
      id
      title
      views
    }
  }
`;

const { data } = useList({
  resource: "posts",
  filters: [
    {
      field: "title",
      operator: "contains",
      value: "tutorial",
    },
    {
      field: "views",
      operator: "gte",
      value: 100,
    },
  ],
  meta: {
    gqlQuery: POSTS_QUERY,
  },
});

Relationships

Query related data using Hasura relationships:
import { useList } from "@refinedev/core";
import gql from "graphql-tag";

const POSTS_WITH_AUTHOR_QUERY = gql`
  query GetPosts {
    posts {
      id
      title
      content
      author {
        id
        name
        email
      }
      categories {
        id
        name
      }
    }
  }
`;

const { data } = useList({
  resource: "posts",
  meta: {
    gqlQuery: POSTS_WITH_AUTHOR_QUERY,
  },
});

Aggregations

Use Hasura’s aggregation features:
import gql from "graphql-tag";
import { useCustom } from "@refinedev/core";

const POSTS_STATS_QUERY = gql`
  query GetPostsStats {
    posts_aggregate {
      aggregate {
        count
        avg {
          views
        }
        max {
          views
        }
      }
    }
  }
`;

const { data } = useCustom({
  url: "",
  method: "get",
  meta: {
    gqlQuery: POSTS_STATS_QUERY,
  },
});

Complete Example

import { Refine } from "@refinedev/core";
import dataProvider, {
  GraphQLClient,
  liveProvider,
} from "@refinedev/hasura";
import { createClient } from "graphql-ws";
import routerProvider from "@refinedev/react-router";
import { BrowserRouter } from "react-router";

const API_URL = "https://your-hasura.hasura.app/v1/graphql";
const WS_URL = "wss://your-hasura.hasura.app/v1/graphql";

const client = new GraphQLClient(API_URL, {
  fetch: (url, options) => {
    const token = localStorage.getItem("token");
    return fetch(url, {
      ...options,
      headers: {
        ...options?.headers,
        Authorization: token ? `Bearer ${token}` : "",
      },
    });
  },
});

const wsClient = createClient({
  url: WS_URL,
  connectionParams: () => {
    const token = localStorage.getItem("token");
    return {
      headers: {
        Authorization: token ? `Bearer ${token}` : "",
      },
    };
  },
});

const App = () => {
  return (
    <BrowserRouter>
      <Refine
        dataProvider={dataProvider(client)}
        liveProvider={liveProvider(wsClient)}
        routerProvider={routerProvider}
        options={{ liveMode: "auto" }}
        resources={[
          {
            name: "posts",
            list: "/posts",
            create: "/posts/create",
            edit: "/posts/edit/:id",
            show: "/posts/show/:id",
          },
        ]}
      >
        {/* Your routes and pages */}
      </Refine>
    </BrowserRouter>
  );
};

export default App;

Hasura-Specific Features

Permission System

Hasura’s permission system is automatically respected by the data provider. Configure permissions in your Hasura console.

Custom Functions

Call Hasura custom functions:
import gql from "graphql-tag";
import { useCustom } from "@refinedev/core";

const SEARCH_POSTS = gql`
  query SearchPosts($search: String!) {
    search_posts(args: { search: $search }) {
      id
      title
      content
    }
  }
`;

const { data } = useCustom({
  url: "",
  method: "get",
  meta: {
    gqlQuery: SEARCH_POSTS,
    variables: {
      search: "tutorial",
    },
  },
});

Next Steps

Data Providers Overview

Learn about other data providers

Hasura Documentation

Explore Hasura features

Build docs developers (and LLMs) love