I've just started with GraphQL and mutations and am having this issue.
When I attempt to pass data to my GraphQL server, it seems the args parameter does not register the args that I am attempting to pass.
When I console.log them, they appear as undefined, but the method is working and receiving the request.
Does anything seem incorrect below? I'm just getting my head around it so would appreciate any feedback.
This is the query I'm attempting to run from GraphiQL
mutation {
addDelivery(
carrier: "AusPost",
goodsDelivered:[],
receiver: {
first_name:"ll",
last_name:"test",
receiver_email: "bbb#bbb.com.au"
},
consignmentNumber:"AAAA000",
poNumber:"PO0000"
) {
id
carrier {
iD
carrier_name
}
}
}
My schema:
type ReceiverType{
first_name: String
last_name: String
receiver_email: String
}
type ItemType {
item_type: String,
quantity: String
}
type Deliveries {
carrier: Carrier
consignmentNumber: String
goodsDelivered: [String]
id: ID
items: [ItemType]
poNumber: String
receiver: ReceiverType
}
type Carrier{
iD: ID
carrier_name: String
}
input Item {
item_type: String,
quantity: String
}
type Query {
deliveries: [Deliveries]
}
input Receiver {
first_name: String
last_name: String
receiver_email: String
}
type Mutation {
addDelivery(
carrier: String!,
consignmentNumber: String!,
goodsDelivered: [Item]!,
poNumber: String!,
receiver: [Receiver]!
) : Deliveries
}
My add delivery resolver
addDelivery: async (parent, args, context, info) => {
const delivery = new Deliveries({
receiver: {
first_name: args.first_name,
last_name: args.last_name,
receiver_email: args.receiver_email
},
items: args.items,
consignment_no: args.consignment_no,
po_number: args.po_number,
carrier_id: args.carrier_id,
delivery_id: args.delivery_id
});
await delivery.save();
return delivery;
}
I changed the signature to the following and it resolved the issue.
addDelivery: async ({receiver, items, consignment_no, po_number, carrier_id, delivery_id}) => {
Related
Cant figure out what I'm missing, and I havent had this issue before on any of my other updates. I expanded a collection and want to be able to update certain fields depending on where in the app the user is interacting. I've had no issue working with subdocs using separate calls, but with this particular nested field I'm getting no errors, and getting the correct document returned without the update. (I have another nested field that is updating fine - "personalInfo" while the "medical" field is the one giving me trouble)
The model looks like this:
const clientSchema = new Schema({
fullName: String,
firstName: String,
lastName: String,
enrollment: {
enrolled: Boolean,
enrollDates: [
{
begin: Date,
end: Date
}
]
},
personalInfo: {
dateOfBirth: Date,
phone: String,
email: String,
address: {
addressLineOne: String,
addressLineTwo: String,
city: String,
state: String,
zip: String
},
regionalCenter: String,
serviceCoordinator: String,
serviceCoordinatorPhone: String,
rcId: String,
emergencyContact: String,
livingSituation: String,
ihss: {
provider: String,
hours: Number,
services: String
}
},
medical: {
primaryIns: String,
primaryInsId: String,
secondaryIns: String,
secondaryInsId: String,
hasMediCal: Boolean,
mediCalId: String,
mediCalEnroll: Date,
hasMedicare: Boolean,
medicareId: String,
medicareEnroll: Date,
logs: {type: [logSchema], default: []},
},
contracts: {type: [contractSchema], default: []},
visits: [{ type: Schema.Types.ObjectId, ref:'Visit' }],
users: [{ type: Schema.Types.ObjectId, ref: 'User' }],
servicePlans: [{ type: Schema.Types.ObjectId, ref: 'ServicePlan'}],
currentPlan: String,
income: {type: [incomeSchema], default: []},
contacts: {type: [contactSchema], default: []}
}
The route:
router.route("/clients/:clientId").patch(updateClient)
And the controller... since I want to keep the controller as restful as possible, but conditionally set the fields depending on the api call, I conditionally set the different aspects and then pass in the body an additional field to tell the controller which aspect to update (so the personalInfo section has a field "personalInfo": "personalInfo" and the medicalInfo field has its own. The personalInfo object updates fine (I commented out the initial line since it was stated in another post that these calls work better doing a findOneAndUpdate- but that hasnt yielded any progress, and the personalInfo update worked without issue).
exports.updateClient = async (req, res) => {
try {
//const client = await Client.findOne({ _id: req.params.clientId })
if (req.body.firstName) {
client.firstName = req.body.firstName
}
if (req.body.lastName) {
client.lastName = req.body.lastName
}
if (req.body.personalInfo === 'personalInfo') {
client.updateOne({$set: {personalInfo: req.body}}, {new: true}, function(err, updatedDoc){
if(err){
console.log("error updating personal info: ", err)
}
})
}
if (req.body.enrollment === 'enrollment') {
client.updateOne({$set: {enrollment: req.body}}, {new: true}, function(err, updatedDoc){
if(err){
console.log("error updating personal info: ", err)
}
})
}
if(req.body.medicalInfo === 'medicalInfo'){
console.log("medInfo: ", req.body)
let clientId = req.params.clientId
// const client = await Client.findById(clientId)
// console.log("Client ", client)
// client.medical.set(req.body)
Client.findById(clientId)
.then((client) => {
client.medical.set(req.body
// hasMediCal: req.body.hasMediCal,
// hasMedicare: req.body.hasMedicare,
// mediCalId: req.body.mediCalId,
// medicareId: req.body.medicareId,
// mediCalEnroll: req.body.mediCalEnroll,
// medicareEnroll: req.body.medicareEnroll,
// primaryIns: req.body.primaryIns,
// primaryInsId: req.body.primaryInsId,
// secondaryIns: req.body.secondaryIns,
// secondaryInsId: req.body.secondaryInsId
);
client.save();
res.send(client)
})
// Client.findOneAndUpdate(
// { _id: req.params.clientId},
// {$set: {medical: req.body}},
// {new: true},
// function(err, updatedDoc){
// if(err){
// console.log("error updating personal info: ", err)
// }
// })
// client.markModified('medical');
}
// await client.save()
// res.send(client)
} catch (error) {
res.status(404)
res.send({ error: "Client not updated: ", error})
}
}
Finally, the body being sent:
{
"hasMediCal": false,
"hasMedicare": false,
"mediCalEnroll": "2005-04-22T08:00:00",
"mediCalId": "91234567A",
"medicalInfo": "medicalInfo",
"medicareEnroll": "2005-04-03T08:00:00",
"medicareId": "9FHS-ASU-95F8",
"primaryIns": "Molina",
"primaryInsId": "91234567A",
"secondaryIns": "SilverScript - Rx",
"secondaryInsId": "08dfA8d8"
}
Whether I've tried findOneAndUpdate, or findOne and then setting the field on the result, or setting each subfield in the object specifically, I keep getting the correct document returned, just not updated, and with no errors. I thought possibly it was because I was attempting to set the update within the conditionals, so I created a separate update controller but that got the same results as well. Really lost as how else to pursue this.
Please let me know if you see anything missing or where I'm going wrong. Much appreciated.
So after running around on this for hours, I came to a working solution, which essentially is no different, other than setting the query as a variable rather than writing it out. If anyone has any guess as to why this works when the multiple other methods didnt, I'd be grateful for your thoughts.
if(req.body.medicalInfo === 'medicalInfo'){
console.log("medInfo: ", req.body)
let clientId = req.params.clientId
let query = {_id: clientId};
Client.findOneAndUpdate(query, {$set: {medical: req.body}}, {new: true, upsert: true}, function(err, doc){
if(err) return res.status(500).send( {error:err});
return res.send(doc)
})
}
I have a query:
createNotification: async (_, args, {req, res}) => {
const followedBy = await prisma.user.updateMany({
where: {
following: {
some: {
id: req.userId
},
},
},
data: {
notifications: {
create: {
message: args.message,
watched: false,
},
},
},
})
And User and Notification models:
model User {
id Int #id #default(autoincrement())
email String #unique
name String
user_name String #unique
password String
movies Movie[]
notifications Notification[]
followedBy User[] #relation("UserFollows", references: [id])
following User[] #relation("UserFollows", references: [id])
}
model Notification {
id Int #id #default(autoincrement())
link String?
movie_id Int?
message String
icon String?
thumbnail String?
user User #relation(fields: [userId], references: [id])
userId Int
watched Boolean
}
When I run my query I get an error:
Unknown arg `notifications` in data.notifications for type UserUpdateManyMutationInput. Did you mean `email`? Available args:
type UserUpdateManyMutationInput {
email?: String | StringFieldUpdateOperationsInput
name?: String | StringFieldUpdateOperationsInput
user_name?: String | StringFieldUpdateOperationsInput
password?: String | StringFieldUpdateOperationsInput
}
The strange thing is that this works:
const followedBy = await prisma.user.findUnique({
where: {id: req.userId},
include: {
followedBy: true,
},
});
followedBy.followedBy.map(async(user) => {
await prisma.user.update({
where: {id: user.id},
data: {
notifications: {
create: {
message: args.message,
watched: false,
},
},
},
});
});
But this isn't making the best of what Prisma offers.
As of September 2021, Prisma does not support mutating nested relations in a top-level updateMany query. This is what the typescript error is trying to tell you, that you can only access email, name, user_name and password fields inside data. There's an open feature request for this which you could follow if you're interested.
For the schema that you have provided, here's a possible workaround that's slightly less readable but more optimized than your current solution.
createNotification: async (_, args, {req, res}) => {
// get the followers array of req.userId
const followedBy = await prisma.user.findUnique({
where: { id: req.userId },
include: {
followedBy: true,
},
});
// array of notification objects to be created, one for each follower of req.userId
let messageDataArray = followedBy.followedBy.map((user) => {
return {
userId: user.id,
message: args.message,
watched: false,
};
});
// do a bulk createMany.
// Since it's one query, it should be more optimized than running an update for each user in a loop.
await prisma.notification.createMany({
data: messageDataArray,
});
};
If you're interested, here's the docs reference for the kinds of nested updates that are possible.
One of my functions returns the object below:
Promise {
{
user: {
name: 'Ervin Howell',
email: 'Shanna#melissa.tv',
type: 'authenticated'
}
}
}
How to destruct user object from this json?
hi you can read the JSON API in this site. it has two methods:JSON.parse and JSON.sringify.
for your situation, you can use JSON.parse if your function return a JSON string
let json = "{
user: {
name: 'Ervin Howell',
email: 'Shanna#melissa.tv',
type: 'authenticated'
}
}
}"
obj = JSON.parse(json);
// when you want the user object
let user = obj.user
// when you want the user's name
let name = user.name
I always get Syntax Error: Unterminated string when I try to update my database using javascript strapi sdk. this.chapter.content is a html string generated by ckeditor. How can I escape this string to update my database using graphql?
async updateChapter() {
const q = `
mutation {
updateChapter(input: {
where: {
id: "${this.$route.params.chapterId}"
},
data: {
content: "${this.chapter.content.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/(?:\r\n|\r|\n)/g, '\n')}"
title: "${this.chapter.title}"
}
}) {
chapter{
title
id
content
}
}
}
`;
const res = await strapi.request("post", "/graphql", {
data: {
query: q
}
});
this.chapter = res.data.chapter;
}
Technically you could use block string notation to get around this issue. However, you really should supply dynamic input values using variables instead of string interpolation. This way you can easily provide any of sort of values (strings, numbers, objects, etc.) and GraphQL will parse them accordingly -- including strings with line breaks.
const query = `
mutation MyMutation ($chapterId: ID!, $content: String!, $title: String!) {
updateChapter(input: {
where: {
id: $chapterId
},
data: {
content: $content
title: $title
}
}) {
chapter{
title
id
content
}
}
}
`
const variables = {
chapterId: '...',
content: '...',
title: '...',
}
const res = await strapi.request("post", "/graphql", {
data: {
query,
variables,
},
})
Note that $chapterId may need to be of the type String! instead if that's what's called for in the schema. Since variables can also be input object types, instead of providing 3 different variables, you could also provide a single variable to be passed to the input argument instead:
const query = `
mutation MyMutation ($input: SomeInputObjectTypeHere!) {
updateChapter(input: $input) {
chapter{
title
id
content
}
}
}
`
const variables = {
input: {
where: {
id: '...',
},
data: {
content: '...',
title: '...',
},
},
}
Again, just replace SomeInputObjectTypeHere with the appropriate type in your schema.
Another solution maybe help
Code with issue: For example mainReason and actionTaken fields are text inputs and data contains some white spaces. This action give error: Unterminated string
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason: "${ticketData.mainReason}"
actionTaken: "${ticketData.actionTaken}"
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}
Fix this problem with JSON.stringify method
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason:${JSON.stringify(ticketData.mainReason)}
actionTaken:${JSON.stringify(ticketData.actionTaken)}
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}
What is the best practice to create an object via mutation connected to another object.
Using the following schema:
type Question #model {
id: ID!
text: String!
answers: [Answer] #connection(name: "QuestionAnswers")
}
type Answer #model {
id: ID!
text: String!
question: Question #connection(name: "QuestionAnswers")
}
The following (and variants of it) fail:
mutation CreateAnswer {
createAnswer(input: {
text:"Yes",
question: {
id: "9d38c759-6b64-4c1f-9e0e-d3b95a72b3a8"
}
})
{
id
}
}
Serverside code:
mutation CreateAnswer($input: CreateAnswerInput!) {
createAnswer(input: $input) {
id
text
question {
id
text
answers {
nextToken
}
}
}
}
With the above, receiving the following error:
"Validation error of type WrongType: argument 'input' with value
'ObjectValue{objectFields=[ObjectField{name='text',
value=StringValue{value='3'}}, ObjectField{name='question',
value=ObjectValue{objectFields=[ObjectField{name='id',
value=StringValue{value='9d38c759-6b64-4c1f-9e0e-d3b95a72b3a8'}}]}}]}'
contains a field not in 'CreateAnswerInput': 'question' #
'createAnswer'"
Seems that this may be a naming convention in AppSync. The below worked for me:
mutation createAnswer {
createAnswer (input: {
text: "5"
answerQuestionId: "9d38c759-6b64-4c1f-9e0e-d3b95a72b3a8"
}) {
id
}
Adding answer to QuestionId was what was needed.