Can we change/alter nested mutation using Laravel events? - graphql

I want to alter the mutation using Laravel Creating event. I want to fetch task ID from key that is coming from front end. And then i want to add this ID in replace of key so that my task will be create automatically using lighthouse structure. Here is sample mutation
mutation
{
createUser(input: {
firstname: "last"
email: "abc#gmaiol.com"
task:
{
create: {
key: 'reminder'
}
}
})
{
id
}
}

My recommendation is to create a resolver for your specific situation:
mutation
{
createUser(input: {firstname: "last", email: "abc#gmaiol.com", key: "reminder"})
{
id
}
}
Remember to always use double quotes " ", never use single quotes ' '
In your schema.graphql
input newUser {
firstname: String!
email: String!
key: String!
}
type newUserResponse {
ID: ID!
}
createUser(data: newUser): newUserResponse #field(resolver: "App\\GraphQL\\Mutations\\createUser")
Here's an example of the resolver: Resolver example
Also check the docs: https://lighthouse-php.com/4.9/api-reference/resolvers.html

Related

graphql: single mutation or one mutation per type

I have a GraphQL schema like this:
type User {
id: ID
name: String
email: String
addresses: [UserAddress]
}
type UserAddress {
id: ID
city: String
country: String
}
I always have doubts about how to make the best design for mutations. (I'm using apollo + prisma)
These are my options:
1) One single mutation
I need to create this mutation and input type:
input userAddressInput {
id: ID
city: String
country: String
}
mutation updateUser (
id: ID
name: String
email: String
addresses: UserAddressInput
): User
Then I execute mutations like this:
mutation updateUserData($id: ID, $name: String, $email: String) {
updateUser(id: $id, name: $name, email: $email) {
id
name
email
}
}
mutation updateUserAddress($id: ID, $userAddress: UserAddressInput) {
updateUser(id: $id, userAddress: $userAddress) {
id
addresses {
id
city
country
}
}
}
And resolvers like this:
Mutation: {
updateUser: (_, args) => {
if (args.name || args.email) {
// update model User by args.userData.id
}
if (args.userAddress) {
// update model UserAddress by args.userAddress.id
}
}
}
2) One mutation per type
I don't need to create any input type but I need two mutations:
mutation updateUser (
id: ID
name: String
email: String
): User
mutation updateUserAddress (
id: ID
city: String
country: String
): UserAddress
Then mutations like this:
mutation updateUser($id: ID, $name: String, $email: String) {
updateUser(id: $id, name: $name, email: $email) {
id
name
email
}
}
mutation updateUserAddress($id: ID, $city: String, $country: String) {
updateUserAddress(id: $id, city: $city, country: $country) {
id
city
country
}
}
And resolvers like this:
Mutation: {
updateUserAddress: (_, args) => {
// update model UserAddress by args.id
}
updateUser: (_, args) => {
// update model User by args.id
}
}
What is the best way to deal with such cases?
It depends what your use case is.
Does your GUI allow for update of a user's addresses without also updating the user info? If so you will likely need a separate mutation for updating only the addresses.
If you are allowing user and addresses to be edited and saved as one operation then individual mutations would require you to send multiple HTTP requests (one per mutation).
Do you need to update the user and addresses as an atomic transaction (i.e. all or nothing)? If so then you should use a single mutation.

Prisma append custom field to info argument when querying data

I am trying to append extra fields to the info object when querying data from prisma database. I have seen this post but I can't get fragments working.
I have below migrations:
type User {
id: ID! #unique
name: String
}
type Video {
id: ID! #unique
name: String
likes: [Like]
}
type Like {
id: ID! #unique
user: User
createdAt: DateTime!
}
Now, I would like to query all videos and check if current user has already liked a video. If so, the likes object should read the createdAt value, otherwise it would be empty.
I am doing this in playground via following query:
query {
videos() {
id
name
hasVoted: likes(where: {user: {id: "cjr7r85jy00rc0892dfwpu96u"}){
createdAt
}
}
}
this works but I would like to add the hasVoted to my query resolver to automatically append it to all queries. How can I do this ?
I tried solving it via fragments but without luck:
videos: {
fragment: `fragment hasVoted on Video {
hasVoted: likes (where: {user: {id: "cjr7r85jy00rc0892dfwpu96u"}}){
createdAt
}
}`,
resolve: async (_, args, ctx, info) => {
return await ctx.prisma.query.videos({},info);
}
}
Anyone has some ideas how I can do this? thx!
You can use addFragmentToInfo from graphql-bindings
https://oss.prisma.io/content/graphql-binding/02-api-reference#addfragmenttoinfo

Create mutation between related types in GraphQL

I'm using GraphQL to try to create a record that has a relation to another type. The types are Task and Day
datamodel.graphql:
type Task {
id: ID! #unique
content: String!
completed: Boolean!
dateToDo: Day!
}
type Day {
id: ID! #unique
content: String!
tasks: [Task]
}
I want to create a task so that it has a reference of the date it should be completed (from the Day type)
schema.graphql
type Mutation {
createTask(content: String!, completed: Boolean!, dateToDo: ID! ): Task!
}
my mutation resolver looks like this:
const Mutations = {
async createTask(parent, args, ctx, info) {
const task = await ctx.db.mutation.createTask(
{
data: {
dateToDo: {
connect: {
id: args.dateToDo
}
},
...args
}
},
info
);
return task;
},
when I run this mutation to create the task:
mutation CREATE_ONE_TASK {
createTask(
content: "a new task",
completed: false,
dateToDo: "cjqycv9dtjklr09179y9zfntq")
{
id
}
}
I get this error:
"message": "Variable \"$_v0_data\" got invalid value
{\"dateToDo\":\"cjqycv9dtjklr09179y9zfntq\",\"content\":\"a new
task\",\"completed\":false}; Expected type
DayCreateOneWithoutTasksInput to be an object at value.dateToDo.",
My questions are: Am I using connect correctly in the mutation resolver? And what the heck is DayCreateOneWithoutTasksInput (I see its been automagically added in prisma.graphql) and how do I use it to create a Task that has a relation to a Day's ID?
The mutation to create the task has the following shape:
mutation b {
createTask(
data: {
content: "Task1"
completed: false
dateToDo: { connect: { id: "cjqzjvk6w000e0999a75mzwpx" } }
}
) {
id
}
}
The type DayCreateOneWithoutTasksInput Prisma is asking for is autogenerated and is the one expected for the field dataToDo. The name means that Prisma would accept a type that creates one Day node but does not have the field tasks or a type that specifies a connection. The WithoutTasksInput part states is there because the type can only be used nested in a mutation where you start from a task, Prisma therefore already has the value to fill in for the tasks field on the nested Day node and you do not need to specify it if you create the day instead of connecting an existing one.
If you use the Playground you can explore the schema that contains all the types on the right side.
schema explorer in the playground
Hope that helps!

Cascade delete related nodes using GraphQL and Prisma

I'm trying to figure out cascade deletion in GraphQL.
I'm attempting to delete a node of type Question, but type QuestionVote has a required relation to Question. I'm looking for a way to delete a Question and all its votes at once.
Mutation for deleting a Question:
type Mutation {
deleteQuestion(where: QuestionWhereUniqueInput!): Question!
}
And its resolver (I'm using Prisma):
function deleteQuestion(parent, args, context, info) {
const userId = getUserId(context)
return context.db.mutation.deleteQuestion(
{
where: {id: args.id}
},
info,
)
}
How can I modify that mutation to also delete related QuestionVote nodes? Or should I add a separate mutation that deletes one or multiple instances of QuestionVote?
In case it's important, here are the mutations that create Question and QuestionVote:
function createQuestion(parent, args, context, info) {
const userId = getUserId(context)
return context.db.mutation.createQuestion(
{
data: {
content: args.content,
postedBy: { connect: { id: userId } },
},
},
info,
)
}
async function voteOnQuestion(parent, args, context, info) {
const userId = getUserId(context)
const questionExists = await context.db.exists.QuestionVote({
user: { id: userId },
question: { id: args.questionId },
})
if (questionExists) {
throw new Error(`Already voted for question: ${args.questionId}`)
}
return context.db.mutation.createQuestionVote(
{
data: {
user: { connect: { id: userId } },
question: { connect: { id: args.questionId } },
},
},
info,
)
}
Thanks!
You can set up cascade deletion by modifying your datamodel.
Given your question, I assume your datamodel looks somewhat like this:
type Question {
id: ID! #unique
votes: [QuestionVote!]! #relation(name: "QuestionVotes")
text: String!
}
type QuestionVote {
id: ID! #unique
question: Question #relation(name: "QuestionVotes")
isUpvote: Boolean!
}
Then you have to add the onCascade: DELETE field to the #relation directive like so:
type Question {
id: ID! #unique
votes: [QuestionVote!]! #relation(name: "QuestionVotes" onDelete: CASCADE)
text: String!
}
type QuestionVote {
id: ID! #unique
question: Question #relation(name: "QuestionVotes")
isUpvote: Boolean!
}
Now, every time a Question node is deleted, all related QuestionVote nodes are also deleted.
Note: If omitting onDelete, the value is automatically set to onDelete: SET_NULL by default. This means that deleting a node results in setting the other side of the relation to null.
You can read more about cascading deletes in Prisma in the documentation.

Graphql with nested mutations?

I am trying to figure out how to mutate a nested object with graphql mutations, if possible. For instance I have the following schema:
type Event {
id: String
name: String
description: String
place: Place
}
type Place {
id: String
name: String
location: Location
}
type Location {
city: String
country: String
zip: String
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String): Event
}
schema {
query: Query
mutation: Mutation
}
How can I add the place information inside my updateEvent mutation?
Generally speaking, you should avoid thinking of the arguments to your mutations as a direct mapping to object types in your schema. Whilst it's true that they will often be similar, you're better off approaching things under the assumption that they won't be.
Using your basic types as an example. Let's say I wanted to create a new event, but rather than knowing the location, I just have the longitude/latitude - it's actually the backend that calculates the real location object from this data, and I certainly don't know its ID (it doesn't have one yet!). I'd probably construct my mutation like this:
input Point {
longitude: Float!
latitude: Float!
}
input PlaceInput {
name
coordinates: Point!
}
type mutation {
createEvent(
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
updateEvent(
id: ID!
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
)
A mutation is basically just a function call, and it's best to think of it in those terms. If you wrote a function to create an Event, you likely wouldn't provide it an event and expect it to return an event, you'd provide the information necessary to create an Event.
If you want to add a whole object to the mutation you have to define a graphql element of the type input. Here is a link to a small cheatsheet.
In your case it could look like this:
type Location {
city: String
country: String
zip: String
}
type Place {
id: String
name: String
location: Location
}
type Event {
id: String
name: String
description: String
place: Place
}
input LocationInput {
city: String
country: String
zip: String
}
input PlaceInput {
id: ID!
name: String!
location: LocationInput!
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String, place: PlaceInput!): Event
}
schema {
query: Query
mutation: Mutation
}

Resources