Must contain a query definition - reactjs

Iam new to react-native and appsync,graphql.we are trying to implement an appsync app using react-native when i tried to run its is throwing an error saying
14:16:38: Must contain a query definition.
* null:null in getQueryDefinition
- node_modules/apollo-cache-inmemory/lib/bundle.umd.js:649:64 in diffQueryAgainstStore
- node_modules/apollo-cache-inmemory/lib/bundle.umd.js:559:32 in readQueryFromStore
- node_modules/apollo-cache-inmemory/lib/bundle.umd.js:899:38 in read
- node_modules/apollo-cache-inmemory/lib/bundle.umd.js:992:23 in readQuery
* null:null in update
- node_modules/apollo-client/bundle.umd.js:1609:0 in
- node_modules/apollo-utilities/lib/bundle.umd.js:818:21 in tryFunctionOrLogError
* null:null in
- node_modules/apollo-cache-inmemory/lib/bundle.umd.js:961:22 in performTransaction
- node_modules/apollo-client/bundle.umd.js:1473:0 in markMutationResult
- node_modules/apollo-client/bundle.umd.js:797:20 in next
- node_modules/zen-observable-ts/node_modules/zen-observable/lib/Observable.js:150:3 in notifySubscription
- node_modules/zen-observable-ts/node_modules/zen-observable/lib/Observable.js:195:5 in onNotify
* null:null in next
- node_modules/zen-observable-ts/node_modules/zen-observable/lib/Observable.js:150:3 in notifySubscription
* null:null in flushSubscription
- node_modules/zen-observable-ts/node_modules/zen-observable/lib/Observable.js:190:26 in
* null:null in
- node_modules/promise/setimmediate/core.js:37:14 in tryCallOne
- node_modules/promise/setimmediate/core.js:123:25 in
- ... 10 more stack frames from framework internals
I tried to trace out the error in apollo-cache but i couldnt find one.Am getting this when ever i press send button.
import React, { Component } from 'react';
import { KeyboardAvoidingView, Text, Button, TextInput, StyleSheet, Alert } from 'react-native';
export default class ChatInput extends Component {
constructor(props) {
super(props);
this.userid = props.userid;
this.state = {
text: ''
}
}
handleSend = () => {
if (this.state.text === '') {
return false;
}
const chat = {
userid: this.userid,
text: this.state.text,
createdAt: new Date().toISOString()
}
this.props.onSubmit(chat);
this.setState({
text: ''
})
this.textInput.clear();
}
render() {
return (
<KeyboardAvoidingView>
<TextInput ref={input => { this.textInput = input }} placeholder="Message.."
onChangeText={(text) => this.setState({text})} onSubmitEditing={this.handleSend}
autoFocus={true} blurOnSubmit={false} returnKeyType="send"></TextInput>
<Button title="Send" onPress={this.handleSend}></Button>
</KeyboardAvoidingView>
);
}
}
const ChatInputData = compose(
graphql(PostMessage, {
options: {
fetchPolicy: 'no-cache'
},props: (props) => ({
onSubmit: chat => {
props.mutate({
variables: chat,
optimisticResponse: {
__typename: 'Mutation',
post: {
__typename: ChatHistory,
id: chat.createdAt,
userid: chat.userid,
text: chat.text,
createdAt: chat.createdAt
}
},
update: (proxy, {data: {post}}) => {
const data = proxy.readQuery({ query: PostMessage });
data.listPostMessages.items.push(Object.assign({}, post));
proxy.writeData({query: listPostMessages, data});
}
})
}
})
})
)(ChatInput)
Please help me out!.Thanks in advance

At this line :
const data = proxy.readQuery({ query: PostMessage });
I do not see a reference to PostMessage where you want to define your query.

The error means that in the imported PostMessage, the query could not be found. Make sure it looks like this: (and not the shorthand equivalent)
query postMessage($id: ID!) {
postMessage(id: $id) {
id
title
}
}

I resolved this error by correcting my mutation ie PostMesssage in my case.This error
Must contain a query definition
mainly occur if your query is incorrect which you have written in graphql. Start seeing how you passing your variables and how you use them and how you define them.

I also got this error when I had added new resolver functions (in new files) but failed to include references to these resolvers into graphql's executable schema.

Related

How to add new key and vaule from object inside Array?

I am using ReactJSand Redux using the code below show how I am tring to add a new key:value in each object. Actually the error are:
Console log: Uncaught (in promise) TypeError: Cannot read property '0' of undefined
Web: TypeError: Cannot read property '0' of undefined
The data received from the API is:
The complete code is:
import React from "react";
import axios from "axios";
import TableData from "../TableData";
import CustomForm from "../FormCliente";
import Modal from "../Modal";
//Función que conecta un componente a Redux store.
import { connect } from "react-redux";
import { createBrowserHistory } from 'history';
class ClienteList extends React.Component {
state = {
DataFromApi: []
};
fetchArticles = () => {
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: `Token ${this.props.token}`,
};
axios.get("http://192.168.196.49:8000/clientes/api/").then(res => {
this.setState({
DataFromApi: res.data
});
});
}
componentDidMount() {
this.fetchArticles();
}
componentWillReceiveProps(newProps) {
if (newProps.token) {
this.fetchArticles();
}
}
render() {
console.log("Token_desde_connect:", this.props.token);
const history = createBrowserHistory();
const location = history.location;
console.log("debug_1.1: ", location)
const dummy = event => {
console.log('mostrando dummy:', event.target.id);
}
const encabezado = [
{
label: 'Cliente',
field: 'nombre',
sort: 'asc',
width: 150
},
{
label: 'Fecha de alta',
field: 'fecha_alta',
sort: 'asc',
width: 270
},
{
label: 'Usuario ID',
field: 'usuario_id',
sort: 'asc',
width: 270
},
{
label: 'Herramientas',
field: '',
sort: 'asc',
width: 270
}
];
console.log("#-Before-#Data_from_API: ", this.state.DataFromApi);
    // Modificar el array con los datos obtenidos de la API.
    //for (let x in this.state.DataFromApi){
//console.log("#-valor_x:", x, "-#Data_from_API: ", this.state.DataFromApi[x]);
      //this.setState.DataFromApi[x] = Object.assign({ coco: x },this.stateDataFromApi[x] ) ;
      //console.log("#-Inside FOR -#New_Data_from_API: ", this.state.DataFromApi[x] );
//Otra forma de hacer:
//console.log("#-Before-#Data_from_API: ", this.state.DataFromApi);
// Modificar el array con los datos obtenidos de la API.
//let data=this.state.DataFromApi;
//data.forEach( (obj) => obj.city="Spain");
//this.setState({ DataFromApi:data})
    //};
//console.log("#Al finalizar el FOR_ fuera -#New_Data_from_API: ", this.state.DataFromApi );
//this.setState((prevState) => {
// DataFromApi: prevState.DataFromApi.map((obj) => ({
// ...obj,//<- This line will copy the entire object
// myKey: obj.myValue,//<- And this will add/override the property in this obj
// })
//);
this.setState((prevState) => ({
DataFromApi: prevState.DataFromApi.map((x) => ({
...x,//<- This line will copy the entire object
coco: x,//<- And this will add/override the property in this obj
}))
}));
console.log("#-After-#Data_from_API: ", this.state.DataFromApi);
return (
<div>
<Modal requestType="post" btnText="Guardar"/>
<TableData data={this.state.DataFromApi} Encabezado={encabezado}/> <br />
<h2> Create an article </h2>
<CustomForm requestType="post" itemID={null} btnText="Create" />
<button id="dummy" onClick={dummy}>Dummy button</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
token: state.token
};
};
export default connect(mapStateToProps)(ClienteList);
ERROR:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
First of all, having your setState inside a loop is not a good idea, what you want to do in this case is pass a function to setState and that function will receive the "previous" (current) state.
this.setState((prevState) => ({
DataFromApi: prevState.DataFromApi.map((x) => ({
...x,//<- This line will copy the entire object
coco: x,//<- And this will add/override the property in this obj
}))
}));
It's also recommended to use this method (passing a function) when you're trying to update your state, based on the previous state, like this case, you're modifying your array, this is because this.setState is asynchronous and you can run into some problems if you don't use the function approach under this circumstances.
I hope this helps.
Update: Missing parenthesis, I used my editor this time so this works, I apologize about that.

React-firestore-hooks to fetch database records from cloud firestore

I'm trying to figure out how to use react-firebase-hooks in my react app so that I can simplify calls on my database.
My previous version (solved with help on this question) of this attempt used this class component with a componentDidMount function (it worked):
class Form extends React.Component {
state = {
options: [],
}
async componentDidMount() {
// const fsDB = firebase.firestore(); // Don't worry about this line if it comes from your config.
let options = [];
await fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, ' => ', doc.data());
options.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
});
});
});
this.setState({
options
});
}
I'm now trying to learn how to use hooks to get the data from the database using react-firebase-hooks. My current attempt is:
import { useDocumentOnce } from 'react-firebase-hooks/firestore';
I have also tried
import { useDocument } from 'react-firebase-hooks/firestore';
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
options.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
}),
);
This generates an error that says: 'useDocumentOnce' is not defined
I tried (that's also incorrect):
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
{snapshot.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id,
})},
);
How do I get a collection from firebase? I'm trying to populate a select menu with the options read from a collection in firebase called abs_for_codes.
I think the point of useState is that I don't need to declare a state anymore, I can just call
I have added my select attempt below:
<Select
className="reactSelect"
name="field"
placeholder="Select at least one"
value={valuesSnapshot.selectedOption}
options={snapshot}
onChange={handleMultiChangeSnapshot}
isMulti
ref={register}
/>
For reference, I have 2 other select menus in my form. The consts I use to set up the options for those are manually defined, but the process to establish their values is below:
const GeneralTest = props => {
const { register, handleSubmit, setValue, errors, reset } = useForm();
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalMethod");
};
const [valuesStudyType, setStudyType] = useState({
selectedOptionStudyType: []
});
const [valuesFundingBody, setFundingBody] = useState({
selectedOptionFundingBody: []
});
const handleMultiChangeStudyType = selectedOption => {
setValue("studyType", selectedOption);
setStudyType({ selectedOption });
};
const handleMultiChangeFundingBody = selectedOption => {
setValue("fundingBody", selectedOption);
setFundingBody({ selectedOption });
};
useEffect(() => {
register({ name: "studyType" });
register({name: "fundingBody"});
}, []);
How do I add the snapshot from the database query?
I have tried making similar handleMultiChange const and useEffect register statements for the snapshot, like so:
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
snapshot.push({
value: snapshot.data().title.replace(/( )/g, ''),
label: snapshot.data().title + ' - ABS ' + snapshot.id
}),
);
const [valuesField, setField ] = useState({
selectedOptionField: []
});
const handleMultiChangeField = selectedOption => {
setValue("field", selectedOption);
setField({ selectedOption });
};
but it doesn't work. The error message says:
ReferenceError: Cannot access 'snapshot' before initialization
I can't find an example of how to populate the select menu with the data from the database.
NEXT ATTEMPT
useEffect(
() => {
const unsubscribe = firebase
.firestore()
.collection('abs_for_codes')
.onSnapshot(
snapshot => {
const fields = []
snapshot.forEach(doc => {
fields.push({
value: fields.data().title.replace(/( )/g, ''),
label: fields.data().title + ' - ABS ' + fields.id
})
})
setLoading(false)
setFields(fields)
},
err => {
setError(err)
}
)
return () => unsubscribe()
})
This doesn't work either - it produces an error message that says:
TypeError: fields.data is not a function
NEXT ATTEMPT
Recognising that i need to search the collection rather than a call on document, but still not sure whether useCollectionData is more appropriate than useCollectionOnce (I can't make sense of the documentation about what useCollectionData offers), I have now tried:
const [value, loading, error] = useCollectionOnce(
firebase.firestore().collection('abs_for_codes'),
{getOptions({
firebase.firestore.getOptions:
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id,
})},
);
This is also incorrect. The error message points to the getOptions line and says: Parsing error: Unexpected token, expected ","
In my collection, i have a number of documents. Each has 2 attributes, a number and a text string. My options are to format the number and the text string so they appear together as well as an acronym i have inserted as text (as I was able to do using componentDidMount).
NEXT ATTEMPT
I next tried this:
const fields = firebase.firestore.collection("abs_for_codes").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, ' => ', doc.data());
fields.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
});
});
});
The error message says: TypeError: _firebase__WEBPACK_IMPORTED_MODULE_5__.firebase.firestore.collection is not a function
NEXT ATTEPMT
const searchFieldOfResearchesOptions = (searchKey, resolver) => {
// for more info
// https://stackoverflow.com/questions/38618953/how-to-do-a-simple-search-in-string-in-firebase-database
// https://firebase.google.com/docs/database/rest/retrieve-data#range-queries
fsDB
.collection("abs_for_codes")
.orderBy("title")
// search by key
.startAt(searchKey)
.endAt(searchKey + "\uf8ff")
.onSnapshot(({ docs }) => {
// map data to react-select
resolver(
docs.map(doc => {
const { title } = doc.data();
return {
// value: doc.id,
// label: title
value: title.data().title.replace(/( )/g, ''),
label: title.data().title + ' - ABS ' + title.id
};
})
);
}, setFieldOfResearchesError);
};
This attempt actually works to retrieve data from the database (hooray) - except I can't get the text label I want to render. Each document in the collection has 2 fields. The first is a title and the second is an id number, my last step is to make a label that has inserted text (ie ABS - ) and then the id number and the title together.
I have added the commented code to show what works to extract each document's title, but the extra bit I tried to make the label the way I want it doesn't present an error, it just doesn't work - I still only get the document title in the list.
Does anyone know how to generate a select menu set of options from a cloud firestore collection using hooks?
import { useDocument } from 'react-firebase-hooks/firestore';
Why ? You are using useDocumentOnce, of course you need to import this function, and no useDocument, which you don't use.
Last error:You are using a const before even it has been initialized, hence the
ReferenceError: Cannot access 'snapshot' before initialization
snapshot is going to be initialized by useDocumentOnce, you cannot use it (snapshot) as an argument passed to the function going to initialize it.
Plus, I had a look on react-firebase-hooks, here is the documentation of useDocumentOnce:
Use this example, and adapt it to use the document you want to use.
import { useDocument } from 'react-firebase-hooks/firestore';
const FirestoreDocument = () => {
const [value, loading, error] = useDocument(
firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt'),
{
snapshotListenOptions: { includeMetadataChanges: true },
}
);
return (
<div>
<p>
{error && <strong>Error: {JSON.stringify(error)}</strong>}
{loading && <span>Document: Loading...</span>}
{value && <span>Document: {JSON.stringify(value.data())}</span>}
</p>
</div>
);
};
You can either use useDocument as in the example, but you can also choose to use useDocumentOnce. But in this case, change the import accordingly (to import { useDocumentOnce } from 'react-firebase-hooks/firestore'; 😏
There are more than one question in place here. See if this helps.
This generates an error that says: 'useDocumentOnce' is not defined
import { useDocumentOnce } from 'react-firebase-hooks/firestore';
If you're importing it just like the DOCs says you should, and you're getting this error, I would say there's something wrong with your dev environment. Try using a different code editor, or even a different PC. If you are importing it correctly like you said, there's nothing wrong in your code and you shouldn't be getting this error. This is not a bug on your code.
See the docs for useCollection and useDocumentOnce (they're very similiar):
And now see what you're doing:
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
{snapshot.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id,
})},
);
You're not sending the correct parameters for the useDocumentOnce function. It need a document reference and an options object. You're sending a collection reference and a bunch of other code to the options parameter.
Your code should be looking like this:
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes').doc('someDocID')
);
OR
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes').where('someProperty','==','someValue')
);
Note that I didn't include the options parameter, since it's optional. Just send it if you need some specific option.
What a component should look like using the useDocumentOnce hook.
import { useDocumentOnce } from 'react-firebase-hooks/firestore';
const FirestoreDocument = () => {
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('someCollection').doc('someDocID'),
{
snapshotListenOptions: { includeMetadataChanges: true },
}
);
return (
<div>
<p>
{error && <strong>Error: {JSON.stringify(error)}</strong>}
{loading && <span>Document: Loading...</span>}
{snapshot && (
<span>
Document: {JSON.stringify(snapshot.data()}
</span>
)}
</p>
</div>
);
};
I'm sure you've already done this, but just in case... have you installed the react-firebase-hooks dependency in your project using the terminal command npm install --save react-firebase-hooks? If you haven't installed the dependency, that could explain the message useDocumentOnce is not defined. Here's a link to the installation document. I have gotten that message before when I've forgotten to install packages or dependencies which is why your error message made me think of that.

Make a common function to store the local storage data

I am a newbie in react-native. I have a folder structure like below:
-screens
-page1.js
-page2.js
-page3.js
-page4.js
-App.js
In page1.js, I have a function to store data to localStorage
let obj = {
name: 'John Doe',
email: 'test#email.com',
city: 'Singapore'
}
AsyncStorage.setItem('user', JSON.stringify(obj));
Now I have to display these data in few of my other pages. This is my code.
class Page2 extends Component {
state = {
username: false
};
async componentDidMount() {
const usernameGet = await AsyncStorage.getItem('user');
let parsed = JSON.parse(usernameGet);
if (parsed) {
this.setState({
username: parsed.name,
email: parsed.email
});
} else {
this.setState({
username: false,
email: false
});
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.saved}>
{this.state.username}
</Text>
</View>
);
}
}
export default Page2;
This is how I display data in page2. I may need to show these in other page too.
I dont want to repeat these codes in each page.
Any suggestions how to do it in react-native?
You can extract the data you need to display into it's own component and re-use it in any page that you need to display it in.
Another option is to use a higher-order component, that way you can wrap it around any components that need the data and it'll be passed down as a prop.
You can make your Constant.js where you can put all your common required utils and constants, reusable anywhere n your app.
In your Constant.js:
export const USER_DATA = {
set: ({ user}) => {
localStorage.setItem('user', JSON.stringify(obj));
},
remove: () => {
localStorage.removeItem('user');
localStorage.removeItem('refresh_token');
},
get: () => ({
user: localStorage.getItem('user'),
}),
}
in your any component, you can import it and use like this :
import { USER_DATA } from './Constants';
let user = {
name: 'John Doe',
email: 'test#email.com',
city: 'Singapore'
}
// set LocalStorage
USER_DATA.set(user);
// get LocalStorage
USER_DATA.get().user
That's you can make Constant common file and reuse them anywhere to avoid writing redundant code.
Simplified Reusable approach of localStorage
export const localData = {
add(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key, value) {
localStorage.removeItem(key);
},
load(key) {
const stored = localStorage.getItem(key);
return stored == null ? undefined : JSON.parse(stored);
},
};
localData.add("user_name", "serialCoder")
console.log( "After set 👉", localData.load("user_name") )
localData.remove("user_name")
console.log( "After remove 👉", localData.load("user_name") )

Apollo seems to refresh, when state is mapped to props, how can i prevent it?

I've build a component which basically list entries in a table, on top of that, i have another component to which filters can be applied. It all works really great with Apollo.
I'm trying to add deep linking into the filters, which on paper seems incredible simple, and i almost had i working.
Let me share some code.
const mapStateToProps = ({ activeObject }) => ({ activeObject });
#withRouter
#connect(mapStateToProps, null)
#graphql(FILTER_REPORT_TASKS_QUERY, {
name: 'filteredTasks',
options: (ownProps) => {
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
return {
variables: {
...filters,
id: ownProps.match.params.reportId,
},
};
},
})
export default class TasksPage extends Component {
static propTypes = {
filteredTasks: PropTypes.object.isRequired,
activeObject: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
this.state = { didSearch: false, initialFilters: filters };
this.applyFilter = this.applyFilter.bind(this);
}
applyFilter(values) {
const variables = { id: this.props.match.params.reportId };
variables.searchQuery = values.searchQuery === '' ? null : values.searchQuery;
variables.categoryId = values.categoryId === '0' ? null : values.categoryId;
variables.cardId = values.cardId === '0' ? null : values.cardId;
/*
this.props.history.push({
pathname: `${ this.props.history.location.pathname }`,
search: '',
});
return null;
*/
this.props.filteredTasks.refetch(variables);
this.setState({ didSearch: true });
}
..... Render functions.
Basically it calls the apply filter method, when a filter is chosen.
Which all works great, my problem is that when the activeObject is updated (By selecting a entry in the list). It seems to run my HOC graphql, which will apply the filters from the URL again, ignoring the filters chosen by the user.
I tried to remove the query strings from the url, once filters are applied, but i get some unexpected behavior, basically it's like it doesn't fetch again.
How can i prevent Apollo from fetching, just because the redux pushes new state?
I actually solved this by changing the order of the HOC's.
#graphql(FILTER_REPORT_TASKS_QUERY, {
name: 'filteredTasks',
options: (ownProps) => {
const filters = queryString.parse(location.search, { arrayFormat: 'string' });
return {
variables: {
...filters,
id: ownProps.match.params.reportId,
},
};
},
})
#withRouter
#connect(mapStateToProps, null)

My query is failing in relay and I don't know why?

I have this simple query which works fine in my Graphql but I cannot pass data using relay to components and I don't know why :(
{
todolist { // todolist returns array of objects of todo
id
text
done
}
}
this is my code in an attempt to pass data in components using relay:
class TodoList extends React.Component {
render() {
return <ul>
{this.props.todos.todolist.map((todo) => {
<Todo todo={todo} />
})}
</ul>;
}
}
export default Relay.createContainer(TodoList, {
fragments: {
todos: () => Relay.QL`
fragment on Query {
todolist {
id
text
done
}
}
`,
},
});
And lastly my schema
const Todo = new GraphQLObjectType({
name: 'Todo',
description: 'This contains list of todos which belong to its\' (Persons)users',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve: (todo) => {
return todo.id;
}
},
text: {
type: GraphQLString,
resolve: (todo) => {
return todo.text;
}
},
done: {
type: GraphQLBoolean,
resolve: (todo) => {
return todo.done;
}
},
}
}
});
const Query = new GraphQLObjectType({
name: 'Query',
description: 'This is the root query',
fields: () => {
return {
todolist: {
type: new GraphQLList(Todo),
resolve: (root, args) => {
return Conn.models.todo.findAll({ where: args})
}
}
}
}
});
This code looks simple and I cannot see why this won't work and I have this error Uncaught TypeError: Cannot read property 'todolist' of undefined, but I configure todolist and I can query in my graphql, you can see the structure of the query is same, I don't know why this is not working?
todolist should be a connection type on Query. Also, your ids should be Relay global IDs. You will not have access to your objects' raw native id fields in Relay.
import {
connectionArgs,
connectionDefinitions,
globalIdField,
} from 'graphql-relay';
// I'm renaming Todo to TodoType
const TodoType = new GraphQLObjectType({
...,
fields: {
id: uidGlobalIdField('Todo'),
...
},
});
const {
connectionType: TodoConnection,
} = connectionDefinitions({ name: 'Todo', nodeType: TodoType });
// Also renaming Query to QueryType
const QueryType = new GraphQLObjectType({
...,
fields: {
id: globalIdField('Query', $queryId), // hard-code queryId if you only have one Query concept (Facebook thinks of this top level field as being a user, so the $queryId would be the user id in their world)
todos: { // Better than todoList; generally if it's plural in Relay it's assumed to be a connection or list
type: TodoConnection,
args: connectionArgs,
},
},
});
// Now, to be able to query off of QueryType
const viewerDefaultField = {
query: { // Normally this is called `viewer`, but `query` is ok (I think)
query: QueryType,
resolve: () => ({}),
description: 'The entry point into the graph',
}
};
export { viewerDefaultField };
The above is not fully complete (you'll likely also need to setup a node interface on one or more of your types, which will require node definitions), but it should answer your basic question and get you started.
It's a huge, huge pain to learn, but once you struggle through it it starts to make sense and you'll begin to love it over RESTful calls.

Resources