How to configure endpoint and custom graphql query in React-Admin - graphql

I'm setting up a react-admin app, that needs to connect with a Hasura Service using a graphql provider. To do so, I need to pass for the provider the endpoint "/v1/graphql" and the query with the selects subfields. Like this:
query MyQuery {
account_customers {
customer_id
email
given_name
}
}
I tried to use the Hasura Provider (https://github.com/hasura/ra-data-hasura/) but the requisitions are going to "/v1/query", and I couldn't found how to change it. Also couldn't figure out how to send my custom query with the subfields.
I also tried to use ra-data-graphql-simple provider, to override a query
to get the resource with the subfields.
/providers/myProvider.js
import buildGraphQLProvider, { buildQuery } from 'ra-data-graphql-simple';
import gql from 'graphql-tag';
const myBuildQuery = introspection => (fetchType, resource, params) => {
const builtQuery = buildQuery(introspection)(fetchType, resource, params);
if (resource === 'account_customers' && fetchType === 'GET_LIST') {
return {
// Use the default query variables and parseResponse
...builtQuery,
// Override the query
query: gql`
query ($id: ID!) {
account_customers {
customer_id
email
name
}
}`,
};
}
return builtQuery;
}
export default buildGraphQLProvider({ buildQuery: myBuildQuery })
App.js
import buildGraphQLProvider from './providers/myProvider.js';
class App extends Component {
constructor() {
super();
this.state = { dataProvider: null };
}
componentDidMount() {
buildGraphQLProvider({ clientOptions: { uri: 'http://localhost:8080/v1/graphql' }})
.then(dataProvider => this.setState({ dataProvider }));
}
render() {
const { dataProvider } = this.state;
return (
<div className="App">
<Admin dataProvider={dataProvider} >
<Resource name="account_customers" list={ListGuesser} />
</Admin>
</div>
);
}
}
export default App;
But I'm getting the error:
"Unknown resource account_customers. Make sure it has been declared on your server side schema. Known resources are "

I have had a similar issue.
The react-admin data provider you're using expects a certain shape of the GraphQL API, so it can get all items, create an item, update an item etc.
I have solved it by making sure that my schema complies with this:
type Query {
Post(id: ID!): Post
allPosts(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): [Post]
_allPostsMeta(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): ListMetadata
}
type Mutation {
createPost(
title: String!
views: Int!
user_id: ID!
): Post
updatePost(
id: ID!
title: String!
views: Int!
user_id: ID!
): Post
deletePost(id: ID!): Post
}
type Post {
id: ID!
title: String!
views: Int!
user_id: ID!
User: User
Comments: [Comment]
}
input PostFilter {
q: String
id: ID
title: String
views: Int
views_lt: Int
views_lte: Int
views_gt: Int
views_gte: Int
user_id: ID
}
type ListMetadata {
count: Int!
}
So in your case, you would need these endpoints (and change the naming in your backend):
AccountCustomer
allAccountCustomers
_allAccountCustomersMeta
updateAccountCustomer
createAccountCustomer
deleteAccountCustomer
etc...

Related

Updated react apollo cached but ui didnt update <Sharing || Solved>

Must included all data attribute inside reference as the typeDefs value. It's okies, if the value is null.
In apollo-client: 2.6.10
TypeDefs Graphql and Mutation below:
type Product {
_id: ID
name: String!
benefit: String
country: String
vitamins: [String]
createdAt: String!
}
const [addFruit] = useMutation(ADD_FRUIT_MUTATION, {
update(cache, result) {
const cachedData = cache.readQuery({
query: GET_FRUTIS_QUERY,
});
cachedData.getFruits = [
{
...result.data.create_fruit, // name, vitamins, country, benefit
createdAt: new Date().toISOString(), // added this
_id: result.data.create_fruit.name, // added this, this only temporary and must unique.
},
...cachedData.getFruits,
];
cache.writeQuery({
query: GET_FRUTIS_QUERY,
data: { ...cachedData },
});
},
onError({ networkError, graphQLErrors }) {},
variables: fruitVariable,
});
hope this helps someone. =)
I wondering why it must all included the data attribute key? if dont, it will updated cached but not into UI.

Prisma and ApolloClient: Prevent overwriting the include conditions at the backend by the frontend for relations

I have a problem, thx for any help.
With prisma we can use include with where conditions for models with a relation. If I make include conditions I get the right result. If I return it to the frontend it gets overwritten. I want to return exact my result from the backend.
I have at the frontend a query (ApolloClient, gql) like. It will return an array of comments for each post, I just want to have the first Comment for each post.
const POSTS = gql`
query posts {
posts(postId: $postId) {
id
comments{ // at the backend I have conditions for the comments
id
}
}
}
`;
Backend: Primsa and graphql nexus
Prisma Schema
model Post {
id String #id #default(cuid())
comments Comment[]
}
model Comment {
id String #id #default(cuid())
post Post #relation(fields: [postId], references: [id])
postId String
}
Nexus Model
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments()
})
const Comment = objectType({
name: 'Comment',
definition(t) {
t.model.id()
t.model.post()
t.model.postId()
})
Resolver
export const posts = queryField('posts', {
type: 'Post',
list: true,
args: {
...
},
resolve: async (_parent, args: any, { prisma, request }, info) => {
const posts = await prisma.post.findMany({
include: {
comments: {
take: 1
}
}
})
console.log(posts)
//Perfect result I want to return the include condition. But at the frontend I have all
//comments
return posts
},
})
The console.log(posts) is exact what I want to return!. Every post has an Array of ONE Comment.
I return the posts and at the frontend every post has an Array of ALL Comments, what I don't want. How can I prevent that the frontend query overwrite the backend return? The fields are the same.
I can't add a comment, so I am adding this to another answer.
Like I said with my PrismaSelect plugin, you can't use nexus-plugin-prisma t.model, t.crud. You will need to use Pal.Js CLI to autoGenerate all CRUD and ObjectTypes for all models.
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments() // this field will overwritten by next one so this not needed
t.list.field('comments', {
type: 'Comment',
list: true,
resolve: (parent, args, { prisma }) => {
// here parent type include all other fields but not this field
return prisma.comment.findMany({ // this query is very wrong will case N+1 issue
where: {
postId: parent.id,
},
take: 1,
})
},
})
})
Example
model User {
id Int #default(autoincrement()) #id
createdAt DateTime #default(now())
email String #unique
name String?
password String
posts Post[]
comments Comment[]
}
model Post {
id Int #default(autoincrement()) #id
published Boolean #default(false)
title String
author User? #relation(fields: [authorId], references: [id])
authorId Int?
comments Comment[]
}
model Comment {
id Int #default(autoincrement()) #id
contain String
post Post #relation(fields: [postId], references: [id])
postId Int
author User? #relation(fields: [authorId], references: [id])
authorId Int?
}
Here is my Pal.Js CLI generated type for Post model
import { objectType } from '#nexus/schema'
export const Post = objectType({
name: 'Post',
definition(t) {
t.int('id', { nullable: false })
t.boolean('published', { nullable: false })
t.string('title', { nullable: false })
t.field('author', {
nullable: true,
type: 'User',
resolve(parent: any) {
return parent['author']
},
})
t.int('authorId', { nullable: true })
t.field('comments', {
nullable: false,
list: [true],
type: 'Comment',
args: {
where: 'CommentWhereInput',
orderBy: 'CommentOrderByInput',
cursor: 'CommentWhereUniqueInput',
take: 'Int',
skip: 'Int',
distinct: 'CommentDistinctFieldEnum',
},
resolve(parent: any) {
return parent['comments']
},
})
},
})
when you use my Pal.js CLI, your frontend request will be like this
query {
findOnePost(where: {id: 1}) {
comments(where: {}, take: 1){
id
}
}
}
``
The best way to handle this issue and just query what your frontend request to use my PrismaSelect plugin.
Prisma Select takes the info: GraphQLResolveInfo object in general graphql arguments (parent, args, context, info) to select object accepted by prisma client. The approach allows a better performance since you will only be using one resolver to retrieve all your request. By doing so, it also eliminates the N + 1 issue.
Also, you can use my CLI to autogenerate all CRUD from your schema.prisma file https://paljs.com/generator/nexus
I mean I can add to my Post-ObjectType a field condition like:
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments()
t.list.field('comments', {
type: 'Comment',
list: true,
resolve: (parent, args, { prisma }) => {
return prisma.comment.findMany({
where: {
postId: parent.id,
},
take: 1,
})
},
})
})
This is working. But if I understood it correct I have for every post one extra request. But I have already at the mutation resolver the right result. And I don't have the comments field at the parent (t.list.field- resolver)

Nested GraphQL mutations with AWS Amplify

I'm struggling to write a nested GraphQL mutation for a React Native app I'm building with GraphQL client being AWS Amplify API. Here's my type definition file
type Game #model {
id: ID!
gameId: String!
players: [Player!]!
}
type Player #model {
id: ID!
username: String!
}
Here's my attempt to create a mutation to create a new Game
import API, { graphqlOperation } from '#aws-amplify/api';
const CreateGame = `
mutation ($gameId: String! $username: String!) {
createGame(input: {
gameId: $gameId,
players: [{ username: $username }]
}) {
id
gameId
players
}
}
`;
const gameObj = {
gameId: 'example_game_id',
username: 'example_username'
};
const queryResp = await API.graphql(graphqlOperation(CreateGame, gameObj));
console.log(queryResp.data);
Here's the error I get
"Validation error of type WrongType: argument 'input' with value 'ObjectValue{objectFields=[ObjectField{name='gameId', value=VariableReference{name='gameId'}}, ObjectField{name='players', value=ArrayValue{values=[ObjectValue{objectFields=[ObjectField{name='username', value=VariableReference{name='username'}}]}]}}]}' contains a field not in 'CreateGameInput': 'players' # 'createGame'"

GraphQL resolver for certain field not being invoked

I wrote simple GraphQL schemas and resolvers in studying purpose, and I could not get reason why my codes work as expected. I used Prisma for ORM, and express-graphql and graphql-import to compose the API.
type User {
id: ID!
name: String!
}
type Link {
id: ID!
url: String!
user: User!
}
type Query {
links(id: ID!): [Link!]!
}
// resolvers
const links = async (root, args, ctx) => {
const links = await ctx.prisma.links({
where: {
user {
id: args.id
}
}
}
}
// resolver for `Link` type
const user = async (root, args, ctx) => {
const user = await ctx.prisma.link({ id: parent.id }).user()
return user
}
// omitted field resolver for `url` and `id`
module.exports = {
user
}
With these codes, I expected to get id, url, user fields when I query links, but when I send the query, it returns null with user field. Which means, if I check in the server-side, the resolver for user field does not invoked at all. What is wrong with this?

Custom InputTypes with react-apollo

I'm trying to execute a mutation query that requires a custom input type on the client. Currently it looks something like this:
import { graphql } from 'react-apollo';
...
const graphQuery = graphql(gql`
input UserSignUpInput {
firstName: String!,
lastName: String!,
email: String!,
password: String!
}
mutation userSignUp($input: UserSignUpInput!) {
createUserByEmail(input: $input) {
authToken
}
}`, {
props: ({ mutate }) => ({
signup: (firstName, lastName, email, password) =>
mutate({ variables: { input: { firstName, lastName, email, password } } }),
}),
});
...
However I'm getting the error that I'm not allowed to define input types in the query. My question is: How do I define these complex input types? It doesn't seem like I'm able to provide a Schema to ApolloClient..
The correct syntax on the client side is just:
gql`
mutation userSignUp($input: UserSignUpInput!) {
createUserByEmail(input: $input) {
authToken
}
}
`
As your input UserSignUpInput is defined on the server, everything is going fine.
server
input UserSignUpInput {
firstName: String!,
lastName: String!,
email: String!,
password: String!
}

Resources