Support Generating the Client Resolver Schema from TypeScript Types
evanyeung opened this issue · 0 comments
Relay Resolvers are a relatively new feature to Relay that allows for augmenting the GraphQL schema on the client side. In contrast to 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 and fields includes the GraphQL typenames, which often are one-to-one with the types in the TypeScript/Flow/etc. In implementation first schemas, for example Grats, 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) and would also like to support TypeScript.
Current Flow Implementation
We use the Hermes parser to generate an AST with Rust bindings. This AST is traversed in 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 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 compiling on Windows. The errors should show up when running
cargo build
in https://github.com/facebook/hermes/tree/main/unsupported/juno and https://github.com/facebook/hermes/tree/main/unsupported/hermes. Both of these folders are required for building the Hermes Rust bindings.
- A prerequisite to expanding TS Resolvers support to all users is to get the Rust Hermes parser bindings compiling on Windows. The errors should show up when running
- Step 2: Add a structured mapping for TypeScript types
codegen.rs
consumes the Hermes output and produces structured Rust data usingecmascript.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 facebook/hermes@2aafa0f.- 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 facebook/hermes@e6bc607 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