Create-react-app: Use Relay

Created on 18 Aug 2016  路  53Comments  路  Source: facebook/create-react-app

Hi, is it possible to use Relay with create-react-app without ejecting project and messing with webpack configuration?

I get the following error in the console:

invariant.js:38 Uncaught Invariant Violation: RelayQL: Unexpected invocation at runtime. Either the Babel transform was not set up, or it failed to identify this call site. Make sure it is being used verbatim as `Relay.QL`.

I was looking for solution and it seems to be problem with lack of Relay.QL transpiler in the Babel configuration. But all these projects were not created with create-react-app cli. From what I know it's not possible to mess with webpack configuration until I eject a project.

proposal

Most helpful comment

We could detect if you have relay in package.json and add its Babel plugin. Since it鈥檚 part of the Facebook stack it makes sense to me to provide an integrated experience (as long as it works great).

All 53 comments

You can't add more transpilation steps without ejecting, so right now I think the answer is no. It would be nice if you could use Relay without ejecting, somehow, though.

Ok thanks for information. Do you know how to add such configuration to ejected project? Do you have some link to tutorial/guide how to do it?

We could detect if you have relay in package.json and add its Babel plugin. Since it鈥檚 part of the Facebook stack it makes sense to me to provide an integrated experience (as long as it works great).

@gaearon that would be great!

In the meantime does anyone know how to configure webpack after ejecting to work with Relay?

I think the solution @gaearon would make a lot of sense!

@jagi in the meanwhile you can eject and use a setup like in this example which uses babel-plugin-react-relay. It uses Relay's babel-relay-plugin internally but makes usage more convenient and extends its functionality. For example you no longer need to have a build/babelRelayPlugin.js script.

Let me know if you need any help setting it up.

@gaearon thinking about writing a pull request for your idea above "detect if you have relay in package.json and add its Babel plugin"

Question though. The babelRelayPlugin needs a reference to a schema.json file (example here https://github.com/relayjs/relay-starter-kit/blob/master/build/babelRelayPlugin.js). This file is typically generated by a script that introspects a graphql server and saves the output to schema.json. What is the the cleanest way to handle this in a pull request to add realyjs support to create-react-app?

More explanation is found here https://facebook.github.io/relay/docs/guides-babel-plugin.html#content.

We could configure babelRelayPlugin to look for my-app/schema.json and write instructions in the README for the user on how he/she can create schema.json.

Additionally we could provide an npm run updateRelaySchema <url-to-your-graphql-server-here> which would introspect the graphql server and create my-app/schema.json.

Does this sound reasonable? Other ideas?

@saintsjd I think it makes a lot of sense in order to simplify this process further. What do you think of a workflow like proposed here: https://github.com/graphcool/graphql-config

@schickling Nice suggestion! I will try it.

cc @josephsavona, I expect the setup will be different in Relay 2?
Since it is preferred for new projects, is there anything we can do on our side to integrate better?

The setup in Relay2 will be basically the same - a single babel plugin that requires access to the schema. So it seems like implementing support would involve:

  • A way to detect if the user wants to use Relay (sounds like checking for react-relay in package.json would work)
  • A way to configure the GraphQL endpoint. graphql-config has potential here, though it seems best to advocate a single canonical place to store graphql config data. The default approach of graphql-config, an environment variable, doesn't seem ideal here because it leaves no room for specifying authentication data. Since this is a React project, using the package.json variation seems sensible.
  • Actually enabling the plugin with the schema. https://github.com/graphcool/babel-plugin-react-relay has potential here, though I think we should consider just changing the default Relay plugin to have the same (more convenient) behavior. babel-plugin-react-relay really is more convenient to use, but because it wraps the official plugin it means we have to upgrade two packages in coordination on every release, one of which we don't own.

So overall, I'm inclined to say that we should change babel-relay-plugin to support the package.json configuration option of graphql-config, and use that for create-react-app.

@josephsavona This sounds like a good plan for babel-relay-plugin. Since you brought up the topic of releases, now I'm wondering how we could determine which version of babel-relay-plugin to use in create-react-app? When the user updates react-relay, also babel-relay-plugin needs to be updated. The Babel plugins are dependencies of react-scripts and therefore not controlled by the user.

Since we can look up the react-relay package version from package.json and babel-relay-plugin seems to always track its version, maybe we could use that information to choose the right plugin version. But depending on many versions babel-relay-plugindoesn't seem like a great idea. We could also always support only the latest version, but that would mean that each time we change the supported Relay version, it would be a breaking change to react-scripts and the users need to update Relay to use it?

@fson Good points - I hadn't considered the plugin versioning aspect. Ideally the plugin would rarely have to change. Instead, the plugin can have a peer dependency on a separate module - the Relay2 compiler - that it delegates to. Then the developer can be in charge of updating the Relay and the compiler versions together (and Relay can have a peer dependency on the compiler to enforce compatible versions), and create-react-app can depend on the (infrequently changing) Relay plugin.

I was studying the source for babel-relay-plugin a bit. If I am not mistaken, getBabelRelayPlugin accepts either a schema object or a function for its first argument.

See https://github.com/facebook/relay/blob/master/scripts/babel-relay-plugin/src/getBabelRelayPlugin.js#L35 and https://github.com/facebook/relay/blob/master/scripts/babel-relay-plugin/src/getBabelRelayPlugin.js#L245-L247

Perhaps we don't need to change babel-relay-plugin? Maybe we can create a plugins/babelRelayPlugin.js file like this:

var getbabelRelayPlugin = require('babel-relay-plugin');

var getGraphQLSchemaFromPackageJson = function() {
   // parse package.json and get graphql settings like graphql-config does
   // use fetch and introspectionQuery to get schema 
   // return schema object
}

module.exports = getbabelRelayPlugin(getGraphQLSchemaFromPackageJson);

Then enable the plugin if the user wants relay (react-relay is in package json):

{
  "passPerPreset": true,
  "presets": [
    {"plugins": ["./plugins/babelRelayPlugin"]},
    "react-native"
  ]
}

I haven't tried this yet.

@saintsjd yeah, that's roughly how it would be implemented, I think. The plugin changes are more due to the need to make release coordination easier.

I am working on a pull request for this. But, instructions in CONTRIBUTING.md in the section Setting Up a Local Copy are not working. Any ideas?

Here is what I am doing...

git clone https://github.com/facebookincubator/create-react-app
cd create-react-app
npm install
cd global-cli
npm install
cd .. 
npm run create-react-app my-app

This gives an error

``````
Installing packages. This might take a couple minutes.
Installing react-scripts from npm...

npm ERR! addLocal Could not install /Users/jonsaints/Dev/create-react-app/react-scripts-0.4.0.tgz
npm ERR! Darwin 15.3.0
npm ERR! argv "/Users/jonsaints/.nvm/versions/node/v4.4.7/bin/node" "/Users/jonsaints/.nvm/versions/node/v4.4.7/bin/npm" "install" "--save-dev" "--save-exact" "/Users/jonsaints/Dev/create-react-app/react-scripts-0.4.0.tgz"
npm ERR! node v4.4.7
npm ERR! npm v2.15.8
npm ERR! path /Users/jonsaints/Dev/create-react-app/react-scripts-0.4.0.tgz
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall open

npm ERR! enoent ENOENT: no such file or directory, open '/Users/jonsaints/Dev/create-react-app/react-scripts-0.4.0.tgz'
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! Please include the following file with any support request:
npm ERR! /Users/jonsaints/Dev/create-react-app/my-app/npm-debug.log
npm install --save-dev --save-exact /Users/jonsaints/Dev/create-react-app/react-scripts-0.4.0.tgz failed```
``````

I think I found a workaround.

diff --git a/tasks/clean_pack.sh b/tasks/clean_pack.sh
index 2d945d5..0eb1ba1 100755
--- a/tasks/clean_pack.sh
+++ b/tasks/clean_pack.sh
@@ -68,7 +68,7 @@ packname=`npm pack`

 # Now we can copy the package back.
 cd ..
-cp -f $clean_path/$packname ./
+cp -f $clean_path/$packname $initial_path/../
 cleanup

Oops, sorry, I broke this.

@saintsjd Should be fixed on master with #566.

Thanks a ton @gaearon! Confirmed. The fix works great on my end.

I'm helping @saintsjd with this pull request. We have the schema being generated successfully like this:

var getGraphQLSchemaFromPackageJson = function() {
   // parse package.json and get graphql settings like graphql-config does
   // use fetch and introspectionQuery to get schema 
   // return schema object
}

and we also have the babelRelayPlugin enabled with, I think, the right settings. But for some reason we are getting a failure to compile with webpack. The specific error is:

Failed to compile.

Error in ./src/index.js
Module build failed: AssertionError: missing path
    at Array.map (native)
    at Array.map (native)
 @ multi main

When we feed the babelRelayPlugin a string of Relay.QL it works fine but something in the build process is keeping babelRelayPlugin from receiving the Relay.QL fragments... any ideas why this might be?

@maxwell-oroark Hmm, that error isn't very helpful in narrowing down the problem. I don't recall (and can't find with github search) a "missing path" assertion in the plugin code, so it looks like maybe the error is coming from somewhere else. Does the error occur simply by enabling the plugin, or does enabling the plugin work so long as there are no Relay.QL tags?

@josephsavona The error goes away when Relay.QL tags are removed.

@maxwell-oroark strange. Unfortunately the error doesn't give enough info that I can give much advice. If you can possibly run the code with a debugger that would be ideal, otherwise this may require digging in and adding manual console.log statements in order to narrow things down. Maybe start with some log statements in the Relay plugin just to see if it is initialized and at least called.

@maxwell-oroark and I have made progress. We have a working version of create-react-app that enables relay support when you have react-relay in your package.json file and have an environment variable GRAPHQL_URL.

We don't have error messages or nice instructions in the readme yet. We are working on those before making the pull request.

If you want to take a look you can see progress here: https://github.com/maxwell-oroark/create-react-app/tree/relay-support

Merged with master. We now have eject support, nicer errors, and a great README.

Note the environment variable is changed to REACT_APP_ GRAPHQL_URL

https://github.com/facebookincubator/create-react-app/issues/662

I talked with @schickling and we would like come up with a standard approach for specifying the GraphQL schema/endpoint that can also be adopted by create-react-app. I think one potential issue with the current package.json configuration suggested above is that it doesn't allow using a different server for production and development. I've opened an issue in graphql-config to discuss solutions for that: https://github.com/graphcool/graphql-config/issues/9

@josephsavona do you have some ideas about this from Relay point of view? I think it would be fantastic, if we can come up with a GraphQL configuration approach for that can be adopted in CRA, but also in the underlying tools like babel-relay-plugin eventually. I commented in the pull request about some difficulties in fetching the schema in a Babel plugin 鈥撀爄s that something that you could see changing with Relay 2 though?

I think it would be fantastic, if we can come up with a GraphQL configuration approach for that can be adopted in CRA, but also in the underlying tools like babel-relay-plugin eventually

@fson I completely agree; create-react-app shouldn't have to change very much to support Relay. To keep things modular, the logic for fetching a schema should ideally be in the plugin itself. As I mentioned above, a possible approach would be:

  • create-react-app depends on a single module, babel-plugin-<relay> (name tbd) that rarely changes.
  • babel-plugin-<relay> has a _peer_ dependency on the actual Relay compiler and is a thin wrapper over it; the plugin's only job is to find GraphQL tagged templates and convert them to code by calling the compiler.
  • The compiler knows how to load a schema and transform GraphQL text to code.
  • react-relay has a peer dependency on the compiler in order to ensure that a compatible compiler version is being used.

With this approach, the changes to create-react-app should be relatively minimal and it can be upgraded independently of react-relay and the compiler.

@fson We (Relay team) don't have the bandwidth to make the changes I proposed in the currently plugin, but we can review PRs and provide feedback.

As far as Relay 2 goes, we designed it following the approach I described above, so the main thing we'll have to figure out in OSS is how to load the schema in order to transform queries to code.

@josephsavona I would be interested in helping to land this in the Relay plugin!

graphql-config is a great starting point for how to find the schema, so we can probably come up with a good way to configure the plugin by building on the ideas there.

One thing I'm still unsure about is how to implement schema fetching inside a Babel plugin in a nice way 鈥撀燼s far as I know Babel plugins must execute synchronously, whereas fetching the schema is an asynchronous operation (unless we make some horrible hacks to stop the event loop in Node.js). So this would be a separate step, but how would that work in the suggested setup, with the Babel plugin depending on the compiler?

In general, it seems reasonable to load the schema first (async preparation), then transform JS files (sync).

@josephsavona @fson that's pretty much was babel-plugin-react-relay does.

Any idea how to address changes in the schema without restarting the used build tool?

Tried to use Relay with ejected app and hit a bug with stalled graphql schema. Relay continued to use old schema even after dev build restart. Issue turned out to be with caching enabled around this lines https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/webpack.config.dev.js#L128

Hope it will help those looking for answer in this thread.

To expand on @vdanchenkov comment, consider using a timestamp to only expire the cache when the schema.json file changes. This is working great for me:

query: {
  cacheDirectory: true,
  cacheIdentifier: fs.statSync('config/schema.json').mtime
}

@dphaener Can you elaborate on your solutions, maybe paste a fuller example?

Should we use relay-fullstack (https://github.com/lvarayut/relay-fullstack) instead if we want to use React with Relay? Lots of posts in this thread but I am still not sure how to use Relay with create-react-app. (I wish github had a tag for marking the correct answer similar to Stack Overflow.)

@nikhilag It is as simple as ejecting after install and editing your package.json. Of course you need to install the necessary Relay packages as you would usually do.

"babel": {
  "plugins": [
    "RELATIVE_PATH_TO/babelRelayPlugin"
  ],
  "presets": [
    "react-app"
  ]
},

@ivosabev @nikhilag Sorry for the delay here. I've actually modified how I am doing this, so here's a more complete example.

Instead of relying on the timestamp of the 'schema.json', which would require that you manually update that file and restart the server, I'm using nodemon to run the create-react-app server. But I'm only watching files that would change my schema. You'll notice that I'm watching .rb files, because I'm using a Ruby GraphQL server, but the same would apply to a node server.

Here's my nodemon.json configuration file:

{
  "restartable": "rs",
  "verbose": true,
  "execMap": {
    "rb": "ruby"
  },
  "watch": [
    "../app/graphql/**/*"
  ],
  "events": {
    "restart": "touch .schema_stamp && osascript -e 'display notification \"App restarted due to:\n'$FILENAME'\" with title \"nodemon\"'"
  },
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "rb"
}

I'm using an osascript to send a notification in my Mac, but it's not necessary. The important thing there is that we are touching the .schema_stamp file, which you will see later.

Here is the relevant part of my webpack config:

loaders: [
  {
    test: /\.(js|jsx)$/,
    include: paths.appSrc,
    loader: 'babel',
    query: {
      cacheDirectory: true,
      cacheIdentifier: fs.statSync('.schema_stamp').mtime
    }
  },
]

You'll notice here that I am now watching the timestamp of the .schema_stamp file that is referred to in nodemon.json file. This will invalidate the cache, and cause the new schema to be reloaded when nodemon restarts.

I'm also using babel-plugin-react-relay here, so configuring where to get your Relay schema can be done in several ways, as outlined here: https://github.com/graphcool/graphql-config.

In my case, it's in my package.json file:

  "graphql": {
    "request": {
      "url": "http://localhost:7000/graphql",
      "headers": {
        "Authorization": "Bearer <my_special_introspection_token>"
      }
    }
  }

That's pretty much all I had to do to get Relay going with Create React App. Now when I start my server, I just use nodemon to start the server script instead of just node and it works perfectly(mostly)!

P.S. This was all done after ejecting the app, BTW.

If you want to stay unejected, you can also use https://github.com/Valentin-Seehausen/create-react-app.

It is basically a wrapper for the Relay Support PR https://github.com/facebookincubator/create-react-app/pull/662, that was canceled. Feedback welcome, would be cool, if this made things easier.

Closing as we don't plan this to work out of the box, but I'm glad that there's a fork supporting it!

@gaearon why the change of heart?

This is quite unfortunate for Relay, that even FB's own tools don't support them, since it is just a matter of letting the user extend the Babel configuration.

I'm pretty sure that with the new version of Relay the tooling will become a lot more flexible. This should make this possible again without needing to fork CRA.

There are a bunch of people at Facebook interested in making create-react-app work more smoothly with Relay. The tricky thing right now is that there are a ton of improvements on Relay coming, and we're not sure whether/how that will change the getting-started experience. So I think we should wait for the next big Relay release and then take another hard look at this and see how we can make the Relay <-> React experience better.

since it is just a matter of letting the user extend the Babel configuration

We may come back to this, but now is not a good time, especially with all the ES modules churn (we're in the process of switching to Webpack 2, it has bugs, etc). We want to retain full control over Babel configuration to allow a guaranteed stable experience for all users. We may explore making Babel configurable once the dust settles a little bit.

@gaearon how about reopening this and leaving it at the proposal state and documenting that this will not be considered until Relay 2 lands, so that we won't have another issue opened?

I think a simple solution is needed here which works seamlessly with create-react-app without requiring to eject or get react-scripts from somewhere else. Given that create-react-app has gained so much popularity, I think people won't use Relay just because it is not straightforward to use it with create-react-app. I had to switch to Apollo (which is an excellent client as well) because I was running into issues with setting up Relay and I really didn't want to eject.

As mentioned above, Relay itself is changing significantly (Relay Classic => Relay Modern), and now is not a very good time to invest into a solution like this. I don鈥檛 think it makes sense to reopen because the churn is still there. Happy to reopen in half a year or so, but another issue might work better at that point.

Agree with @gaearon - let's reevaluate this once Relay Modern is officially open source. It would be great to either support Relay Modern officially within create-react-app, or to make it very easy for projects to build upon create-react-app (so that we could build a create-relay-app that tracked create-react-app and configured Relay).

@gaearon @josephsavona Now that Relay Modern is out, would love to see Create React App add support without ejecting; looks like there is an open issue for this: https://github.com/facebookincubator/create-react-app/issues/2001.

@trevordmiller The idea of create-react-app is meant as an easy way to get started with React; it isn't meant to cover every case. As the readme notes, "the feature set is intentionally limited" and the eject feature exists to support power users such as yourself who need additional configuration. I would hope that the need to eject wouldn't hold people back from trying Relay Modern (or, for that matter, from trying Apollo client and its Babel plugin!)

That said, it would be great to come up with some solution to make it easier for people using CRA to try out Relay Modern or any other framework that required an extra Babel plugin. @gaearon, do you have any thoughts on this?

@josephsavona Thanks for your response. I understand and agree with the premise. But I'm wondering if a conditional feature could be added as has been done with yarn (when you have a yarn.lock it uses yarn instead of npm), flow (when you have a .flowconfig), Jest, etc. I've found great value in not ejecting so that I get all of the underlying react-scripts updates essentially for free. I understand not every tool can be conditionally supported, but since Relay is a facebook product like Yarn and Jest, to me it makes sense to have some sort of conditional support as the other facebook tools have in CRA. Anyways, just a thought :)

I did try out Relay Modern and am enjoying it. Thanks for the great work.

It's such a shame since I really want to try Relay Modern. Setting up Apollo in create-react-app was painless for me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xgqfrms-GitHub picture xgqfrms-GitHub  路  3Comments

wereHamster picture wereHamster  路  3Comments

fson picture fson  路  3Comments

oltsa picture oltsa  路  3Comments

jnachtigall picture jnachtigall  路  3Comments