Hasura Metadata SDK

Our #GraphQLJanuary continues with blog posts, live streams, Discord Q&A, office hours, and more. For a schedule of upcoming events, join the community or register at https://hasura.io/graphql/graphql-january/.

What is it?

The Hasura Metadata SDK is a collection of type definitions for Hasura metadata in various programming languages. Users can also generate definitions in other languages if their preferred language is not readily available.

These pre-generated SDK's included:

  • TypeScript (also published on NPM, as @hasura/metadata-types)
  • Python 3.7 Dataclasses + MyPy
  • Golang
  • Haskell

Why was it built, what problems does it solve?

Hasura's workflow is flexible. Some users prefer to interact with & develop their project through our web interface. Other users prefer to stick to purely text-based flows, or integrate with the existing editor tooling they have configured.

We saw two areas where we felt we could better serve the needs of the community and enhance our user experience by augmenting the tooling around metadata:

Providing our users with a platform to build more complex tooling, and programmatic workflows, around interacting with metadata

Providing our users with enhanced integration to maintain flow-state while working in their editor environments on their Hasura projects

To the first point, we felt that publishing fully type-safe and documented SDK's would allow users to more confidently build the tooling they needed, while feeling comfortable they weren't making mistakes.

Type-safe and documented SDK's

To the second point, we realized that users in most modern editors would be able to take advantage of JSON/YAML Schema integrations, so that when working on, or referencing metadata files, there is intellisense, type-checking, and doc-comments on hover.

JSON/YAML schema integrations

How might I use this?

Below is an example to demonstrate the common use cases you may encounter when wanting to script your interactions with metadata. It includes:

  • Loading tables.yaml, and actions.yaml files
  • Adding a new table
  • Creating a JSON and text diff of tables.yaml, and writing it to a diffs folder
  • Repeating the above process for metadata.json (could be metadata.yaml as well)
import { Convert } from "./customMetadataConverter"
import {
  TableEntry,
  Action,
  CustomTypes,
  HasuraMetadataV2,
} from "../generated/HasuraMetadataV2"

// Read "tables.yaml" file as text from filesystem
const tablesMetadataFile = fs.readFileSync("./metadata/tables.yaml", "utf8")
// Convert it to JSON object with type annotation using loadYAML utility
const tablesMetadata: TableEntry[] = Convert.loadYAML(tablesMetadataFile)
tablesMetadata.forEach(console.log)

// Read "actions.yaml" file as text from filesystem
const actionMetadataFile = fs.readFileSync("./metadata/actions.yaml", "utf8")
// Convert it to JSON object with type annotation using loadYAML utility
const actionMetadata: {
  actions: Action[]
  custom_types: CustomTypes
} = Convert.loadYAML(actionMetadataFile)
actionMetadata.actions.forEach(console.log)
console.log(actionMetadata.custom_types)

// Make a new table object
const newTable: TableEntry = {
  table: { schema: "public", name: "user" },
  select_permissions: [
    {
      role: "user",
      permission: {
        limit: 100,
        allow_aggregations: false,
        columns: ["id", "name", "etc"],
        computed_fields: ["my_computed_field"],
        filter: {
          id: { _eq: "X-Hasura-User-ID" },
        },
      },
    },
  ],
}

// Clone the tables for comparison after changes using diff()
const originalTablesMetadata = Convert.clone(tablesMetadata)
// Add the new table to tables metadata
tablesMetadata.push(newTable)

// Generate a structural and text diff from the changes between original and now
const tableDiff = Convert.diff(originalTablesMetadata, tablesMetadata)
// Write the diffs to /diffs folder, will output "tables.json" and "tables.diff"
Convert.writeDiff({ folder: "diffs", file: "tables", diffs: tableDiff })
// Ouput the updated "tables.yaml" to filesystem
fs.writeFileSync(
  "./tables-updated.yaml",
  Convert.metadataToYAML(tablesMetadata)
)

// Read "metadata.json"
const metadataFile = fs.readFileSync("./metadata.json", "utf-8")
// Convert.to<typeName> does runtime validation of the type
const allMetadata: HasuraMetadataV2 = Convert.toHasuraMetadataV2(metadataFile)
console.log(allMetadata)

// Clone, add table
const beforeMetadataChanges = Convert.clone(allMetadata)
allMetadata.tables.push(newTable)

// Diff, write diff
const metadataDiff = Convert.diff(beforeMetadataChanges, allMetadata)
Convert.writeDiff({ folder: "diffs", file: "metadata", diffs: metadataDiff })

Closing Words

We hope that you enjoyed and learned something from this article, and in this SDK find a valuable tool for your own workflow.

We invite you to check out the Github repo for more information, play with it, and give us feed back on what works & what doesn't =)

Lastly, we hope you'll join and become a part of our online community.

Blog
21 Jan, 2021
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.