RedwoodJS with Hasura = ❤️

redwoodjs_hasura

RedwoodJS is the new sensation in full-stack application development. It is highly opinionated with a choice of technologies. This blog is a step by step tutorial on how to use Hasura with Redwood JS and make it double awesome.

Before we move forward, let’s have a small introduction about our two participants Redwood and Hasura.

Redwood JS:

  • Redwood is an opinionated, full-stack, serverless web application framework that will allow you to build and deploy JAMstack applications with ease.

Redwood JS uses a lot of the latest bleeding technology and abstracts all the complexity away from the developers. It uses React with Apollo for the frontend and has a lot of code generators like pages, cells, etc which makes development very easy. It also takes care of configuring tests, storybooks, etc out of the box. It uses Prisma for its GraphQL backend and enables you to create quick backends with DashBoard/GUI support. Sounds fascinating? Not just that, it supports out of the box deployment with Netlify, AWS, etc.

In one line, the developer has to write only the styles and business logic. Everything else has been taken care of.

Hasura:

  • Instant GraphQL with built-in authorization for your data

Hasura is a futuristic tool that lets you built GraphQL backends instantly from your existing database. It makes creating GraphQL backends as simple as a walk in the park.

The Problem

My equal love for both the tools created a desire for using them together. But Redwood JS was highly opinionated and didn't have any provisions to do it out of the box. This made me explore their codebase and figure out simple steps to start building applications with RedwoodJS + Hasura.

Why Hasura?

If we just focus on frontend parts of Redwood, it gives you literally everything you need, out of the box. It lets you create pages and control the routes manually as well (in case of customization), it lets you create cells along with stories and tests pre-configured. They will support React Native and Electron in the future. So, it's kind of an ideal frontend with all complexity abstracted (similar to Next.js, but more GraphQL focused).

Any frontend engineer who uses redwood would start loving it. But the constraint is, it's highly opinionated and you are obliged to use default integrations and this can be a lot of work. Hasura, on the other hand, is truly a one-stop GraphQL solution with redwood. It can be directly consumed by the Web side (what redwood calls its frontend) and also it supports auth/webhooks etc. Especially when the DB is Postgres, Hasura is pure black magic. Also with Prisma it's again the developer's responsibility to look after scaling/performance etc while Hasura does it out of the box.

Step 1: Build a Hasura GraphQL backend

Refer to this detailed tutorial by Hasura to build a GraphQL backend here. I am using one of their demo apps for this tutorial - https://learn-graphql.demo.hasura.app/console.

This backend gives me a set of queries on Super Heros data. We will use the following query to fetch the required data:

    query getSuperHeros {
      superhero_aggregate {
        nodes {
          name
          team {
            name
          }
        }
      }
    }

Step 2: Bootstrap a Redwood JS app

To bootstrap a Redwood JS app, run the following command.

yarn create redwood-app my-redwood-app

This will create a full-stack boilerplate with two subfolders web and api. Since we are going to use Hasura for the backend, we will not require this api folder. Hence, this folder can be deleted.

Step 3: Create a Page

A Page is a screen/route of the app. Each Page gets loaded in a particular route. To create a Page, run the below command:

yarn redwood generate page SuperHero /

This will create a new Page called SuperHero inside the pages folder and will load the page in the route / ie: root route. You can replace / with /custom-route to load it in the desired path.

The page looks like this:

import { Link, routes } from '@redwoodjs/router'

const SuperHeroPage = () => {
  return (
    <>
      <h1>SuperHeroPage</h1>
      <p>
        Find me in 
        <code>./web/src/pages/SuperHeroPage/SuperHeroPage.js</code>
      </p>
      <p>
        My default route is named <code>superHero</code>, link to me with `
        <Link to={routes.superHero()}>SuperHero</Link>`
      </p>
    </>
  )
}

export default SuperHeroPage

Step 4: Setup Hasura in RedWoodJS

In the generated Redwood JS files, the src/index.js file has the following code:

ReactDOM.render(
  <FatalErrorBoundary page={FatalErrorPage}>
    <RedwoodProvider>
      <Routes />
    </RedwoodProvider>
  </FatalErrorBoundary>,
  document.getElementById('redwood-app')
)

Here, the entire App is wrapped inside RedWoodProvider which is from the redwoodjs library. Reading through the source code, this provider uses ApolloProvider underneath.

/**
 * Create a GraphQL Client (Apollo) that points to the `apiProxyPath` that's
 * specified in `redwood.toml`.
 */
export const createGraphQLClient = (config: GraphQLClientConfig) => {
  return new ApolloClient({
    uri: `${window.__REDWOOD__API_PROXY_PATH}/graphql`,
    cache: new InMemoryCache(),
    ...config,
  })
}

/**
 * A GraphQL provider that instantiates a client automatically.
 */
export const GraphQLProvider: React.FC<GraphQLProviderProps> = ({
  config = {},
  ...rest
}) => {
  return <ApolloProvider client={createGraphQLClient(config)} {...rest} />
}

and the provider gets the config as props in RedWoodProvider as below:

/* Modified a bit to shorten the code */
const RedwoodProvider: React.FC<RedwoodProviderProps> = ({
  useAuth = window.__REDWOOD__USE_AUTH,
  graphQLClientConfig,
  children,
  ...rest
}) => {
    return (
      <GraphQLProvider config={graphQLClientConfig} {...rest}>
        <FlashProvider>{children}</FlashProvider>
      </GraphQLProvider>
    )
}

Considering the above piece of source code, we can override the existing set up by passing the config in props of RedWoodProvider. The src/index.js is modified accordingly:

ReactDOM.render(
  <FatalErrorBoundary page={FatalErrorPage}>
    <RedwoodProvider graphQLClientConfig={{
      // URL of your Hasura backend
      uri: 'https://learn-graphql.demo.hasura.app/v1/graphql' 
    }}>
      <Routes />
    </RedwoodProvider>
  </FatalErrorBoundary>,
  document.getElementById('redwood-app')
)

Note: You can pass any config supported by Apollo Client through the graphQLClientConfig props.

Step 5: Generate a Cell

Cells are a declarative approach to data fetching and one of Redwood's signature modes of abstraction.

Cells are auto-generated files, that have scaffolding to fire a GraphQL query and display loading/error states or data based on the response of the query. It reduces the efforts of retyping the boilerplate code. You can create a cell using the following command:

yarn rw g cell SuperHeros

This command will generate a Cell inside the src/components folder along with scaffolded tests and storybooks. The file looks like this:

export const QUERY = gql`
  query SuperHerosQuery {
    superHeros {
      id
    }
  }
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ superHeros }) => {
  return JSON.stringify(superHeros)
}

Here we will also have to change the query and the components based on our UI/UX requirements. Let’s modify the above file to consume our query:

export const QUERY = gql`
  query getSuperHeros {
    superhero_aggregate {
      nodes {
        name
        team {
          name
        }
      }
    }
  }
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ superhero_aggregate }) => {
  return (
    <div>
      {superhero_aggregate.nodes.map((hero) => (
        <li>{hero.name} - {hero.team.name}</li>
      ))}
    </div>
  )
}

Now lets import and use it in our pages/SuperHeroPage/SuperHeroPage.js file as below:

import SuperHerosCell from 'src/components/SuperHerosCell';
const SuperHeroPage = () => {
  return (
    <>
      <h1>SuperHeroPage</h1>
      <SuperHerosCell />
    </>
  )
}
export default SuperHeroPage

Execute the command yarn rw dev to run the application. The app opens in http://localhost:8910/ by default. The output looks like this:

Tadaa! We have successfully created an app with RedwoodJS and Hasura.

Bonus:

RedWood JS is actively building a lot of tools and to take advantage of all the tools and support, we will also have to modify graphql.config.js file.

Replace the contents of the file with the below code and you are ready to enjoy all the benefits RedwoodJS offers along with Hasura's superpowers!

module.exports = {
  // URL of your Hasura backend
  schema: `https://learn-graphql.demo.hasura.app/v1/graphql`, 
}

A sample implementation of Hasura + Redwood can be found here. Happy coding


About the author
This blog post was written by Vilva Athiban P B, a JavaScript developer working for Omio as part of the Hasura Technical Writer Program.

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