Skip to main content

· 11 min read
Gabriel Nordeborn

It's our great pleasure to announce the release of RescriptRelay 3.0.0. Version 3 is close to a rewrite, leveraging new and shiny features from both ReScript and Relay, creating the best and most idiomatic version of RescriptRelay yet.

In this post, we're going to talk about the new version and some of its features. But, let's start with a brief history of RescriptRelay, where we're at now, and where we're going.

A brief history of RescriptRelay

RescriptRelay started out as the marriage between my two favorite technologies - ReScript and Relay.

I've been using Relay virtually since it came out. I'm still in love with it, and it gets better every day. To me, Relay is a proven way to build scalable, maintainable and performant apps in a way that is both fast and robust.

Similarily, ReScript is an equally large passion for me, and I love it for the same reasons I love Relay - it's a robust, fast, proven way to build scalable, maintainable and performant apps.

Naturally, marrying these two should be a good idea, right? Well, that's what I set out to do, and after countless hours (5 years and counting), I can say that with version 3 it's as good as ever.

Relay is still a great bet to take

Quite a lot of time has passed since Relay and GraphQL was released to open source. And I strongly believe using it is still a great choice to make. In fact, Relay is still unique in that it addresses a lot of issues that not even recent innovations like React Server Components can address in a good way.

Meta also still works on it very actively, with the recent expansion of capabilites around local data and using Relay as your general state manager is a testament to. So, Relay continues to be a great bet. It's a proven technology with well know pros/cons, that's here to stay.

As for RescriptRelay, it has also been around for long and it's as actively developed as ever. We track new features from Relay, and move to new Relay versions as soon as they're released.

RescriptRelay is in production in quite a lot of companies, and I dare say the general experience is great! People enjoy working with it, and continue chosing it for new enedevours. For this I'm both grateful and excited.

A dedicated router for RescriptRelay

In tandem with RescriptRelay, we've built RescriptRelayRouter, a web router (React Native coming soon?) built specifically for use with RescriptRelay. It's performant, fully type safe (including for URL search params), and has a bunch of great features and tooling.

Check it out!

A Relay video series

Together with the release of v3, we've started recording a comprehensive video series on how to build great apps with ReScript + Relay (and RescriptRelayRouter). Check it out here. It's intended to serve as onboarding material for developers new to RescriptRelay.

This is of course extra important when introducing RescriptRelay at your company, since it ensures you'll have a set path to get new developers productive with ReScript and Relay.

More videos are continuously being recorded and the goal is to cover all aspects of ReScript and Relay, including the more advanced techniques that exist.

A few of the new features

That's enough background, let's look at some of the new features!

The lift from 1.0/2.0 to 3.0 has allowed us to enable a number of new very nice features from Relay in RescriptRelay, plus build some more unique features of our own. Version 3 is more robust, smoother, and leverages a bunch of new powerful features from ReScript v11+ that lets you write more concise and better Relay apps as well.

Below we'll take you on a tour of some of the new features.

Input unions (RescriptRelay exclusive)

GraphQL maps quite well to ReScript, which is a big part in why ReScript and Relay works so well together. However, there are plenty of places still where you'll miss the power of ReScript's type system in GraphQL.

One of these things is input objects. For output types in GraphQL we have unions, which map well to variants in ReScript. However, GraphQL doesn't have unions for inputs. This can needlessly complicate APIs, because there's no good way to communicate that a field or mutation takes one of a set of inputs.

Using the @oneOf input object and fields RFC in GraphQL, we've been able to work around this in RescriptRelay! Any time your server schema defines an input object with the @oneOf directive, RescriptRelay will give you a variant to use for that input instead of a regular object.

Here's an example:

input Address {
streetAddress: String!
postalCode: String!
city: String!
}

input Coordinates {
lat: Float!
lng: Float!
}

input Location @oneOf {
byAddress: Address
byCoordinates: Coordinates
byId: ID
}

type Query {
allShops(location: Location!): ShopConnection
}

This will produce the following variant for location:

type input_ByAddress = {
streetAddress: string,
postalCode: string,
city: string,
}

type input_ByCoordinates = {
lat: float,
lng: float,
}

type input_Location =
| ByAddress(input_ByAddress)
| ByCoordinates(input_ByCoordinates)
| ById(string)

Which in turn will let you interact with the input like this:

@react.component
let make = (~lat, ~lng) => {
let data = Query.use(~variables={location: ByCoordinates({lat, lng})})
}

This can make a world of difference for how easy to use and understandable an API is, and we're really happy this has landed in RescriptRelay. Read more in the docs on input unions.

Updatable queries and fragments

Updatable queries and fragments is Relay's new way of doing type safe, local updates to the cache. You'll use these in primarily 2 cases:

  • When working with local state managed by Relay (more on this below)
  • When updating the local cache after mutations

Previously, this has been quite complicated, since no official (and type safe) Relay API has existed. That's however fixed now!

Here's a basic example of an updatable query that lets you update a field in a type safe way:

// ViewerCurrentActiveUpdater.res
module Query = %relay(`
query ViewerCurrentlyActiveUpdaterQuery @updatable {
loggedInUser {
currentlyActive
}
}
`)

let updateCurrentlyActive = (currentlyActive: bool, ~environment: RescriptRelay.Environment.t) => {
RescriptRelay.commitLocalUpdate(~environment, ~updater=store => {
let {updatableData} = Query.readUpdatableQuery(store)

updatableData.loggedInUser.currentlyActive = currentlyActive
})
}

Read more in the docs on updatable queries and fragments.

Using Relay as your state manager with Relay resolvers and client schema extensions

With 3.0, we can finally officially recommend using Relay as your local state manager. We now support all relevant parts from Relay (which one notable exception in @assignable fragments, which will be supported soon).

Turns out, Relay is really powerful as a local state manager, because:

  • It's performant by default, with granular re-renders
  • It lets you twine together your local data with your server data
  • It even lets you link to server data from local data, so you can create your own local "subgraphs" that actually fetch data from the server
  • It's all type safe, and for reading data you work with it just like with server data. No need to consider that it's really local data

This is very exciting, and will let many Relay apps get rid of other state managers like Redux, Recoil, Jotai etc entirely. Read more in the docs on interacting with the store.

Better codesplitting with @preloadable

The @preloadable directive in Relay allows you to speed up and parallelize loading of data and component code even more by ensuring that the bare minimum to initiate the query for data is available separately so that it doesn't need to be bundled together with the code for also writing that response into the local data store.

RescriptRelayRouter already helps you separate loading data and code efficiently to eliminate waterfalls and ensure your network performance is efficient by default. @preloadable is another tool in the network performance toolbox, especially efficient for large queries.

Read more in the docs on @preloadable in RescriptRelay.

Easy and efficient data-driven codesplitting with @codesplit (RescriptRelay exclusive feature)

With RescriptRelayRouter and preloadable queries, codesplitting is very easy and efficient. But this is primarily at the route level. Sometimes you want to codesplit driven by what data is returned, not what route you're on.

Imagine a blog post where the post content itself can be either plain text or in markdown. When we render markdown, we'll need to load a potentially heavy markdown parser and renderer. But, we don't want to load that unless the blog post content is actually markdown. Enter the @codesplit directive!

The @codesplit directive let's you mark fragment spreads to have their components codesplit automatically, and the code for the codesplit component downloaded as soon as possible as the server response with the component data comes back, if that component indeed matches.

An example:

module Query = %relay(`
query BlogPostView($id: ID!) {
blogPost(id: $id) {
title
content {
... on Markdown {
...BlogPostMarkdownRenderer_content @alias
...BlogPostMarkdownRenderer_content @alias @codesplit
}
... on PlainText {
text
}
}
}
}
`)

@react.component
let make = (~blogPostId) => {
let data = Query.use(~variables={id: blogPostId})

switch data.blogPost {
| Some({title, content}) =>
open Query.CodesplitComponents

<div>
<Title>{React.string(title)}</Title>
{switch content {
| Some(Markdown({blogPostMarkdownRenderer_content})) =>
<BlogPostMarkdownRenderer content=blogPostMarkdownRenderer_content />
| Some(PlainText({text})) => <div>{React.string(text)}</div>
| None => React.null
}}
</div>
| None => <FourOhFour context="blog post" />
}
}

That's all it takes! <BlogPostMarkdownRenderer /> is now codesplit, and the code for it will be downloaded if it matches as soon as the server response comes back, not when the codesplit component is first rendered, like with vanilla React.lazy.

Check out the @codesplit documentation for more information and how to start using it.

Another good example is something like Facebook's news feed, where there are potentially hundreds of different components that might be needed depending on what type of news feed items are returned from the server. You want to codesplit all of these so you only load code to render the actual stories you get back, but you won't know what components to load until you get the actual server response back. @codesplit helps you do this easily and with great performance.

@alias and conditional fragments

Relay uses the fragment model, which is one of the most powerful things about GraphQL. However, it has traditionally been really difficult to figure out if a fragment was actually fetched or not.

With the @alias directive, this has now become possible, and easy! An example:

module Query = %relay(`
query BlogPostView($id: ID!) {
blogPost(id: $id) {
...BlogPostTitle_post
...BlogPostTitle_post @alias
}
}
`)

@react.component
let make = (~blogPostId) => {
let data = Query.use(~variables={id: blogPostId})

switch data.blogPost {
| Some({fragmentRefs}) => <BlogPostTitle post=fragmentRefs />
| Some({blogPostTitle_post}) => <BlogPostTitle post=blogPostTitle_post />
| None => <FourOhFour context="blog post" />
}
}

Relay puts the fragment ref on its own prop here. This is especially important when including that fragment is conditional:

module Query = %relay(`
query BlogPostView($id: ID!, $includeTitle: Boolean!) {
blogPost(id: $id) {
...BlogPostTitle_post @alias @include(if: $includeTitle)
}
}
`)

@react.component
let make = (~blogPostId, ~includeTitle) => {
let data = Query.use(~variables={id: blogPostId, includeTitle})

switch data.blogPost {
| Some({blogPostTitle_post: Some(blogPostTitle_post)}) =>
// With the regular `fragmentRefs` approach where all fragment refs are on the same prop,
// there's no way to figure out if `BlogPostTitle_post` was actually included in the
// response without resorting to hacks.
<BlogPostTitle post=blogPostTitle_post />
| _ => <FourOhFour context="blog post" />
}
}

This makes working with conditional fragments easier and safer.

Going from V2 to V3

Since V3 has a lot of changes, you'll need to take some manual steps to migrate from prior versions to V3. Check out the migration doc for more information.

Wrapping up

Thank you for reading, and we hope you're as excited as we are for RescriptRelay v3!

Oh, and also, if you haven't already, you should check out the tutorial on RescriptRelay. And of course, the new Relay video series.

· 2 min read
Gabriel Nordeborn

The time has finally come - RescriptRelay 1.0.0 is released! 1.0.0 is a culmination of a long period of work, and comes with a large amount of improvements and new features.

All changes (including the breaking ones) can be seen in the changelog.

Development is as active as ever, and RescriptRelay itself is used in a growing number of production code bases. It feels great to be able to finally put 1.0.0 out there, and start a new chapter in RescriptRelay's journey.

Installing it

yarn add rescript-relay@latest

Make sure you check out the changelog for breaking changes as you upgrade.

What comes next?

While there's always work to do and improvements to make, RescriptRelay is in a good state feature wise. The thing missing isn't necessarily features, it's teaching you how to use all of this in the best possible way.

Therefore, we're going to enter a phase for RescriptRelay where we'll focus as much as possible on teaching the ins and outs of building UI with RescriptRelay. This will include things like:

  • Much extended and more in depth docs
  • Concrete copy-pasteable recipes for how to do just about anything you might need to do when building UI
  • Testimonials and case studies from the companies that are already using RescriptRelay in production

We'll also focus on how we can explain the value proposition of RescriptRelay better. Both ReScript and Relay is quite a large buy in, and we need to do a better job explaining why that buy in might be worth it for your company.

Therefore, documentation, examples, recipes and testimonials are the most important things going forward, both for our existing users, as well as new users. Evaluating if RescriptRelay is a good fit for your particular use case should be accessible and easy. Not necessarily easy to make the decision, but easy to find all the information you need.

Are you interested in helping out with this? Please join our Discord!

Thank you all users and contributors!

Lastly I'd like to thank everyone who's been supporting me in various ways throughout the years. Everything from testing, contributing actual code, to just cheering on as RescriptRelay makes progress - you're a huge part of why I still think it's fantastically fun to maintain and continue develop RescriptRelay.

I hope you enjoy 1.0.0!

· 4 min read
Gabriel Nordeborn

It's finally here - the first beta of the first stable RescriptRelay version! This is a huge milestone for the RescriptRelay project. Thank you everyone who's helped out with testing (and PR:s) for this release!

Follow the instructions in the changelog to install and try it out, and make sure you post any issues you encounter on the issue tracker. 1.0.0-beta.1 already runs in production in a mid-sized product, and has been tried on multiple code bases of varying sizes, so it should already be pretty stable.

What's new in 1.0.0

The big news in 1.0.0 is that RescriptRelay has been fully integrated into the new Relay Rust compiler. This means that the ReScript type generation has been rewritten from the old combination of ReasonML and TypeScript, into 100% Rust. Having the type generation deeply integrated into the Rust compiler comes with the following benefits:

  1. Speed. The new Rust based compiler is ~7-10x faster than the current compiler. And even faster than that for incremental recompiles. This is in part thanks to Rust being a fast language, and also thanks to type generation now happening in the same process as the rest of the compilation, rather than being shelled out to a separate process like before.
  2. Future proofing. The old JS based compiler has been killed, and the new Rust Relay compiler already has a bunch of features we can now use that would've never made it into the JS compiler. One example is the @required directive that can force nullable fields to be non-null. Perfect when dealing with views that requires data to be present that's nullable in the schema, letting the nullability bubble to one central place.
  3. More advanced features. Being 100% integrated into the Relay compiler itself means we have access to all of the things the compiler has access to when generating our types and artifacts. This means we'll be able to build much more advanced and deeply integrated features than we could've ever done with the old compiler.

For this, we've forked the Relay compiler to make a few, surgical changes to support what we need for ReScript type generation. The fork changes very little in the core compiler, so maintaining the fork going forward should be really easy.

Breaking changes?

Moving to the new compiler should be fairly seamless. Basically, you shouldn't need to change anything at all on the RescriptRelay side. Just install the new package and restart the compiler, and you should be good to go. Check out the changelog for more details on all of the changes in 1.0.0.

What lies ahead

Shipping 1.0.0 will be a significant milestone for the RescriptRelay project. But, development is unlikely to slow down with releasing 1.0.0. Instead, we're now able to start exploring a bunch of things we couldn't explore before. Here's a non-exhaustive list of what'll happen in the coming year for RescriptRelay.

  • Integrating the Relay LSP. The Relay team has built a dedicated LSP for Relay in parallel with the Rust rewrite. The LSP has a bunch of really nice Relay specific IDE functionality like displaying Relay problems directly in the editor, autocompleting fragments, autocompleting fragment arguments, etc. We'd like to integrate this into the RescriptRelay VSCode extension
  • Exploring a "dual mode" for easy integration with existing TypeScript code bases. This would mean that you could start using RescriptRelay in your existing Relay TypeScript code base as easily as possible. Something that's possible today, but is cumbersome.
  • Content! A ton of content for everything you need to know and learn in order to build great UI using RescriptRelay. Learning the Relay paradigm, neat tricks you can use with ReScript, etc. A comprehensive resource for everything you need.

Fun things are ahead!

RescriptRelay is here to stay, and I'm very excited to continue working on it together with all of you great contributors and users. Special shout out to everyone active in the Discord who's giving encouragement, helping out testing and so on.

Thank you for reading!