Giter Site home page Giter Site logo

zth / rescript-relay Goto Github PK

View Code? Open in Web Editor NEW
332.0 10.0 48.0 53.9 MB

Use Relay with ReScript.

Home Page: https://rescript-relay-documentation.vercel.app/docs/getting-started

JavaScript 34.82% Shell 0.35% TypeScript 15.78% HTML 0.18% ReScript 41.18% OCaml 7.69%
reasonml reason-react relay-modern relay react graphql rescript rescript-react

rescript-relay's Introduction

rescript-relay

Use Relay with ReScript.

Join our Discord

Getting started

Are you using version >= 0.13.0 and ReScript syntax with VSCode? Make sure you install our dedicated VSCode extension. Note: It only works with ReScript syntax.

Check out the documentation.

Also, check out the changelog - things will continue to change between versions (including breaking changes, although we'll try and keep them to a minimum) as we iterate and reach a stable version.

What it looks like

Your components define what data they need through %relay().

/* Avatar.res */
module UserFragment = %relay(`
  fragment Avatar_user on User {
    firstName
    lastName
    avatarUrl
  }
`)

@react.component
let make = (~user) => {
  let userData = UserFragment.use(user)

  <img
    className="avatar"
    src=userData.avatarUrl
    alt={
      userData.firstName ++ " "
      userData.lastName
    }
  />
}

Fragments can include other fragments. This allows you to break your UI into encapsulated components defining their own data demands.

Hooks to use your fragments are autogenerated for you. The hook needs a fragment reference from the GraphQL object where it was spread. Any object with one or more fragments spread on it will have a fragmentRefs prop on it, someObj.fragmentRefs. Pass that to the fragment hook.

Avatar_user is spread right on the fragment, so we pass userData.fragmentRefs to the <Avatar /> component since we know it'll contain the fragment ref for Avatar_user that <Avatar /> needs. The <Avatar /> component then uses that to get its data.

/* UserProfile.res */
module UserFragment = %relay(`
  fragment UserProfile_user on User {
    firstName
    lastName
    friendCount
    ...Avatar_user
  }
`)

@react.component
let make = (~user) => {
  let userData = UserFragment.use(user)

  <div>
    <Avatar user=userData.fragmentRefs />
    <h1> {React.string(userData.firstName ++ (" " ++ userData.lastName))} </h1>
    <div>
      <p>
        {React.string(
          userData.firstName ++ (" has " ++ (userData.friendCount->string_of_int ++ " friends.")),
        )}
      </p>
    </div>
  </div>
}

Finally, you make a query using %relay() and include the fragments needed to render the entire tree of components.

/* Dashboard.res */
module Query = %relay(`
  query DashboardQuery {
    me {
      ...UserProfile_user
    }
  }
`)

@react.component
let make = () => {
  let queryData = Query.use(~variables=(), ())

  <div> <UserProfile user=queryData.me.fragmentRefs /> </div>
}

Note about versioning

There's plenty of work ongoing to bring RescriptRelay to full ReScript v11 support, including uncurried mode. Here's the versioning scheme that'll be followed going forward:

  • 1.x will receive critical bug fixes etc, but new features won't be added
  • 2.x will soon ship, and it'll focus on compatibility with ReScript v11, and uncurried mode (uncurried mode will be optional). This is intended to make the transition to v11+ smooth
  • 3.x will also soon ship, and that'll fully embrace uncurried mode (no curried mode available), and add a bunch of new stuff + change existing APIs to make them better and more ergonomic

Examples

rescript-relay's People

Contributors

anmonteiro avatar arnarkari93 avatar bdj avatar cometkim avatar dan003400 avatar dependabot[bot] avatar dzakh avatar emilios1995 avatar fakenickels avatar gyeop avatar hariroshan avatar justive avatar kingdutch avatar knowbody avatar lozlow avatar mbirkegaard avatar mellson avatar miryangjung avatar moox avatar mulholo avatar mxthevs avatar reck753 avatar renanmav avatar sorenhoyer avatar sync avatar tsnobip avatar webican avatar xiniha avatar yakimych avatar zth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rescript-relay's Issues

Error on pattern matching within commitMutation

Description

I’m trying to execute a mutation without using Mutation.use(...). Instead, I want to execute it within another file (outside a component) using Mutation.commitMutation(...). Something like this.

My mutation basically executes some code inside onCompleted. It uses the response data to define which behavior the app will follow. The mutation SDL is described below:

mutation UserLoginWithEmailMutation($input: UserLoginWithEmailInput!) {
  userLoginWithEmail: UserLoginWithEmail(input: $input) {
    token
    error
  }
}

Simple, right? If I write the below snippet inside of my component code, the mutation works perfectly and I can execute a pattern matching over the data to dictate which path the app will follow.

// UserLogin.re

let (mutate, _) = UserLoginWithEmailMutation.use();

let handleLogin = _ =>
  if (!loading) {
    setLoading(_ => true);

    mutate(
      ~variables={
        input: {
          clientMutationId: None,
          email,
          password,
        },
      },
      ~onCompleted=
        (data, _) => {
          setLoading(_ => false);

          (
            switch (data) {
            | {userLoginWithEmail: None} => ()
            | {userLoginWithEmail: Some({error: Some(error)})} =>
              Alert.alert(~title="Error", ())
            | {userLoginWithEmail: Some(userLoginWithEmail)} =>
              switch (userLoginWithEmail.token) {
              | Some(token) =>
                ReactNativeAsyncStorage.setItem("token", token) |> ignore;
                navigation->Navigation.navigate("FeedNavigator");
              | None => Alert.alert(~title="Error", ())
              }
            }
          )
          |> ignore;
        },
      (),
    )
    |> ignore;
  };

Now let’s take a look at how it would work within a UserLoginWithEmailMutation.re file that is near to the component file.

The new architecture

Encouraged to extract some logic from the component, let's define the mutation and the mutate actions in another file.

Our mutation file would be something like this:

// UserLoginWithEmailMutation.re

exception MutationFailed(string);

open ReactNative;
open ReactNavigation;
open UserLoginWithEmailMutation_graphql.Types;

module UserLoginWithEmailMutation = [%relay.mutation
  {|
    mutation UserLoginWithEmailMutation($input: UserLoginWithEmailInput!) {
      userLoginWithEmail: UserLoginWithEmail(input: $input) {
        token
        error
      }
    }
  |}
];

let commit = (~environment, ~setLoading, ~email, ~password, ~navigation) => {
  setLoading(_ => true);

  UserLoginWithEmailMutation.commitMutation(
    ~environment,
    ~variables={
      input: {
        clientMutationId: None,
        email,
        password,
      },
    },
    ~onCompleted=
      (data, _) => {
        setLoading(_ => false);

        (
          switch (data) {
          | {userLoginWithEmail: None} =>
            raise(MutationFailed("Could not find userLoginWithEmail."))
          | {userLoginWithEmail: Some({error: Some(error)})} =>
            Alert.alert(~title="Error", ())
          | {userLoginWithEmail: Some(userLoginWithEmail)} =>
            switch (userLoginWithEmail.token) {
            | Some(token) =>
              ReactNativeAsyncStorage.setItem("token", token) |> ignore;
              navigation->Navigation.navigate("FeedNavigator");
            | None => Alert.alert(~title="Error", ())
            }
          }
        )
        |> ignore;
      },
    (),
  )
  |> ignore;
};

And our component file (the part that interacts with Relay mutation) would turn to something like this:

// UserLogin.re

let environment = ReasonRelay.useEnvironmentFromContext();

let handleLogin = _ => {
  UserLoginWithEmailMutation.commit(
    ~environment,
    ~setLoading,
    ~email,
    ~password,
    ~navigation,
  )
  |> ignore;
};

Finally, the problem

When we try to do the exact same pattern matching that we did within UserLogin.re, we get the following error:

  We've found a bug for you!
  /Users/renan/git/relay/src/modules/auth/mutations/UserLoginWithEmailMutation.re 36:11-38
  
  34 ┆ (
  35 ┆   switch (data) {
  36 ┆   | {userLoginWithEmail: None} =>
  37 ┆     raise(MutationFailed("Could not find userLoginWithEmail."))
  38 ┆   | {userLoginWithEmail: Some({error: Some(error)})} =>
  
  This pattern matches values of type
    UserLoginWithEmailMutation_graphql.Types.response
  but a pattern was expected which matches values of type
    option(UserLoginWithEmailMutation.Types.response)
  
[2/6] Building src/modules/auth/UserLogin.reast
FAILED: subcommand failed.
>>>> Finish compiling(exit: 1)

Use relay-experimental package

Why is this library not using the relay-experimental package but rather binding experimental features in relay from react-relay/lib/relay-experimental? Is there some reason for it or was relay-experimental maybe non-existent when the bindings were written?

Mutation results in compilation error in generated *_graphql.re file

Hi! I've started converting a reason-apollo-hooks-based app to reason-relay and have encountered an issue after trying to implement a mutation. Seems like the generated types are circular for this case.

Steps to reproduce:

  1. Clone the "relay" branch from here: https://github.com/Yakimych/relogify/tree/relay
  2. Run npm run relay. The files should get generated without errors.
  3. Run npm run build.

The following error in AddResultMutation_graphql.re shows up:
This type constructor's parameter, 'players_insert_input', can't be found. Is it a typo?

The schema comes from Hasura and is public, so it should be possible to reproduce: https://hockeyapi.yakim.dev/v1beta1/relay.

Crash since 0.9.0 (bs-platform 7.3.x issue)

Still looking at what's happening but here it's what i have so far

SSR fetch error TypeError: Cannot read property 'undefined' of undefined
    at Object.traverser (/Users/anthonymittaz/Projects/Homepass/homepass-meet/meet-app/.next/server/static/development/pages/room.js:14912:38)
    at Module._convertObj (/Users/anthonymittaz/Projects/Homepass/homepass-meet/meet-app/.next/server/static/development/pages/room.js:13901:58)
    at convertResponse (/Users/anthonymittaz/Projects/Homepass/homepass-meet/meet-app/.next/server/static/development/pages/room.js:15720:88)
    at Module._1 (/Users/anthonymittaz/Projects/Homepass/homepass-meet/meet-app/.next/server/static/development/pages/room.js:8222:12)
    at /Users/anthonymittaz/Projects/Homepass/homepass-meet/meet-app/.next/server/static/development/pages/room.js:14657:138
    at runMicrotasks (<anonymous>)

Screen Shot 2020-05-18 at 8 38 48 am

[PPX] Generate module signature with PPX

Currenty, we don't generate a module signature for each extension node with the PPX, which means a lot of internal/irrelevant stuff ends up accessible/in autocomplete etc from the PPXed extension nodes. This is potentially confusing, so we should limit what we expose by generating that as well with the PPX.

Missing utils.js

I'm seeing an issue that utils.js is missing. The require statement looks for ./utils which doesn't exist in the compiled output in /lib/js/src. If I copy the utils file from /src everything starts working perfectly.

[PPX] Validate reserved field names

Since Reason has a bunch of reserved words, there's a bunch of words that you simply cannot use for field names when querying your GraphQL API through ReasonRelay. The solution is quite simply to alias the fields to something appropriate. Things that need to be dealt with:

  • Any of the reserved words like type etc
  • Anything that is capitalized (User)

Note that this is only for the field selection, so having fields with "reserved/invalid" names in your schema is still totally fine. It just cannot end up on the Reason side named like that.

The PPX is best suited for this type of validation I think.

Reason-Relay seems incompatible with latest graphql package 15.0.0

error:

Generating ReasonRelay assets...
(node:67882) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '__schema' of undefined
    at generateSchemaAssets (/Users/anthonymittaz/Projects/Startup/pollen-tracker/node_modules/reason-relay/compiler/generateSchemaAssets.js:6:27)
    at /Users/anthonymittaz/Projects/Startup/pollen-tracker/node_modules/reason-relay/compiler/compiler-cli.js:69:9
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:67882) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:67882) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
HINT: pass --watch to keep watching for changes.

Support for request mocking

Hi :)

I'm currently looking into using reason-relay for an internal project and digging deeper into getting testing working I ran into the lack of bindings for the mocking components. Is there any progress in this area? If not do you foresee any hurdles to binding to them? If not I could feasibly start on a PR (for now I'll be mocking fetch to work around the issue).

Update after 7 hours:

Just noticed your repository zth/graphql-query-test-mock. Unsure of the advantages to that one over the built in MockPayloadGenerator etc. If this is the preferred one I don't mind binding to that instead. I see you've used this in the unit tests for reason-relay in it's raw JS form.

[feature] Add a version of useFragment you can pass an option to

Occasionally you'll want to pass fragment refs that are optional. Due to the rules of hooks in React, you always have to return the same amount of hooks in your render functions, so you can't just conditionally not run Fragment.use if you don't have that fragments data.

We'll need to add something like Fragment.useOptional: option(fragmentRefs) => option(fragmentData).

[docs] Clarify Query.use vs Query.usePreloaded/Query.useLoader

Relay allows you to easily start loading data as soon as possible, and not have to wait until a component actually renders (like with Query.use). There's a bunch of primitives for this in Relay/ReasonRelay, and they need to be documented properly; when to use what, and what the trade-offs are.

[feature] Add way to resolve a query fetch only from local cache

Mimicking Query.fetch, but instead of fetching from the server, only check the cache. This isn't "native" to Relay (Relay does not provide a fetchQuery that checks the cache out of the box), so it'll need some code.

One way is to just add ~checkCache=? to Query.fetch.

Suspense with fragments

Fragments will suspend when their required data is not ready. In javascript, you would pass in a nullable fragment reference. How would you do this in reason? Since fragment references are not optional.

Would it make sense to move the getFragmentRefs() function "one level" up? So it is more inline how it is done in plain javascript?

/// now
type response_field = {
  getFragmentRefs: unit => { ... }
};
type response = { field: option(response_field)};

// perposal
type response_field = {...not_sure_about_this };
type response = {
 field: option(response_field),
 getFragmentRefs: unit => { ... }
};

Uncaught ReferenceError: global is not defined

When following the Getting Started docs to add Reason Relay to my project, I get the following error in my console with no components being rendered to the page:

image

I am starting off of the bsb -init my-react-app -theme react-hooks template (with updated react and react-dom packages, as specified in 'Getting Started').

Apologies if I have missed some obvious config, but the stack trace is pointing me to relay modules so I was wondering if there was a bug or easy fix to be found with this repo.

Happy to upload a reproduction repo if that is helpful to you but I've put what I think could be relevant code below for brevity.

Potentially Relevant Code

index.re

ReactExperimental.renderConcurrentRootAtElementWithId(
  <ReasonRelay.Context.Provider environment=RelayEnv.environment>
    <App />
  </ReasonRelay.Context.Provider>,
  "root",
);

package.json

{
  "name": "frontend",
  "version": "0.1.0",
  "scripts": {
    "build": "bsb -make-world",
    "start": "bsb -make-world -w -ws _ ",
    "clean": "bsb -clean-world",
    "server": "moduleserve ./ --port 8000",
    "relay": "reason-relay-compiler",
    "relay:watch": "reason-relay-compiler --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "BuckleScript",
    "ReasonReact",
    "reason-react"
  ],
  "resolutions": {
    "react": "^0.0.0-experimental-aae83a4b9",
    "react-dom": "^0.0.0-experimental-aae83a4b9"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "bs-fetch": "^0.5.2",
    "graphql": "^14.6.0",
    "react": "^0.0.0-experimental-aae83a4b9",
    "react-dom": "^0.0.0-experimental-aae83a4b9",
    "react-relay": "0.0.0-experimental-5f1cb628",
    "reason-react": ">=0.7.0",
    "reason-relay": "^0.5.3",
    "relay-compiler": "8.0.0",
    "relay-config": "8.0.0",
    "relay-runtime": "8.0.0"
  },
  "devDependencies": {
    "bs-platform": "^7.0.1",
    "moduleserve": "^0.9.0"
  }
}

package.json

{
  "name": "reason-react-examples",
  "reason": {
    "react-jsx": 3
  },
  "sources": {
    "dir" : "src",
    "subdirs" : true
  },
  "bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
  "package-specs": [{
    "module": "commonjs",
    "in-source": true
  }],
  "suffix": ".bs.js",
  "namespace": true,
  "ppx-flags": ["reason-relay/ppx"],
  "bs-dependencies": [
    "reason-react",
    "reason-relay",
    "bs-fetch"
  ],
  "refmt": 3
}

RelayEnv.re

/** Custom error */
exception Graphql_error(string);

// See https://relay.dev/docs/en/network-layer for .js version
// of this file

/**
 * A standard fetch that sends our operation and variables to the
 * GraphQL server, and then decodes and returns the response
 */
let fetchQuery: ReasonRelay.Network.fetchFunctionPromise =
  (operation, variables, _cacheConfig) =>
    Fetch.(
      fetchWithInit(
        "http://localhost:4000/graphql", // TODO update for production
        RequestInit.make(
          ~method_=Post,
          ~body=
            Js.Dict.fromList([
              ("query", Js.Json.string(operation.text)),
              ("variables", variables),
            ])
            |> Js.Json.object_
            |> Js.Json.stringify
            |> BodyInit.make,
          ~headers=
            HeadersInit.make({
              "content-type": "application/json",
              "accept": "application/json",
            }),
          (),
        ),
      )
      |> Js.Promise.then_(resp =>
           if (Response.ok(resp)) {
             Response.json(resp);
           } else {
             Js.Promise.reject(
               Graphql_error(
                 "Request failed: " ++ Response.statusText(resp),
               ),
             );
           }
         )
    );

let network =
  ReasonRelay.Network.makePromiseBased(~fetchFunction=fetchQuery, ());

let environment =
  ReasonRelay.Environment.make(
    ~network,
    ~store=
      ReasonRelay.Store.make(~source=ReasonRelay.RecordSource.make(), ()),
    (),
  );

Error on older Mac Os X versions

First of all, thanks for the awesome work you're doing :)

On older OSX versions (10.13.6), when proceeding after installing this module, it throws an error:
dyld: Symbol not found: ____chkstk_darwin
and additional error
Referenced from: /path/to/my-project/node_modules/reason-relay/ppx (which was built for Mac OS X 10.15)

I'm running OSX version 10.13.6

I fixed it by compiling the ppx from source after cloning the project and replacing the installed ppx.
cd packages/reason-relay/reason-relay-ppx/
esy build
cp _esy/default/build/default/bin/ReasonRelayPpxApp.exe ../../../path/to/my-project/node_modules/reason-relay/ppx

Hopefully, this will help someone else too πŸ‘

Handling FutureAddedValue

I have a graphgl query that looks like this:

{
  listItems {
    id
    name
    type
  }
}

with type property as an enum with values: COMPREHENSIVE, PRACTICE and MOCK.

This is what the enum type in the generated graphql file looks like

type enum_ItemType = [
  | `COMPREHENSIVE
  | `MOCK
  | `PRACTICE
  | `FutureAddedValue(string)
];

However whenever I make a request using relay to fetch this query, the type property keeps returning a FutureAddedValue. How do I handle this?

[PPX] Automatically add scope hints to records returned from pagination hooks

Currently, destructuring a record coming from a hook generated by the PPX needs to be provided a scope hint in order for it to work. Example:

let ReasonRelay.{data, hasNext, loadNext} = SomePaginationFragment.usePagination(fragmentRef);

Ideally, the PPX should inject ReasonRelay. to any pagination hook call, enabling the call to look like this instead:

let {data, hasNext, loadNext} = SomePaginationFragment.usePagination(fragmentRef);

Just better DX, no other reason really.

Integrate with GraphQL ppx

With 1.0 on the way and several improvements in the GraphQL ppx, I think it will be feasible to support Relay. This slightly changes the implementation. With GraphQL ppx we can generate javascript that will look the same as if you'd write Relay by hand in Javascript including tagged template literals etc.

Basically where GraphQL ppx can come in is generating types from the schema (this is generic and is the same for each client), and parsing the JSON output and converting it to Reason datatypes (also generic).

To have the best support we need a "Relay mode" in GraphQL ppx. That will facilitate a few things like how to handle fragments in Relay (as they are global) etc.

I think it would be really great to have a single piece of infrastructure in Reason that can be reused across the GraphQL ecosystem. One of the cool benefits that this would open up, is that now GraphQL fragments and queries can be literally reused across clients. (For instance, if you want to share components between a Gatsby site and a Relay app).

[Tooling] Integrate ReasonRelay with VSCode GraphQL extension vscode-apollo

A while back I got started on integrating ReasonRelay (and graphql_ppx for that matter) support to vscode-apollo, which is an excellent VSCode extension for getting syntax highlighting and auto completion for GraphQL. vscode-apollo can also be used with vscode-apollo-relay https://github.com/relay-tools/vscode-apollo-relay which is awesome and gives a really good experience working with Relay.

However, I got stuck and couldn't make it work, I don't know enough about VSCode extensions. I'd be very interested in getting help with this if anyone's interested in picking it up. Here's the PR that I got started working on: apollographql/apollo-tooling#1488

Please reach out to me if you're interested in this!

Proper location error reporting for PPX

I never managed to get proper location based error reporting working in the PPX, so I opted for Obj.magic(). I don't know how to solve this currently, but if someone more experienced with OCaml/Reason knows how to fix this and wants to contribute, that'd be awesome!

Problem is located here:
https://github.com/zth/reason-relay/blob/d2a7f4f8524d8af92fb00db11cd17089916db8f5/packages/reason-relay/ppx/ppx/ReasonRelayPpx.re#L57-L65 (just remove Obj.magic() and uncomment the error code)

It produces this error:

Error: This expression has type
         ('a -> 'b, Caml.Format.formatter, Ppxlib__.Import.unit,
          (Caml.Format.formatter -> 'a) -> 'c, 'd, 'e)
         CamlinternalFormatBasics.fmt
       but an expression was expected of type
         ('a -> 'b, Caml.Format.formatter, Ppxlib__.Import.unit,
          Ppxlib__.Import.unit, Ppxlib__.Import.unit,
          Ppxlib.Location.Error.t)
         CamlinternalFormatBasics.fmt
       Type (Caml.Format.formatter -> 'a) -> 'c is not compatible with type
         Ppxlib__.Import.unit = unit 

And if that's solvable, there's probably more places to add proper location errors, where custom exceptions are raised now instead.

Separate preload code from the component module

Hi @zth,

I'm trying to clone relay's issue tracker example into reason-relay here (to learn ReasonML, and now I'm thinking ReasonRelay is best of Relay at this moment)

Currently, I'm having difficulty implementing Concurrent UI Pattern (a.k.a Render-as-you-fetch) in reason-relay. The problem is preload function is colocated with the component in reason-relay. When implementing Concurrent UI pattern, I wanna execute component lazy loading and data preloading in parallel just like this, but this isn't possible because calling preload requires importing component module synchronously.

So It seems should be moved or duplicated the function into the generated artifacts.

let loadRoute = (route: t) =>
  switch (route) {
  | Home => {
      component:
        ReactCache.make(
          () => {
            DynamicImport.(
              import("./HomeRoot.bs.js")->resolve
              <$> (
                (module Component: PreloadableComponentType) => Component.make
              )
            )
          },
          None,
        ),
      data:
        Home(
-          HomeRoot.HomeRootQuery.preload(
+          // This should be:
+          HomeRootQuery_graphql.preload(
+          // So BuckleScript import only query code, not the component code above.
            ~environment=RelayEnv.environment,
            ~variables={owner: "facebook", name: "relay"},
            ~fetchPolicy=StoreOrNetwork,
            (),
          ),
        ),
    }

Fragment with top-level union type.

I get an error when trying to define a fragment as a top-level node in a fragment. Let's say I have a school query and then I define in a fragment for the different classes

// school query
school {
  id
  name
  classes {
    __typename
    ... Classes_fragment
  }
}


// class fragment
fragment Classes_fragment on Classes {
  __typename
  ... on Math {
    room
    hasExam
  }
  ... on English {
    teacherName
  }
}

This should be a valid query/fragment set up in Relay if I am not mistaken. However, I get the error:

ERROR:
Error writing modules:
TypesTransformer.No_extractable_operations_found,19

It looks like it has something todo with defining both __typename in the query and the fragment. ReasonRelay enforces to define a __typename in the query. If I remove the __typename from the fragment like so:

// class fragment
fragment Classes_fragment on Classes {
  ... on Math {
    room
    hasExam
  }
  ... on English {
    teacherName
  }
}

then relay-compiler does compile with out errors but it does not generate variants for the fragment types as I suspected. It will instead define type like:

// actual 
type fragment = {
   room: string,
   hasExam: bool,
   teacherName: string
   ...
}

// exptected
type fragment_math = {
  room: string,
  hasExam: bool
};
type fragment_english = {
  teacherName: string
};
type fragment = [
  | `Math(fragment_math)
  | `English(fragment_english)
  | `UnselectedUnitonMember(string)
]

Is my setup wrong in some way or is this a real issue?

[RFC] Improving DX of Js.Nullable.t

As outlined in https://reason-relay-documentation.zth.now.sh/docs/quirks-of-reason-relay#nullability, we for various reasons need to model all nullable fields as Js.Nullable.t. This means that you'll end up switching a lot, and also doing someValue |> Js.Nullable.toOption, which isn't a great DX. I thought those interested could discuss possible improvments to the nullability situation here.

Propsal: Vendor a modified version of get_in PPX.

https://github.com/jaredly/get_in_ppx get_in PPX is a PPX for dealing with the same issues as outlined here, but for pure option values. We could probably vendor and modify it into get_nullable, which might turn this situation:

let anotherNestedField = switch (data##someField |> Js.Nullable.toOption) {
| Some(someField) =>
  switch (someField##someNestedField |> Js.Nullable.toOption) {
  | Some(someNestedField) =>
    someNestedField##anotherNestedField |> Js.Nullable.toOption
  | None => None
  }
| None => None
};

...into something like this:

let anotherNestedField = [%get_nullable data##someField##?someNestedField##?anotherNestedField];

Thoughts and comments are much welcome.

Case-sensitive filename cause errors with operation name

Given I have a file tempTest.re (notice the lowercase prefix) with this operation:

module WhateverThisIsQuery = [%relay.query
  {|
     query TempTest_WhateverThisIsQuery($blah: ID!) {
    gitHub {
      node(id: $blah) {
        __typename
        ... on GitHubRepository {
          nameWithOwner
        }
      }
    }
  }
|}
];

Because of the capitalization mismatch, I'll get this error from the reason-relay compiler:

Watching for changes to re...
ERROR:
Parse error: Error: RelayFindGraphQLTags: Operation names in graphql tags must be prefixed with the module name and end in "Mutation", "Query", or "Subscription". Got `TempTest_WhateverThisIsQuery` in module `tempTest`. in "components/tempTest.re"

If I then change the operation name to make the reason-relay compiler happy, then bucklescript will give me this error:

  We've found a bug for you!
  /Users/s/src/egghead/egghead-static/src/components/tempTest.re 112:3-123:2

  110 β”‚
  111 β”‚ module WhateverThisIsQuery = [%relay.query
  112 β”‚   {|
  113 β”‚      query tempTest_WhateverThisIsQuery($blah: ID!) {
    . β”‚ ...
  122 β”‚   }
  123 β”‚ |}
  124 β”‚ ];
  125 β”‚

  The module or file tempTest_WhateverThisIsQuery_graphql can't be found.

Update on Hasura Relay Compatibility

The docs on Using with Hasura mention that:

As of now, Hasura does not provide the primitives needed in the schema for Relay to fully work with it.

As of v1.3.0-beta.1, Hasura is relay compliant.
Source: The issue page

I've tested this on my own machine. I'm planning to use reason-relay with Hasura. If it helps, I can add some docs on how to set up Hasura with Reason-Relay.

Just informing here. Feel free to delete this issue :)

[feature] Bind way to provide (and create your own) handlerProvider

handlerProvider is a way of letting you write custom handlers (think middlewares that run before a specific field is merged into the store, allowing you to change anything in the store as a field is added or updated in a centralized place). This is done by defining a handler and then referencing that on a field using the @__clientField(handle: String!) directive.

The magic @connection does is implemented internally by providing a handler for instance.

There should be bindings and docs for this.

React / Relay version update (experimental)

I just updated my project to and everything seems to work fine, just wanted to let you know and feel free to delete this issue:

"react": "0.0.0-experimental-e5d06e34b",
 "react-dom": "0.0.0-experimental-e5d06e34b",
 "react-relay": "0.0.0-experimental-895a6fe0",

and relay 9.1.0

"relay-runtime": "9.1.0",
"babel-plugin-relay": "9.1.0",
"relay-compiler": "9.1.0",
"relay-config": "9.1.0",

Why is `getFragmentRefs()` a function

I am working on integrating reason-relay with NextJs and I am having problems with the hydration. NextJs server injects data fetched into a script tag (NEXT_DATA) on the page which is then used on the client-side.
The problem with this is that we can't hydrate the function getFragmentRefs since this is just text data. But is there a reason why getFragmentRefs is a function and not just a string identifier?

I would be glad to submit a PR for changing this from a function to string if that is possible. It would be good to get some pointers for where to get started.

Here is an example project: vercel/next.js#12080

reson-relay-compiler not working for [email protected]+

reason-relay-compiler references bs-platform/lib/js/block.js. A file that was deleted in version 8.0.0+ of bs-platform.

When trying to use the compiler with yarn run reason-relay-compile, it outputs the following error:

yarn run v1.17.3
$ reason-relay-compiler
Error: Cannot find module 'bs-platform/lib/js/block.js'
Require stack:
- /my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js
- /my-project/node_modules/relay-compiler/bin/relay-compiler
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1030:15)
    at Function.Module._load (internal/modules/cjs/loader.js:899:27)
    at Module.require (internal/modules/cjs/loader.js:1090:19)
    at require (internal/modules/cjs/helpers.js:75:18)
    at Object.<anonymous> (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:1023)
    at r (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:124)
    at Object.<anonymous> (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:1141723)
    at r (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:124)
    at Object.<anonymous> (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:1141087)
    at r (/my-project/node_modules/reason-relay/language-plugin/reason-relay-language-plugin.js:1:124)

Reproducing the issue

simplified files to reproduce the issue:

package.json

{
  "name": "test",
  "version": "0.1.0",
  "scripts": {
    "relay": "reason-relay-compiler"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "graphql": "^14.6.0",
    "reason-relay": "^0.9.2",
    "relay-compiler": "10.0.0",
    "relay-config": "10.0.0",
    "relay-runtime": "10.0.0"
  },
  "devDependencies": {
    "bs-platform": "^8.0.3"
  }
}

relay.config.js

// relay.config.js
module.exports = {
  src: ".",
  schema: "./schema.graphql",
  artifactDirectory: "./__generated__"
};

schema.graphql (it can be empty, included for completeness)

schema {
  query: RootQueryType
}

interface Node {
  "The id of the object."
  id: ID!
}

type RootQueryType {
  node(
    "The id of an object."
    id: ID!
  ): Node
}

with all those files in a directory, run

$ yarn install
$ yarn run reason-relay-compiler

Use open poly variant to cover the FUTURE case

Right now the SchemaAsset generated file uses a marker to signify that additional values could be added to the schema later so it is unsafe to exhaustively match. Could we instead use an open poly variant like so:

type t('a) = [> | `Case1 | `Case2 ] as 'a;`

[feature] Bind missingFieldsHandler

You can teach Relay how to resolve things locally in your schema by giving it a missingFieldsHandler. Say that you have a top level field called userById(id: ID!): User and one for userByEmail(email: String!): User. These return the same user given the same id/email, but there's no way for Relay to know that from just looking at your schema. With missingFieldsHandler, you can teach Relay how to look for an existing User in the cache for both of those fields. And missingFieldsHandler extend well beyond that, allowing for some pretty cool use cases.

This should have bindings and be documented.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.