React Relay fetch list - reactjs

I need to fetch list
I have Schema
export const Schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
activities: {
name: 'Activities',
type: new GraphQLList(activityType),
resolve: (root, args, { rootValue }) => {
return User.findById(rootValue.req.user.id)
.populate('activities')
.then((user) => user.activities);
},
},
When I do graphql request using curl and sends
query ActivitiesQuery {
activities {
name
}
}
it gives me
{
"data": {
"activities": [
{
"name": "Eat"
}
]
}
}
Can somebody give me example how can I fetch it using relay ? because all the examples give object and then list like Store: { teas: [...

activities is an example of a plural non-identifying field. It is plural in the sense that it returns a list of things, and non-identifying in the sense that the individual elements of that list are not identified by global IDs.
Relay does not support plural non-identifying fields at the root, but support is coming. Follow along at https://github.com/facebook/relay/issues/112

Related

How can I cache nested objects with Apollo Client?

I'm using the Contentful GraphQL API to fetch a collection of items, in this example football clubs.
query Clubs($limit: Int!, $skip: Int!) {
clubCollection(limit: $limit, skip: $skip) {
total
items {
name
description
}
}
}
The structure of the response is:
clubCollection: {
items: [{
... array of all the clubs
}]
}
It looks like the Apollo InMemoryCache is only caching the full query in its ROOT_QUERY object. But not each individual club. The setup for the cache looks like this:
new InMemoryCache({
typePolicies: {
Query: {
fields: {
clubCollection: concatContentfulPagination()
},
},
},
})
Does anyone know how I can target the clubs in items so that I can cache each individual club?
EDIT:
Thanks to the answer from #xadm I realised I did not need to extend the InMemoryCache like so:
new InMemoryCache({
typePolicies: {
Query: {
fields: {
clubCollection: {
items: {
},
...concatContentfulPagination()
}
},
},
},
})
But instead add it to the root of the typePolicies based on the type of the object, for me it is Club. When I added that it did work!
new InMemoryCache({
typePolicies: {
Club: {
keyFields: ['name']
},
Query: {
fields: {
clubCollection: concatContentfulPagination()
},
},
},
})
Items should have an id prop requested ... to be normalized - Apollo is normalizing cache GraphQL client
It's required to cache entries/types/subtypes properly.
https://graphql.org/learn/caching/
It, unique key can be id, _id or customized key per type using typePolicies - customizing-identifier-generation-by-type.
https://www.apollographql.com/docs/react/caching/cache-configuration/#default-identifier-generation
In this case (no querable id prop inside items), you should check in API (docs/specs/schema or explore network response body - __typename prop of items object) the type of club items entries (probably Club) and customize cache policies like:
new InMemoryCache({
typePolicies: {
Club: {
keyFields: ['name']
},
... assuming name is unique.
For this pupose you can use reactive variables. That way you can access clubs array anywhere in your code without rerunning query and perform any array operation on it.
Visit this page for more info on it.

How to use custom field in react admin, insted of { data: [...] }

I'm new in react-admin and I'm trying to create a new admin panel for my old API.
So when my data provider do API calls it causes me this error:
The response to 'getList' must be like { data : [...] }, but the received data is not an array. The dataProvider is probably wrong for 'getList'
The responses of my old API has various data fields like { 'posts': [] } or { 'users': [] }. How can I use these name of fields instead of { 'data': [] } ?
The 'data' in this case just refers to the type of information that should be retuned, not the name of the object.
Within your API, you can simply return a list in the following form:
const posts = [
{
"id":1,
"name":"post1"
},
{
"id":2,
"name":"post2"
},
];
return JSON.stringify(posts);
Then return that 'posts' object in your response and don't forget to set the expected ContentRange headers.
Not sure what language you are using, but the principle above should be easy enough to follow and apply in any language.

How do I query a referenced object in MongoDB

There are two collections in my mongo database: Book and Author
The Book collection has an author field which references the author who created it
const schema = new mongoose.Schema({
...
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Author'
})
I am using graphql, and my current code looks like this. booksByAuthor is a query that takes in a name, then returns the books written by that author.(this is working and gives me the correct output)
booksByAuthor: async (root) => {
const author = await Author.findOne({name: root.name})
return await Book.find({
author: author._id
})
}
My current implementation first queries the Author collection, and then uses the id of the author to then query the Book collection. However, I feel like there must be a way to do this without going through the Author collection.
Ideally, I want it to be something like this: (pseudo-code)
return await Book.find({
author.name: {root.name}
})
I've tried doing
return await Book.find({
"author.name": { $in: [root.name] }
})
and
return await Book.find({
name: { author: { $in: [root.name] } }
})
but both returns an empty array. I don't know if I should even be using $in for this case
You can use $lookup to achieve this in single query. $lookup is similar to joins in sql.
Based on your question i have created some sample documents. Below is the query for getting the books by author.
db.book.aggregate([
{
"$lookup": {
"from": "author",
"localField": "author",
"foreignField": "_id",
"as": "author"
}
},
{
"$match": {
"author.name": "Kafka"
}
}
])
Here is the Mongo Playground. Please follow the link to see the query in action. You will also able to see the sample documents i have used. Feel free to play around with it according to your needs.

RelayJS Invariant Violation with integer query parameter

I'm relatively new to Relay, so this may be an easy mistake I've made but I've been looking for some time already and I haven't found any information about the problem I'm having.
This is the error that I get from my application:
Uncaught Error: Invariant Violation: GraphQLFragmentPointer: Value for the argument to story on query Route should be a string, but it was set to 10. Check that the value is a string.
The problem is I actually want it to be 10 and don't want it to be string. Have I configured something incorrectly?
This is my GraphQL Schema:
var queryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
node: nodeField,
story: {
type: storyType,
args: {
storyID: {
description: 'Story ID',
type: GraphQLInt
}
},
resolve: (root, {storyID}) => {
if (storyID) {
return Story.get(storyID)
} else {
return Story.get(10)
}
}
},
}),
});
This is the relay route I've defined:
export default class extends Relay.Route {
static queries = {
story: () => Relay.QL`
query {
story(storyID: $storyID)
}
`,
};
static paramDefinitions = {
storyID: {
required: false
},
};
static routeName = 'StoryRoute';
};
And this is how I instantiate it:
let route = new Route({storyID: 10})
Ok, it looks like I've figured it out finally.
It appears that root fields are severely limited and can currently only have no parameters, a single string parameter or a multiple string parameters, connected straight with IDs of objects fetched.
Look for more information here: https://github.com/facebook/relay/issues/112
and here: https://github.com/facebook/relay/issues/94

How do I customize the url path of a sub resource in angular-restmod

I have the following setup
angular.module('xyz').factory('Bike', function (restmod) {
return restmod.model('/bikes').mix({
BikeParts: { hasMany: "BikeParts" },
$extend: {
Record: {
savePart: function (part) {
this.BikeParts.$create(part);
},
}
}
});
});
Now if I have a bike with id 1 calling bike.savePart(part) creates a POST request to /bikes/1/bike-parts. I want instead that it posts to /bikes/1/parts/. Is there a way to do this (without renaming the BikeParts entry in the call to mix)?
You should be able to change the url by adding a path property in your hasMany relation.
So change BikeParts: { hasMany: "BikeParts" } to BikeParts: { hasMany: "BikeParts", path: "parts" } and your url should become /bikes/1/parts/.

Resources