Create mutation between related types in GraphQL - 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!

Related

GraphQL query with multiple nested resolvers and mapping fields to arguments

From GraphQL Client's perspective, how do I perform a query with multiple nested resolvers where the fields from the parent are passed as arguments to the child resolver?
Here is a minimal example:
GraphQL Schema:
type Author {
id: ID!
name: String!
}
type Book {
id: ID!
title: String!
releaseDate: String!
}
type Query {
// Returns a list of Authors ordered by name, 'first' indicates how many entries to return
getAllAuthors(first: Int!): [Author]!
// Returns a list of Books ordered by releaseDate, 'first' indicates how many entries to return
getBooksByAuthorId(first: Int! authorId: ID!): [Book]!
}
Is it possible to write a query to get all authors and their last released book? Something around the lines:
query GetAuthorsWithLastBook($first: Int!) {
getAllAuthors(first: $first) {
authorId: id
name
lastBook: getBooksByAuthor(1, authorId) {
title
}
}
}
In the example above, I attempted to alias getAllAuthors.id as authorId and pass the alias down as argument to getBooksByAuthor(...) but that didn't work.
The key aspect of the problem is that I don't know the authorIds beforehand. I could fetch the authors first and build a query to fetch their last book but that will result in multiple queries and that is something I would like to avoid.
Update
A Java Kickstarter example is available here: https://www.graphql-java-kickstart.com/tools/schema-definition/
yes, on the graphql definition, you need to add lastBook in the Author
type Author {
id: ID!
name: String!
lastBook: [Book]
}
Next up u need to write the resolver for the lastBook
const resolvers = {
Query: {
Author {
lastBook: (parent, args) {
const userId = parent.id;
return getBooksByAuthor(userId, 1);
},
}
}
};

Prisma: Field ... is not defined in ... input type OR type ... expected but non object was submitted

I'm using Prisma 1.34. fro API development. Testing it through the localhost playground.
Sorry for long text in advance, can't understand where I went wrong.
I have the following scheme representing hierarchy Script template consist of the Cards templates, and Cards include Tasks templates:
type ScriptTemplate {
id: ID!
name: String!
cards: [CardTemplate!]
input: String!
output: String!
}
type CardTemplate {
id: ID!
title: String!
description: String
tasks: [TaskTemplate!]
}
input ExistingCardTemplateInput {
id: ID!
}
input NewCardTemplateInput {
title: String!
description: String!
tasks: [NewTaskTemplateInput!]
}
type TaskTemplate {
id: ID!
label: String!
description: String!
cards: [CardTemplate!]
}
input ExistingTaskTemplateInput {
id: ID!
}
input NewTaskTemplateInput {
label: String!
description: String!
}
Corresponding mutations are:
type Mutation {
createScriptTemplate(name: String!, input: String!, output: String!, cards: [ExistingCardTemplateInput!], new_cards: [NewCardTemplateInput!]): ScriptTemplate
createCardTemplate(title: String!, description: String! tasks:[ExistingTaskTemplateInput!], new_tasks:[NewTaskTemplateInput!]): CardTemplate
createTaskTemplate(label: String!, description: String! cards:[ExistingCardTemplateInput!]): TaskTemplate
}
So basically, if I trying to use createTaskTemplate mutation or createCardTemplate mutation - everything is working fine. I can create these entities, including nested mutation creating new Card with the new Tasks in it or binding already existing Tasks. Or existing Card to newly created Task. That's why explicitly defined input types: ExistingTaskTemplateInput, NewTaskTemplateInput and NewCardTemplateInput.
Everything is working as expected when I'm trying to create a new script with the inclusion of a new Card or connecting it to an existing one, as well.
However, if I'm trying to create Script, Card and include new Tasks in it I've got error messages above.
When trying the following mutation:
mutation{
createScriptTemplate(
name: "Script via API_H2"
input: "Something describing initial state"
output: "Something describing required state at the end"
cards: [
{
id: "cjycl2nup00ta0703sd0kd8oa"
},
{
id: "cjye3ryee01ey070383sxaoxz"
}
]
new_cards:[
{
title:"New card via scriptis2"
description:"desc"
tasks: [
{
description: "test dewscription"
label: "test label"
}
]
},
{
title:"New card through scriptos2"
description: "desc"
}
]
){
id
name
input
output
createdAt
updatedAt
cards{
id
title
tasks{
id
label
}
}
}
}
I'm having error:
{
"data": {
"createScriptTemplate": null
},
"errors": [
{
"message": "Variable '$data' expected value of type 'ScriptTemplateCreateInput!' but got:
{\"name\":\"First Script through API_H2\",\"input\":\"Something describing initial state\",\"output\":\"Something describing requred state at the end\",\"cards\":{\"connect\":[{\"id\":\"cjycl2nup00ta0703sd0kd8oa\"},{\"id\":\"cjye3ryee01ey070383sxaoxz\"}],\"create\":[{\"title\":\"New card via scriptis2\",\"description\":\"desc\",\"tasks\":[{\"label\":\"test label\",\"description\":\"test dewscription\"}]},{\"title\":\"New card through scriptos2\",\"description\":\"desc\"}]}}.
Reason: 'cards.create[0].tasks'
Expected 'TaskTemplateCreateManyWithoutCardsInput', found not an object. (line 1, column 11):\nmutation ($data: ScriptTemplateCreateInput!) {\n ^",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createScriptTemplate"
]
}
]
}
The actual request looks like this (via console.log) :
{ name: 'First Script through API_H2',
input: 'Something describing initial state',
output: 'Something describing requred state at the end',
cards:
{ connect:
[ [Object: null prototype] { id: 'cjycl2nup00ta0703sd0kd8oa' },
[Object: null prototype] { id: 'cjye3ryee01ey070383sxaoxz' } ],
create:
[ [Object: null prototype] {
title: 'New card via scriptis2',
description: 'desc',
tasks:
[ [Object: null prototype] { label: 'test label', description: 'test dewscription' } ] },
[Object: null prototype] { title: 'New card through scriptos2', description: 'desc' } ] } }
Looks like I missing {connect or create} bit for the tasks field.
However when I'm altering it to look like:
tasks: {create: [
{
description: "test dewscription"
label: "test label"
}
]
}
I'm getting error that Field \"create\" is not defined by type NewTaskTemplateInput and Field NewTaskTemplateInput.label and description of required type String! was not provided
However, this works perfectly fine (same request without tasks) :
mutation{
createScriptTemplate(
name: "Script via API_H2"
input: "Something describing initial state"
output: "Something describing required state at the end"
cards: [
{
id: "cjycl2nup00ta0703sd0kd8oa"
},
{
id: "cjye3ryee01ey070383sxaoxz"
}
]
new_cards:[
{
title:"New card via scriptis2"
description:"desc"
},
{
title:"New card through scriptos2"
description: "desc"
}
]
){
id
name
input
output
createdAt
updatedAt
cards{
id
title
tasks{
id
label
}
}
}
}
Checked generated scheme, can't spot any problems there.
input TaskTemplateCreateManyWithoutCardsInput {
create: [TaskTemplateCreateWithoutCardsInput!]
connect: [TaskTemplateWhereUniqueInput!]
}
input TaskTemplateCreateWithoutCardsInput {
id: ID
label: String!
description: String!
}
Looks like I'm confusing scheme that I defined in gql files and the one I'm doing requests again, but don't know which direction to go.
Description
First outline the entire process of a request.
The above image is taken from Prisma Basics in the official Prisma documentation.
As shown in this picture, localhost playground is the client in the figure. The Server accessed by localhost playground is the API Server in the figure, which is Prisma Client.Here is the first connection, the green arrow in the picture.
Prisma Client gets the data from the database by accessing Prisma Server. This is the second connection, the yellow arrow in the figure.
The process of data flow, sent from localhost playground to API Server to complete the first connection transmission, and then processed by resolve, use Prisma Client to send to Prisma Server to complete the second connection transmission. Prisma Server retrieves data from Database according to the requested data and returns it to API Server, completes the second connection acceptance. API Server returns the data to localhost playground, completing the acceptance of the first connection. A complete process is completed.
In the process of using Prisma, we define two schema, one for defining Prisma Server, the name is usually datamodel.prisma. The other one is used to define Prisma Client, if it is defined in the file , usually named schema.graphql, can also be defined in the JS file through the template string, there are other ways, here is not discussed in depth. What you are showing belongs to Client Schema.
Successful completion of this process requires that the data sent by each part is correct.
Your first error occurred when the second connection was sent. The data sent from API Server did not match the schema definition in Prisma Server.
The second error is that the data sent from localhost playground does not match the schema definition in API Server when the first connection is sent.
Example
Assume the following architecture
# datamodel.prisma
type User {
id: #id!
name: String!
post: [Post!]!
}
type Post {
id: #id!
title: String!
}
# schema.graphql
input CreatePost {
title: String!
}
type Mutation {
createUser(name: String!,posts:[CreatePost])
}
Use the following mutation Create user
mutation {
createUser(name: "prisma", posts: [{ title: "test" }]) {
id
name
}
}
When sent from localhost playground to API Server, the data is as follows
{
"name": "prisma",
"posts": [{ "title": "test" }]
}
If you send this data directly to Prisma Server, it does not conform to the definition of Prisma Server. You need to convert the data to a definition that conforms to the Prisma Server schema.
{
"name": "prisma",
"posts": { "create": [{ "title": "test" }] }
}
Then send it to Prisma Server via Prisma Client.
const resolve = {
Mutation: {
createUser: async (_, args) => {
const userCreateInput = {
name: args.name,
posts: {
create: args.posts,
},
}
return await prisma.createUser(userCreateInput)
},
},
}
This way the final data arrives at the database via Prisma Server.
Solve the problem
Check out the definition of createScriptTemplate in API Server.It should be that it did not convert the received data into an error caused by the format required by API Server.
Understand Prisma

how can I fetch data from graphql in my resolver

Within my resolver I seem to be unable to fetch connected data
this works in the graphql playground (prisma) but I am unsure of the syntax about how to form a resolver in apollo server
// my typedef for activity is
type Activity {
id: ID! #id
ActivityType: ActivityType!
title: String!
date: DateTime
user: User!
distance: Float!
distance_unit: Unit!
duration: Float!
elevation: Float
elevation_unit: Unit
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
// and my resolver currently looks like this
async activity(parent, args, ctx, info) {
const foundActivity = await ctx.db.query.activity({
where: {
id: args.id
}
});
// todo fetch user data from query
console.log(foundActivity);
}
// where db has been placed onto the ctx (context)
// the CL gives all of the data for Activity apart from the user
// in the playground I would do something like this
query activity {
activity(where: {
id: "cjxxow7c8si4o0b5314f9ibek"
}){
title
user {
id
name
}
}
}
// but I do not know how to specify what is returned in my resolver.
console.log(foundActivity) gives:
{ id: 'cjxxpuh1bsq750b53psd2c77d',
ActivityType: 'CYCLING',
title: 'Test Activity',
date: '2019-07-10T20:21:27.681Z',
distance: 13.4,
distance_unit: 'KM',
duration: 90030,
elevation: 930,
elevation_unit: 'METERS',
createdAt: '2019-07-10T20:48:50.879Z',
updatedAt: '2019-07-10T20:48:50.879Z' }
Prisma is the DB ORM and then I have an Apollo-Server 2 server running on top of that. Unfortunately, stack overflow also thinks that there is too much code on this post so I will have to waffle on about inconsequential gibberish due to the fact that their system can't handle it.
You will have to implement a resolver for Activity.user. Unfortunately your entity does not seem to contain a reference to the user. First, add the user connection to your Prisma data model. Then implement a resolver for Activity.user. I am not very familiar with Prisma 1 but this naive implementation should already do what you want:
let resolvers = {
Query: {
// ...
},
Activity: {
user(parent, args, ctx) {
return ctx.db.query.activity({ id: parent.id }).user();
}
}
}
Find out more about resolving relations in Prisma here
So the answer was incredibly simple:
I just add a second argument to the query (after the "where" with a gql tag of the data shape to be returned so my code now looks like:
const foundActivity = await ctx.db.query.activity(
{
where: {
id: args.id
}
},
`{id title user { id name }}`
);

Cannot perform update query because update values are not defined

I am trying to execute a mutation like so
mutation creating {
createTeam(
payload: {
name: "Team von abc"
tacts:["94b4cbc2-b996-482f-b712-967bdb646e73"]
}
) {
id
name
}
}
This results in :
"message": "Cannot perform update query because update values are not
defined. Call \"qb.set(...)\" method to specify updated values.",
My graphql is defined like this:
input CreateTeamPayload {
name: String
tacts:[ID!]
}
type Team {
id: ID!
name: String
tacts: [Tact]!
}
type Query {
fetchTeams: [Team]!
fetchTeamById(id: ID!): Team
}
type Mutation {
createTeam(payload: CreateTeamPayload): Team!
}
My Team requires an ID from a "tact" so I provide him with an ID from a "tact" I created before. Is this approach wrong? How can I mutate types that reference other types? is there some documentation that actually does this?

GraphQL - variable not defined by operation

My GraphQL schema is defined as:
type Query {
getEntity(id: Int!): Entity
getEntityUsers(entityId: Int!, statusId: Int): [User]
}
type Entity {
id: Int!
name: String!
email: String!
logo: String
createdAt: DateTime!
updatedAt: DateTime!
users(statusId: Int): [User]
}
As you can see I have two ways of getting users for an Entity object. The one that is currently working for my query is the getEntityUsers root resolver method. This query looks like this:
query getEntityUsers($entityId: Int!, $statusId: Int) {
users: getEntityUsers(entityId: $entityId, statusId: $statusId) {
...
}
}
.. with the variables:
{
entityId: 1,
statusId: 2
}
Is there anyway to make the other way work by allowing me to pass in the statusId? Right now the query looks like this:
query getEntity($id: Int!) {
entity: getEntity(id: $id) {
...
users (statusId: 2) {
...
}
}
}
This obviously works with the variables:
{
id: 1
}
But, what if I wanted to use this second method and change the statusId? Is there anyway to pass in the statusId if it's not defined on the root resolver?
I have tried the query:
query getEntity($id: Int!) {
entity: getEntity(id: $id) {
...
users (statusId: $statusId) {
...
}
}
}
.. with the variables:
{
id: 1,
statusId: 2
}
But I just get the error: Variable "$statusId" is not defined by operation "getEntity". Is there anyway to do this?
Every operation (query or mutation) must explicitly define any variables you use inside that operation. So if you have a variable called $statusId, the type for this variable must be specified as part of your operation definition:
query getEntity($id: Int!, $statusId: Int) {
# your selection set here
}
Where those variables are used within your query (whether at the root level, or elsewhere) is irrelevant -- they must always be defined as part of your operation definition.

Resources