10

I've been working with reactjs a fair bit and thought I'd start playing around with graphql and relay. I've hit an error that I can't get to the bottom of, so I wondered if anyone on here may have encountered the same. Any pointers or ideas would be welcomed.

The error I am getting is

Error: GraphQL validation/transform error ``Object  has no method 'find'`` in file `/Users/chris/Documents/App/site/js/main.js`.

So I have a GraphQL server running and everything works fine I can query the backend database (I'm using mongodb) and the following query produces the correct results through the graphql ui

{
  store{
    items{
        _id
        title
        sub
    }
  }
}

produces the following output which is correct

{
  "data": {
    "store": {
      "items": [
        {
          "_id": "56e2c71d150040c491c73b26",
          "title": "Test Item One",
          "sub": "sub title here"
        }
      ]
    }
  }
}

So I think its safe to assume the the graphql server is fine and the problem must be with my implementation of relay or the babelRelayPlugin.js

The following is the main.js file with the render method simplified.

import React from 'react';
import Relay from 'react-relay';


class Main extends React.Component {

    render(){
        return (
            <div>
                <p>Test</p>
            </div>
        );
    }
}

Main = Relay.createContainer(Main, {
    fragments:{
        store: () => Relay.QL`
            fragment on Store {
                items {
                    title,
                }
              }
           `
    }
});

export default Main;

The following is the App.js file

import React from 'react';
import ReactDOM from 'react-dom';
import Relay from 'react-relay';
import Main from './main'

//React.render(<Main />, document.getElementById('react-main-mount'));

class HomeRoute extends Relay.Route {
    static routeName = 'Home';
    static queries = {
        store:(Component) => Relay.QL`
        query MainQuery {
            store { ${Component.getFragment('store')}}
        }`
    }
};

ReactDOM.render(<Relay.RootContainer 
                    Component={Main} 
                    route={new HomeRoute()} 
                />, 
                document.getElementById('react-main-mount')
               );

For completeness here is the schema.js I'm using.

import { GraphQLSchema, 
         GraphQLObjectType,
         GraphQLList,
         GraphQLInt,
         GraphQLString
       } from 'graphql';

// Schema allows us to use node feature to link the schema to the database
let Schema = (db) => {
let store = {};

let storeType = new GraphQLObjectType({
    name:'Store',
    fields:() => ({
        items: {
            type: new GraphQLList(itemType),
            resolve:() => db.collection("Item").find({}).toArray()
        }
    })
});

let itemType = new GraphQLObjectType({
    name: 'Item',
    fields:() =>({
        _id:{type: GraphQLString},
        title: {type: GraphQLString},
        sub:{type:GraphQLString},
        type:{type:GraphQLString},
        comments:{type:GraphQLInt}
    })
});

let schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name:'Query',
        fields: () => ({
            store: {
                type: storeType,
                resolve: () => store
            }

        })
    }),
});
    return schema;
};
export default Schema;

which is converted to JSON on by the server shown here

let app = express();
app.use(express.static('site')); // public folder

console.log("Starting Server");
//mongo url is the server 
(async () => {
    try{
    let db = await MongoClient.connect("mongodb://localhost:27017/App");
    let schema = Schema(db); 

    app.use('/graphql', GraphQLHTTP({
        schema,
        graphiql:true
    }));

  app.listen(3000, () => console.log('listening on port 3000'));  

  // Generate Schema
  let json = await graphql(schema, introspectionQuery); 
  fs.writeFile('./database/schema.json', JSON.stringify(json,null,2), err => {
      if(err) throw err;
      console.log("JSON schema created");
  });
    }catch(e){
        console.log(e);
    }
})();

And the babelRelayPlugin.js is below

var getBabelRelayPlugin = require('babel-relay-plugin');
var schemaData = require('./database/schema.json').data;
module.exports = getBabelRelayPlugin(schemaData);

And Webpack.config

module.exports = {
    entry:'./site/js/app.js',
    output:{
        path: __dirname + "/site",
        filename: "bundle.js"
    },
    module:{
        loaders:[
            {test:/\.js$/, exclude:/node_modules/, loader: 'babel-loader',
             query: {presets: ['react', 'es2015', 'stage-0'],
                     plugins: ['./babelRelayPlugin']
                    }
            }
        ]
    }
}

UPDATE: So I've had a little play around and the problem lies in the main.js RelayQL statement

Main = Relay.createContainer(Main, {
        fragments:{
            store: () => Relay.QL`
                fragment on Store {
                    items {
                        title,
                    }
                  }
               `
        }
    });

I commented out the code in app.js and replaced it with the following code as a test to see if basic a basic relay query worked and it did by returning the correct output to the console

ReactDOM.render(<Main />, document.getElementById('react-main-mount'));
console.log(Relay.QL`query store { store{ items {title}}}`);

So as I mentioned above the issue must be I suspect with the really statement in main.js, now the question is why?

For total completeness I have included the full error stack trace below

-- Relay Transform Error -- main --

File:  /Users/chris/Documents/App/site/js/main.js
Error: TypeError: Object  has no method 'find'
    at new RelayQLFragment (/Users/chris/Documents/App/node_modules/babel-relay-plugin/lib/RelayQLAST.js:143:49)
    at RelayQLTransformer.processDocumentText (/Users/chris/Documents/App/node_modules/babel-relay-plugin/lib/RelayQLTransformer.js:156:16)
    at RelayQLTransformer.transform (/Users/chris/Documents/App/node_modules/babel-relay-plugin/lib/RelayQLTransformer.js:67:29)
    at PluginPass.TaggedTemplateExpression (/Users/chris/Documents/App/node_modules/babel-relay-plugin/lib/getBabelRelayPlugin.js:124:36)
    at newFn (/Users/chris/Documents/App/node_modules/babel-traverse/lib/visitors.js:262:19)
    at NodePath._call (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:63:18)
    at NodePath.call (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:47:17)
    at NodePath.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:93:12)
    at TraversalContext.visitQueue (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:146:16)
    at TraversalContext.visitSingle (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:115:19)
    at TraversalContext.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:178:19)
    at Function.traverse.node (/Users/chris/Documents/App/node_modules/babel-traverse/lib/index.js:135:17)
    at NodePath.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:103:22)
    at TraversalContext.visitQueue (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:146:16)
    at TraversalContext.visitMultiple (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:110:17)
    at TraversalContext.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:176:19)
    at Function.traverse.node (/Users/chris/Documents/App/node_modules/babel-traverse/lib/index.js:135:17)
    at NodePath.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:103:22)
    at TraversalContext.visitQueue (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:146:16)
    at TraversalContext.visitSingle (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:115:19)
    at TraversalContext.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:178:19)
    at Function.traverse.node (/Users/chris/Documents/App/node_modules/babel-traverse/lib/index.js:135:17)
    at NodePath.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:103:22)
    at TraversalContext.visitQueue (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:146:16)
    at TraversalContext.visitSingle (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:115:19)
    at TraversalContext.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:178:19)
    at Function.traverse.node (/Users/chris/Documents/App/node_modules/babel-traverse/lib/index.js:135:17)
    at NodePath.visit (/Users/chris/Documents/App/node_modules/babel-traverse/lib/path/context.js:103:22)
    at TraversalContext.visitQueue (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:146:16)
    at TraversalContext.visitMultiple (/Users/chris/Documents/App/node_modules/babel-traverse/lib/context.js:110:17)
jonathancardoso
  • 11,737
  • 7
  • 53
  • 72
Kraken18
  • 663
  • 2
  • 11
  • 25

1 Answers1

1

In your HomeRoute:

class HomeRoute extends Relay.Route {
    static routeName = 'Home';
    static queries = {
        store:(Component) => Relay.QL`
        query MainQuery {
            store { ${Component.getFragment('store')}}
        }`
    }
};

Have you tried just:

    static queries = {
        store:() => Relay.QL`
        query MainQuery {
            store
        }`
    }
nethsix
  • 800
  • 8
  • 17
  • 1
    Good idea to simplify the HomeRoute, (which admittedly I hadn't tried) but unfortunately I still get the same issue, I think the syntax is correct, I think the issue could be with the connection setup itself as running 'webpack -wd' generates the error – Kraken18 Mar 15 '16 at 13:26
  • The error seems to point to the GraphQL schema for Store -> fields -> items -> `resolve:() => db.collection("Item").find({}).toArray()`. Perhaps instead of using .find(), try returning a fixed array of items to see if the problem goes away. – nethsix Mar 15 '16 at 15:07
  • Nope same problem unfortunately. The GraphQL server works which is puzzling the generated JSON looks correct, I may need to re-implement the relay side again after re-installing some modules again. - Cheers for the sugestions – Kraken18 Mar 15 '16 at 15:46
  • I saw your update where you just did `console.log(Relay.QL\`query store { store{ items {title}}}\`);`, which worked! However performing this query is similar to your HomeRoute performing it, and not your Main Relay fragment code doing it, which is what you are comparing to. Try updating the HomeRoute with this same working Relay.QL you used in your console.log. – nethsix Mar 21 '16 at 02:32
  • Alternatively, you could try stuffing your existing HomeRoute query into console.log as: `console.log(Relay.QL\`query MainQuery { store { ${Component.getFragment('store') } } }\`)`. Can this work? – nethsix Mar 21 '16 at 02:36
  • I still seem to get the same error, however I located the source code of the tutorial I was using, ran it and noticed I had the same error appear. As I seem to be the only one who has got this error I think there may be something fundamentally wrong with my setup which is causing the issue. – Kraken18 Mar 21 '16 at 11:32