How to make multiple connects in one GQL mutation? - javascript

So I have the following in my Prisma datamodel. There's a relationship between the games and the characters, where the characters can appear in multiple games. Also the characters should be able to be listed on the games when the games alone are queried:
type Game {
id: ID! #unique
name: String! #unique
name_ja: String #unique
name_ko: String #unique
name_zh_CN: String #unique
name_zh_TW: String #unique
name_zh_HK: String #unique
characters: [Character]
filters: [GameFilter]
}
type Character {
id: ID! #unique
name: String! #unique
name_ja: String #unique
name_ko: String #unique
name_zh_CN: String #unique
name_zh_TW: String #unique
name_zh_HK: String #unique
games: [Game]
}
I then have this as a mutation for updating characters in my schema. I pass along an array of Game IDs because there's the possibility of multiple games being added within the same mutation:
updateCharacter(
name: String
name_ja: String
name_ko: String
name_zh_CN: String
name_zh_HK: String
name_zh_TW: String
id: ID!
games: [ID]
): Character!
And the following mutation written:
async updateCharacter(parent, args, ctx, info) {
if (!ctx.request.userId) {
throw new Error('You must be logged in');
}
const updates = {...args};
delete updates.id;
delete updates.games;
console.log(args.games);
const res = await ctx.db.mutation.updateCharacter({
data: updates,
games: {
connect: {
id: args.games
}
},
where: {
id: args.id
}
}, info);
return res;
}
And the following mutation written React-side. It's meant to pass along an array of IDs to the mutation (which will be expected because the schema assumes that an array of IDs will be passed along:
const UPDATE_CHARACTER_MUTATION = gql`
mutation UPDATE_CHARACTER_MUTATION(
$name: String!
$name_ja: String!
$name_ko: String!
$name_zh_CN: String!
$name_zh_TW: String!
$name_zh_HK: String!
$id: ID!
$games: [ID]!
) {
updateCharacter(
name: $name
name_ja: $name_ja
name_ko: $name_ko
name_zh_CN: $name_zh_CN
name_zh_TW: $name_zh_TW
name_zh_HK: $name_zh_HK
games: $games
id: $id
) {
id
name
name_ja
name_ko
name_zh_CN
name_zh_TW
name_zh_HK
games {
id
name
}
}
}
`;
I am also passing along the IDs of the games in an array onSubmit within the form to the mutation.
The result is that the mutation passes, but will only update the name fields, and the connection with the Games remains unchanged.
Not sure what I can do to make this mutate properly. I'm not sure whether passing the array directly into the mutation in the connect is what's causing the issue, but I tried mapping through and passing along each game id individually, but had the same result of the connection not updating.

The problem seems to be two-fold.
Regarding not seeing any changes at all:
In the mutation written, games is added to the async request json outside of data, meaning the data will never be found by the request.
Rather than:
data: ...,
games: ...
games must be part of data like such:
data: {
games: ...
}
Regarding wanting to do multiple connects:
GraphQL supports this style of nested updates using the following connect syntax:
connect: [
{ id : /* id from args */ },
{ id : /* another id from args */ },
...
]

Related

AWS Datastore GraphQL Unable to Query many to many relations

I am currently following this workflow on the AWS Amplify docs:
https://docs.amplify.aws/lib/datastore/relational/q/platform/js/#many-to-many-relationships
Yet I cannot query my relational join tables. I am confused as to what I have done incorrectly in terms of syntax. This is my code and these are the errors I receive.
// schema.graph.ql
type Icd10 #model #auth(rules: [{allow: public}]) {
id: ID!
Code: String
Description: String
MedicalTests: [MedicalTest] #manyToMany(relationName: "Icd10MedicalTest")
Disease: [Disease] #manyToMany(relationName: "Icd10Disease")
}
type MedicalTest #model #auth(rules: [{allow: public}]) {
AdditionalIcd10: [String]
AdditionalTesting: String
ClinicalUtility: String
Cpt: [String]
DescriptiveMethodology: String
Icd10: [String]
Icd9: [String]
Interpretation: String
Loinc: [String]
Methodology: String
Name: String
Overview: String
PanelTests: [String]
ReferenceRanges: String
References: String
SpecimenCollection: String
SpecimenType: String
Synonyms: [String]
TurnaroundTime: String
createdAt: AWSDateTime!
id: ID!
updatedAt: AWSDateTime!
uuid: String
ratings: [Rating] #manyToMany(relationName: "RatingMedicalTest")
icd10s: [Icd10] #manyToMany(relationName: "Icd10MedicalTest")
}
// TransferData.js
const value = await DataStore.query(MedicalTest,
m => m.Name.eq("11-Dehydro-Thromboxane B2, Urine")
)
console.log(value)
const result = await DataStore.query(Icd10,
m => m.MedicalTests.MedicalTest.id.eq(value.id)
)
console.log("updateIcd10MedicalTest Successful!")
console.log(result)
Error Image
I have attempted to change the input values multiple times as I thought maybe I was wrong in reading how my schema functioned as I'm new to GraphQL.

Grabbing the ID of a linked object with GraphQL, Apollo and React

I'm building an event managing database that handles RSVPs and links those RSVPs to a specific event. I'm using React, Apollo, GraphQL and Prisma as my stack and am having trouble correctly writing a mutation that would link an RSVP to an existing event. Still relatively new to the syntax, but I can't grab the event.id when I'm using the createRsvp mutation.
I'm able to pass the event.id down on the front-end through props, and I think a less-elegant way of receiving event.id in the args of the RSVP would be creating a hidden form with the event.id, but I KNOW there's got to be a way through graphQL. I've tried looking through the docs and various examples on grabbing the id from a different object. Any help is much appreciated :)
On the backend, here's my datamodel:
type Rsvp {
id: ID! #id
event: Event! #relation(link: INLINE)
firstName: String!
lastName: String!
email: String! #unique
company: String
jobTitle: String
mobile: String
dietary: String
}
type Event {
id: ID! #id
isPublished: Boolean
title: String!
startDate: String!
endDate: String!
description: String!
image: String
address: String
url: String!
suburb: String
postcode: String
state: String
country: String
guestLimit: Int
rsvps: [Rsvp]
}
The actual resolver for the Mutation.. I think I'm incorrectly retrieving the event.id here. My though process is that data: {...args} is first taking in the data from the RSVP form, and the connection to event is linking it to a certain event.
async createRsvp(parent, args, ctx, info) {
// 1. Query the event that is being registered for
const eventId = ctx.request.event.id;
// 2. Create the RSVP for this specific event
const rsvp = await ctx.db.mutation.createRsvp(
{
data: {
...args,
event: {
connect: { id: eventId }
}
}
},
info
);
console.log(rsvp);
return rsvp;
}
};
On the front end, this is what my Mutation looks like
const RSVP_MUTATION = gql`
mutation RSVP_MUTATION(
$email: String!
$firstName: String!
$lastName: String!
$company: String
$jobTitle: String
$mobile: String
$dietary: String
) {
createRsvp(
email: $email
firstName: $firstName
lastName: $lastName
company: $company
jobTitle: $jobTitle
mobile: $mobile
dietary: $dietary
) {
id
}
}
`;
and finally, the Mutation function in the form:
<Mutation mutation={RSVP_MUTATION} variables={({ id }, this.state)}>
{(createRsvp, { loading, error }) => (
<Form
onSubmit={async e => {
e.preventDefault();
const res = await createRsvp();
}}>
The error I receive in the console is "Uncaught (in promise) Error: GraphQL error: Cannot read property 'id' of undefined" which leads me to believe I'm incorrectly trying to access the event.id. Any tips or advice? Thanks again!
Divide and conquer
You can use /graphiql playground to test queries and mutations (API generally).
Testing this mutation:
mutation RSVP_MUTATION(
$email: String!
$firstName: String!
$lastName: String!
$company: String
$jobTitle: String
$mobile: String
$dietary: String
) {
createRsvp(
email: $email
firstName: $firstName
lastName: $lastName
company: $company
jobTitle: $jobTitle
mobile: $mobile
dietary: $dietary
) {
id
}
}
... with required variables you should quickly notice that you need to pass event id as variable, too. No special (more/less elegant), separate method required, this is a standard, query/mutation and variables, nothing more!!!
Just add eventId variable:
mutation RSVP_MUTATION(
$eventId: ID!
$email: String!
$firstName: String!
$lastName: String!
$company: String
$jobTitle: String
$mobile: String
$dietary: String
) {
createRsvp(
eventId: $eventId
email: $email
firstName: $firstName
lastName: $lastName
company: $company
jobTitle: $jobTitle
mobile: $mobile
dietary: $dietary
) {
id
}
}
Of course in resolver you'll get it within args, you can use f.e.
const { eventId, ...rest } = args;
const rsvp = await ctx.db.mutation.createRsvp(
{
data: {
...rest,
event: {
React variables can be passed f.e. by
variables={{ eventId: id, ...this.state }}

How can I pass an array of objects into Query?

I'm trying to figure out how to pass an array of objects into my GraphQL query, however i'm finding the documentation a little unclear on how to do so. I'm working with Apollo in the FE, Graphql-yoga in the BE and using Prisma as my database along with their API.
Here is my query with the array of objects hard coded:
const USERS = gql`
query USERS(
$userId: ID
) {
users(
where: {
id_not: $userId
hasProducts_some: {
OR: [
{ itemId: 1 },
{ itemId: 2 }
]
}
}
) {
firstName
}
}
`;
The above query returns me what I want, where i'm a bit stuck is how to get this array:
[
{ itemId: 1 },
{ itemId: 2 }
]
passed in as a variable of the query. From what I could find online, I might need to create a GraphQLObjectType on the client side to be able to pass in an object definition. Here was my implementation of that:
import { GraphQLObjectType, GraphQLString } from 'graphql';
const ProductName = new GraphQLObjectType({
name: 'ProductName',
fields: () => ({
itemId: {
type: GraphQLString,
},
})
});
const USERS = gql`
query USERS(
$userId: ID,
$hasProducts: [ProductName]
) {
users(
where: {
id_not: $userId
hasProducts_some: {
OR: $hasProducts
}
}
) {
firstName
}
}
`;
The above returns me the following error:
Unknown type "ProductName"
Have I gone with the correct approach here for passing in arrays of objects, if so what's wrong with my implementation?
Types are created and used in creating your schema server-side. Once created, the schema cannot be modified at runtime -- it has whatever types and directives were specified when it was created. In other words, defining a new type on the client-side is meaningless -- it can't be used in any queries you send to the server since the server is not aware of the type.
If a variable (like $hasProducts) is passed to an argument (like hasProducts_some), that variable's type must match the type of the argument. This type could be a scalar (like String or Int) or it could be an input object type. What exact type that that is depends on the schema itself. To determine the type to use, you can open up your schema's documentation in GraphQL Playground (or GraphiQL) and search for the field in question (in this case, hasProducts_some).
Note that you can also just pass a single variable in for the whole where field.
Since the gql function expects a template literal, you should escape the product object like so:
const USERS = gql`
query USERS(
$userId: ID,
$hasProducts: [${ProductName}]
) {
users(
where: {
id_not: $userId
hasProducts_some: {
OR: $hasProducts
}
}
) {
firstName
}
}
`;
New to graphql. But was wondering if this can resolve it.
const USERS = gql`
query USERS(
$userId: ID,
$hasProducts: GraphQLList(ProductName)
) {
users(
where: {
id_not: $userId
hasProducts_some: {
OR: $hasProducts
}
}
) {
firstName
}
}
`;
Minor change, but am not privileged to comment . So posting it as answer.

Use array as variable for GraphQL Query

I want to query a nested field for multiple variables.
In this case, I want to query RPR, but only return RPR if the label of the nested Region is given. One variable for the nested Region (field: label) works fine, but how can I filter on multiple variables of the same field?
The way I see it, when I call up the query-client (in my case through Apollo), I want to give an array as a variable and let the query in the backend go through that array and return results based on any of the variables given in the array.
The resolver does nothing more then:
rPRs: (root, args, ctx, info) => {
return ctx.db.query.rPRs(args, info);
}
The relevant part of the schema:
type RPR {
id: ID! #Unique
RPRID: String! #Unique
state: String
region: Region!
resource: Resource!
price: Float
theme: String
editionTitle: String
information: String
}
type Region {
id: ID! #Unique
regionID: String! #Unique
label: String! #Unique
name: String! #Unique
aov: Float!
aov_multiplier: Float!
}
The current query to retrieve all 'RPR' with nested regions:
query ADVICE_RESOURCES_QUERY($theme: String, $regio: String) {
rPRs(where: {
theme: $theme,
region: {
label: $regio
}
})
{
RPRID
region {
label
}
}
}
you should be able to use the label_in filter and provide the array of Strings to it. Your where would then look like this:
where: {
theme: $theme,
region: {
label_in: $regio
}
}

What's proper way of escaping input string in JavaScript?

See graphql mutation with JavaScript variables injected (please ignore that it's constructed manually and not through some gql helper):
mutation {
createPost(
name: "${name}",
description: "${description}"
) { id }
}
Is there any standard function for escaping name & description string variables ?
Eg. some function gqlEscape which will make query safe:
mutation {
createPost(
name: "${gqlEscape(name)}",
description: "${gqlEscape(description)}"
) { id }
}
How would that function look like?
Instead of doing string interpolation, you can use GraphQL variables which are designed for your use case. You can do this using raw HTTP, too, but I usually use graphql-request in this case.
Here's an example:
import { request } from 'graphql-request'
const mutation = `
mutation newPost($name: String!, $description: String) {
createPost(
name: $name,
description: $description
) {
id
}
}
`
const variables = {
name: 'New Post',
}
request('my-endpoint', mutation, variables).then(data => console.log(data))
A few notes:
I defined two variables, name: String! and description: String. String! means, it's a required variable of type String, while String means it's an optional variable. You can see that I did not pass a value for description, which is possible because it's optional.
I did not specify the double quotes for the two strings, that's already handled by the GraphQL Variables themselves.
You can read more about GraphQL Variables here and here.

Categories