facebook / facebook/relay

Support Generating the Client Resolver Schema from TypeScript Types

Open
#4,768 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
19k
Forks
1.9k
PR merge metrics
No merged PRs in 30d

Description

[Relay Resolvers](https://relay.dev/docs/guides/relay-resolvers/introduction/) are a relatively new feature to Relay that allows for augmenting the GraphQL schema on the client side. In contrast to [client schema extensions](https://relay.dev/docs/guides/client-schema-extensions/), Relay Resolvers include an implementation for computing a new field or model. This allows for backing Relay with an external, client-side data source or computing fields based on other fields.

Currently, defining a Relay Resolver feels redundant when using a typed language. The docblock syntax used to define [types](https://relay.dev/docs/guides/relay-resolvers/defining-types/) and [fields](https://relay.dev/docs/guides/relay-resolvers/defining-fields/) includes the GraphQL typenames, which often are one-to-one with the types in the TypeScript/Flow/etc. In [implementation first](https://jordaneldredge.com/blog/implementation-first/) schemas, for example [Grats](https://grats.capt.dev/), the schema types are pulled directly out of the definition, reducing the amount of boilerplate and making it impossible for the schema and implementation to get out of sync.

We already have this working in Flow (see [https://github.com/facebook/relay/blob/main/compiler/crates/relay-schema-generation/src/lib.rs](https://github.com/facebook/relay/blob/main/compiler/crates/relay-schema-generation/src/lib.rs)) and would also like to support TypeScript.

## Current Flow Implementation

We use the [Hermes parser](https://github.com/facebook/hermes) to generate an AST with [Rust bindings](https://github.com/facebook/hermes/tree/2c36b0a775dfdc4f8f2eb1b079b565f6e9a91707/unsupported/hermes/crates/hermes\_estree\_codegen/src). This AST is traversed in [`relay-schema-generation/src/lib.rs`](https://github.com/facebook/relay/blob/89afc1ae30c0bbe1ddd37a20dd4cceb21c18dc21/compiler/crates/relay-schema-generation/src/lib.rs) to build an IR representing the models and fields for the resolvers. We use two passes in this file: the first one collects all the models and fields and the second pass connects the fields to the models. This IR is the same as the docblock IR used to generate Resolvers from docblock comments which means it has a few quirks to work around. However, it is easier to understand than directly generating a [GraphQL Schema](https://spec.graphql.org/October2021/\#sec-Type-System) and already has the necessary Relay codegen set up. In the future, we may directly generate GraphQL Types.

## Steps to Add TypeScript Support

The simplest way we can see of adding TS support is to just map the TS nodes to the same ESTree nodes as their equivalent Flow nodes. For the simple types supported by Relay, each TS node should have a corresponding Flow node. This means the only changes necessary would be in the Hermes parser.

* **Step 1: Windows Support**
* A prerequisite to expanding TS Resolvers support to all users is to get the [Rust Hermes parser bindings](https://github.com/facebook/hermes/tree/main/unsupported/hermes) compiling on Windows. The errors should show up when running `cargo build` in [https://github.com/facebook/hermes/tree/main/unsupported/juno](https://github.com/facebook/hermes/tree/main/unsupported/juno) and [https://github.com/facebook/hermes/tree/main/unsupported/hermes](https://github.com/facebook/hermes/tree/main/unsupported/hermes). Both of these folders are required for building the Hermes Rust bindings.
* **Step 2: Add a structured mapping for TypeScript types**
* [`codegen.rs`](https://github.com/facebook/hermes/blob/main/unsupported/hermes/crates/hermes\_estree\_codegen/src/codegen.rs) consumes the Hermes output and produces structured Rust data using [`ecmascript.json`](https://github.com/facebook/hermes/blob/main/unsupported/hermes/crates/hermes\_estree\_codegen/src/ecmascript.json). This is the point at which the Flow and TS types can be combined. Each of the TS types can be mapped to the same ESTree node that the Flow type maps to. This abstracts away the differences between the Flow and TS ASTs before reaching the Relay compiler proper. An example of changing the codegen output is in [https://github.com/facebook/hermes/commit/2aafa0f0d9d542ac3de7ce29db76b12f6dc0765b](https://github.com/facebook/hermes/commit/2aafa0f0d9d542ac3de7ce29db76b12f6dc0765b).
* The `ecmascript.json` types may need to be updated for clarity or to find the shared set of types between Flow and TS. For example, take a look at commit [https://github.com/facebook/hermes/commit/e6bc6075364fae356996e7fe1e9b27af2d7e0f05](https://github.com/facebook/hermes/commit/e6bc6075364fae356996e7fe1e9b27af2d7e0f05) which adds some Flow types.

## Future Work

While this solution should work for TypeScript, supporting more compile-to-JS languages could not be done in Hermes. Creating a well defined API directly in the GraphQL schema definition language (SDL) would be a more flexible solution. The GraphQL SDL is well defined, contains enough flexibility to add metadata necessary to run resolvers, and is serializable to easily pass the information from language specific compilers to the Relay compiler. An example of the current SDL output can be found at [https://github.com/facebook/relay/blob/6bb5ade19f356d8540c8edb49641e4fbb81f8af0/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.expected#L34](https://github.com/facebook/relay/blob/6bb5ade19f356d8540c8edb49641e4fbb81f8af0/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.expected#L34)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.