/apollo-hasura-remote-schema

Sample repo of using hasura's types in an apollo-server remote schema

Primary LanguageTypeScriptMIT LicenseMIT

Purpose

The point of this sample is to demonstrate a full example of automatically importing the schema types from Hasura into a remote schema, to allow usage of Hasura types in a remote schema.

Additionally, there is a utility for matching the remote schema's role permissions to the hasura schema role permissions.

This is accomplished using the graphql-codegen package. See "Points of Interest" below for specifics.

Architecture

├── hasura                          - metadata and databse definitions for hasura container
│   ├── metadata
│   ├── migrations
│   └── seeds
├── migrations                      - knex-based migration definitions
│   ├── migrations
│   └── seeds
└── server                          - apollo-server backend, proxied to with graphql from hasura and remote schema stitching back
    └── src
        ├── exceptions
        ├── graphql
        │   ├── local               - apollo-server graphql schema, types, and resolvers
        │   │   └── resolvers
        │   └── remote              - remote hasura graphql operations
        │       ├── mutations
        │       ├── queries
        │       └── utils           - helpers for managing remote schema permissions
        ├── middlewares
        ├── services                - functionality pertaining to local/resolvers implementations, does the heavy lifting for custom apollo-server resolvers
        └── utils

Points of Interest

For a minimum setup to include automatic schema codegen (assuming you have an apollo-server instance already), look at the following files:

Codegen:
server/package.json                         - includes `run` commands for graphql-codegen
server/codegen-remote.yml                   - Hasura graphql config
server/codegen-local.yml                    - Apollo-server graphql config
server/src/graphql/local/file-loader.js     - Custom loader to remove the schema type definitions from the hasura schema

Custom remote schema types:
server/src/graphql/local/localTypes.ts                 - Your apollo-server operations that will get merged into Hasura. NOTE: at least one `Query` operation is required
server/src/graphql/local/graphql.schema-remote.graphql - Hasura schema, stripped of the schema type (generated by codegen-remote.yml)
server/src/graphql/local/graphql.schema.graphql        - Apollo-server schema, incorporating stripped Hasura schema types

Custom resolvers:
server/src/graphql/local/resolvers/usersResolvers.ts   - Custom resolver for mutation seen in localTypes.ts above

Custom services (optional, depending on how one wishes to implement their resolvers):
server/src/services/users.service.ts                   - Service pattern for delegating actual resolver business logic

Apollo-server config:
server/src/app.ts             - Imports and merges types and resolvers. See initializeApollo()

Generating graphql TS types:

$ cd server
$ npm run generate                    # generate local and remote
$ npm run generate-l                  # generate local
$ npm run generate-r                  # generate remote

!!After updating the apollo-server schema, be sure to reload the remote schema in Hasura!!

Generating remote schema permissions:

Note: this repository assumes the name of the remote schema is apollo-server. Change this by changing the HASURA_REMOTE_SCHEMA_NAME parameter in the .env root file. If changes to the hasura role permissions cause a metadata conflict with the remote schema permissions, first drop the remote schema: (from ./server)

$ npm run hasura:remove_schema        # remove remote schema from hasura.

Make necessary changes to the hasura role permissions, then re-add the remote schema: (from ./server)

$ npm run hasura:add_schema           # add remote schema to hasura

Then, configure the allowed operations in the roles const in server/src/graphql/remote/utils/add_remote_schema_permissions.ts. The object is structed as follows:

const roles = {
    user: {
        Query: ["required"],
        Mutation: [
            "user_set_username_via_remote_schema"
        ],
        Subscription: [""]
    }
}

The above example would allow the user role to call the required query, and the user_set_username_via_remote_schema mutation. Configure as fits your use case.

Then, generate the permissions: (from ./server)

$ npm run hasura:add_schema_permissions          # add remote schema permissions to hasura

Full Setup

Setup ENV

$ cp .env.sample .env

Customize values as required.

Install required software:

$ install docker and docker-compose (depends on system)

Setup containers and volumes:

$ sudo docker-compose build
$ sudo docker-compose volume create db

Start postgres first:

$ sudo docker-compose up db

Setup database:

$ cd migrations
$ npm install
$ npx knex migrate:latest
$ npx knex seed:run --specific=seed_user.js

Ctrl+c docker-compose and start up everything:

$ sudo docker-compose up

Import the hasura metadata:

# install hasura CLI
$ curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
$ cd hasura
$ hasura metadata appy --admin-secret <hasura_admin_secret>

Go to the Hasura CLI at http://127.0.0.1:8080 and you should see the custom mutation in the graphiql explorer pane. Try running it, eg:

mutation MyMutation {
  user_set_username_via_remote_schema(jwt_uid: "abcdefghijklmnop", username: "abcd") {
    updated_at
    username
  }
}