Create-react-app: Using create react app with Graphql

Created on 29 Dec 2016  路  14Comments  路  Source: facebook/create-react-app

Hello,

I'll start new project with new react app, new api. And I love using create-react-app and Graphql. So how can i start with both create-react-app and graphql on my project?

Thanks!

Most helpful comment

Just a heads up: you can use react-app-rewired to configure graphql-tag to load .graphql files without ejecting from CRA. (https://github.com/timarney/react-app-rewired)

I created a package for use with react-app-rewire to make configuration easy:
https://github.com/tkvw/react-app-rewire-graphql-tag
Config comes down to:
config-overrides.js:

const rewireGqlTag = require('react-app-rewire-graphql-tag');

module.exports = function override(config, env) {
    config = rewireGqlTag(config,env);    
    return config;
}

All 14 comments

It should be as simple as installing Create React App, then running npm install graphql and using it in your project.

If with "start graphql" you mean how to start its server, you just have to follow its instructions, as CRA only handles the boilerplate with webpack, babel etc for serving/building/testing the frontend part.

:wink:

Hi EnoahNetzach,

How to connect CRA to Graphql? I think we need to use middleware to connect like apollo client.

What do you mean by "connecting"?

As long as GraphQL doesn't require you to add a webpack plugin (I might remember it wrong, but it doesn't), you should be able to just follow the instructions it provides.

If what you mean is a way to run the GraphQL server, again, you have to follow the instructions on how to run graphql-server (or any other server).

Create React App doesn't prevent you from adding other servers apart from the one it provides.

Up to now, we are not able to load plain graphql files with the .graphql extension as such, as explained here, as this requires a 'graphql-tag/loader' entry in the webpack configuration:

There might be a work around by placing those files into .js files and defining the graphql tags inline as you would do it with gql`query something`, but so the editors (or at least my sublime) doesn't recognize the graphql syntax highlighting within the grapqhl tag string.

It'll be nice feature, if a create_react_app can load .graphql files by default.

I've tried to do it like follows, ....

cd node_modules/react_scripts
npm install --save graphql-tag  
vi config/webpack.config.dev.js

Find the default url loader exclusions...

      {
        exclude: [
          /\.html$/,
          /\.(js|jsx)$/,
          /\.css$/,
          /\.json$/,
          /\.svg$/
        ],

Add graphql and gql as exclusion to the default url loader ...

        exclude: [
          /\.html$/,
          /\.(js|jsx)$/,
          /\.css$/,
          /\.json$/,
          /\.svg$/,
          /\.graphql$/,
          /\.gql$/,
        ],

Now adding the graphql-tag/loader for .graphql and .gql files:
find // "file" loader for svg
change to...

      // "file" loader for svg
      {
        test: /\.svg$/,
        loader: 'file',
        query: {
          name: 'static/media/[name].[hash:8].[ext]'
        }
      },
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader'
      }

...but the graphql-tag/loader doesn't resolve and lead to an Error in my App:

Module not found "graphql-tag/loader"

Has anyone an idea how to resolve this?

Ok, resolved..

if I do this instead

yarn eject
yarn add graphql-tag
vi config/webpack.config.dev.js
vi config/webpack.config.prod.js

in the config files default loader I've added the following exclusions:

        exclude: [
          /\.html$/,
          /\.(js|jsx)$/,
          /\.css$/,
          /\.json$/,
          /\.svg$/,
          /\.graphql$/,
          /\.gql$/
        ],

..in the loader if added the graphql-tag/loader:

      {
          test: /\.(graphql|gql)$/,
          include: paths.appSrc,
          exclude: /node_modules/,
          loader: 'graphql-tag/loader'
      }

So I can import my graphql files now by....

import gql_query from './_query.graphql';
...
const ContainerWithMutations = compose(
  graphql(gql_query, {
    options: ({ selected }) => ({
      variables: { processId: selected},
    })
  })
)(Container);

Closing since we don't currently support any special integration with GraphQL without ejecting.

@anhdn there is an alternative version of relay-scripts with embedded Relay support, which does not require to eject: https://github.com/Valentin-Seehausen/create-react-app

i am not sure about this but i got CRA to work with apollo without ejecting. is it not supposed to work? i dont think to have done anything special. server on :4000 client on :3000

@cezarneaga Can you please share your implementation in a fork? Or, at least a Readme in a gist?

it's a bit more, as i have auth there too and i am currently refactoring it but: https://github.com/cezarneaga/apollo-cra-authenticated
also, apollo team has a tutorial too on this

Just a heads up: you can use react-app-rewired to configure graphql-tag to load .graphql files without ejecting from CRA. (https://github.com/timarney/react-app-rewired)

I created a package for use with react-app-rewire to make configuration easy:
https://github.com/tkvw/react-app-rewire-graphql-tag
Config comes down to:
config-overrides.js:

const rewireGqlTag = require('react-app-rewire-graphql-tag');

module.exports = function override(config, env) {
    config = rewireGqlTag(config,env);    
    return config;
}

CRA does not expose a way to extend webpack config. But here is a hack way to do it. The basic idea is to change the webpack config file in node_modules/react-scripts/config/ by

  1. move the node_modules/react-scripts/config/webpack.config.dev.js to node_modules/react-scripts/config/webpack.config.dev.origin.js,
  2. create a custom config that export function webpackConfig => webpackConfig in app config/webpack.config.dev.js
  3. create a node_modules/react-scripts/config/webpack.config.dev.js which content is simply import webpack.config.dev.origin.js and pass it through config/webpack.config.dev.js, and export the result.

So I wrote a simple extentConfig helper to do above operation
app/scripts/lib/extendConfig.js

const fs = require('fs')
const fse = require('fs-extra')
const path = require('path')

const appDirectory = fs.realpathSync(process.cwd())
function resolveApp(relativePath) {
  return path.resolve(appDirectory, relativePath)
}

function getFileStat(absolutePath) {
  try {
    return fs.statSync(absolutePath)
  } catch (e) {
    if (e.code === 'ENOENT') return null
    throw e
  }
}

function getConfig(webpackEnv, type) {
  if (!type) return resolveApp('./node_modules/react-scripts/config/webpack.config.' + webpackEnv + '.js')
  if (type === 'my') return resolveApp('./config/webpack.config.' + webpackEnv + '.js')

  return resolveApp('./node_modules/react-scripts/config/webpack.config.' + webpackEnv + '.' + type + '.js')
}

function extendConfig(webpackEnv, callback) {
  if (webpackEnv !== 'dev' && webpackEnv !== 'prod') {
    console.error('Only dev and prod is supported')
    return
  }

  return (
    moveAsOrigin(webpackEnv)
      .then(() => createWebpackConfig(webpackEnv))
      .then(() => callback())
  )
}

function moveAsOrigin(webpackEnv) {
  const webpackConfig = getConfig(webpackEnv)
  const webpackConfigOrigin = getConfig(webpackEnv, 'origin')
  const originStat = getFileStat(webpackConfigOrigin)

  if (!originStat) {
    return fse.move(webpackConfig, webpackConfigOrigin)
  } else {
    return Promise.resolve()
  }
}

function createWebpackConfig(webpackEnv) {
  const webpackConfig = getConfig(webpackEnv)
  const stat = getFileStat(webpackConfig)
  if (!stat) {
    return fse.outputFile(
      webpackConfig,
      `const config = require('./webpack.config.${webpackEnv}.origin')
const customConfigFunc = require('../../../config/webpack.config.${webpackEnv}')

module.exports = customConfigFunc(config)  
    `
    )
  }
}

module.exports = extendConfig

app/scripts/start.js

const extendConfig = require('./lib/extendConfig')

extendConfig('dev', () => {
  const start = require('react-scripts/scripts/start')
})

config/webpack.config.dev.js

module.exports = config => {
  const oneOfRules = config.module.rules.find(r => !!r.oneOf)
  const fileLoaderRule = oneOfRules.oneOf.find(r => r.loader && r.loader.includes('/file-loader/'))

  fileLoaderRule.exclude.push(/\.(graphql|gql)$/)

  config.module.rules.push({
    test: /\.(graphql|gql)$/,
    exclude: /node_modules/,
    loader: 'graphql-tag/loader',
  })
  return config
}

And it is similiar to extend webpack.config.prod.js by extendConfig('prod').

With this hack way, the all of packages, config and scripts ejected go away, and easy to upgrade along with react-scripts.

And in this way, you can extend any webpack config as you want.

@shendepu thank you so much for this workaround. I think this is the way to go for tweaking the settings of CRA ATM, not just for the sake of using GraphQL.

Yet another way is to use patch-package, edit the configs as mentioned by @tobkle and save the patch.

Here is the result of my patch against [email protected].

--- a/node_modules/react-scripts/config/webpack.config.dev.js
+++ b/node_modules/react-scripts/config/webpack.config.dev.js
@@ -213,6 +213,13 @@ module.exports = {
               },
             ],
           },
+          {
+            test: /\.(graphql|gql)$/,
+            include: paths.appSrc,
+            exclude: /node_modules/,
+            loader: 'graphql-tag/loader'
+          },
           // "file" loader makes sure those assets get served by WebpackDevServer.
           // When you `import` an asset, you get its (virtual) filename.
           // In production, they would get copied to the `build` folder.
@@ -223,7 +230,16 @@ module.exports = {
             // its runtime that would otherwise processed through "file" loader.
             // Also exclude `html` and `json` extensions so they get processed
             // by webpacks internal loaders.
-            exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
+            // exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
+            exclude: [
+              /\.html$/,
+              /\.(js|jsx|mjs)$/,
+              // /\.css$/,
+              /\.json$/,
+              // /\.svg$/,
+              /\.graphql$/,
+              /\.gql$/
+            ],
             loader: require.resolve('file-loader'),
             options: {
               name: 'static/media/[name].[hash:8].[ext]',
--- a/node_modules/react-scripts/config/webpack.config.prod.js
+++ b/node_modules/react-scripts/config/webpack.config.prod.js
@@ -239,6 +239,13 @@ module.exports = {
             ),
             // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
           },
+          {
+            test: /\.(graphql|gql)$/,
+            include: paths.appSrc,
+            exclude: /node_modules/,
+            loader: 'graphql-tag/loader'
+          },
           // "file" loader makes sure assets end up in the `build` folder.
           // When you `import` an asset, you get its filename.
           // This loader doesn't use a "test" so it will catch all modules
@@ -249,7 +256,16 @@ module.exports = {
             // it's runtime that would otherwise processed through "file" loader.
             // Also exclude `html` and `json` extensions so they get processed
             // by webpacks internal loaders.
-            exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
+            // exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
+            exclude: [
+              /\.html$/,
+              /\.(js|jsx|mjs)$/,
+              // /\.css$/,
+              /\.json$/,
+              // /\.svg$/,
+              /\.graphql$/,
+              /\.gql$/
+            ],
             options: {
               name: 'static/media/[name].[hash:8].[ext]',
             },
Was this page helpful?
0 / 5 - 0 ratings

Related issues

fson picture fson  路  3Comments

onelson picture onelson  路  3Comments

DaveLindberg picture DaveLindberg  路  3Comments

oltsa picture oltsa  路  3Comments

stopachka picture stopachka  路  3Comments