Can't extending a remote graphql nested input type - graphql

graphql service 1 type defs:
import { gql } from 'apollo-server';
const typeDefs = gql`
type Post {
postId: ID!
postTitle: String!
postContent: String!
postAuthorId: ID
}
input PostTag {
name: String!
}
input PostInput {
postTitle: String!
postContent: String!
postAuthorId: ID!
postTags: [PostTag!]!
}
type CommonResponse {
code: Int!
message: String!
}
type Query {
posts: [Post]!
}
type Mutation {
addPost(post: PostInput): CommonResponse!
}
`;
export { typeDefs };
Now, graphql service 2 wants to extend PostTag input type from graphql service 1 like this:
import { gql } from 'apollo-server';
const typeDefs = gql`
extend input PostTag {
color: String
}
`;
export { typeDefs };
I print stitching schema, it's correct.
enum CacheControlScope {
PUBLIC
PRIVATE
}
type CommonResponse {
code: Int!
message: String!
}
type Mutation {
addPost(post: PostInput): CommonResponse!
}
type Post {
postId: ID!
postTitle: String!
postContent: String!
postAuthorId: ID
}
input PostInput {
postTitle: String!
postContent: String!
postAuthorId: ID!
postTags: [PostTag!]!
}
input PostTag {
name: String!
color: String
}
type Query {
posts: [Post]!
}
"""The `Upload` scalar type represents a file upload."""
scalar Upload
But when client sends a mutation like this:
mutation{
addPost(post: {
postTitle: "ez2on",
postContent: "golang",
postAuthorId: "1",
postTags: [{
name: "222",
color: "red"
}]
}){
code
message
}
}
Got this error:
{
"errors": [
{
"message": "Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
"locations": [
{
"line": 7,
"column": 3
}
],
"path": [
"addPost"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"errors": [
{
"message": "Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
"locations": []
}
],
"stacktrace": [
"Error: Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
" at new CombinedError (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/errors.ts:90:5)",
" at Object.checkResultAndHandleErrors (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/errors.ts:111:11)",
" at CheckResultAndHandleErrors.transformResult (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/CheckResultAndHandleErrors.ts:15:12)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/transforms.ts:37:45",
" at Array.reduce (<anonymous>)",
" at applyResultTransforms (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/transforms.ts:35:21)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/delegateToSchema.ts:104:12",
" at step (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:31:23)",
" at Object.next (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:12:53)",
" at fulfilled (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:3:58)"
]
}
}
}
],
"data": null
}

It's important to keep in mind that makeRemoteExecutableSchema just "uses the [provided] link to delegate requests to the underlying service". When you query fields from the remote schema, it's delegating the request for those particular fields to the remote server, effectively rerouting the request. This is true whether you stitch the schema with some other one, or use it by itself.
Schema stitching allows you to combine any number of local and remote schemas. However, any remote schemas will still have their fields resolved by their respective servers.
Because stitching merges the provided schemas' type definitions, you can use the extend keyword inside one schema to modify types from another, even if it's a remote schema. If we extend an object type, we can also add some resolvers to help resolve the fields we've added.
Extending a remote schema's input object is a bit different. There's no "resolving" input objects. Instead, all we do by extending it is saying "these fields are also valid". However, when we request some remote schema field that takes this modified input object as an argument, the resolution of this field is, again, delegated to the underlying remote schema. It gets the modified input object and when it validates it, it finds extra fields and throws an error.
In other words, it's not possible to extend input types like this. And consider, even if the request didn't fail validation -- even if you extend the input type, the original resolver has not been changed and so it necessarily won't know how to handle the additional input type fields anyway.
NOTE: If you do the above but with two local schemas, the extension should work as expected because there is no delegation in this case. You're still left with a resolver that doesn't necessarily know how to handle the new input object field though.

Related

Cannot read properties of null (reading '_doc')

I'm running a query in GraphQL and getting this error:
{
"errors": [
{
"message": "Cannot read properties of null (reading '_doc')",
"locations": [
{
"line": 38,
"column": 5
}
],
"path": [
"songs",
0,
"creator"
]
}
],
"data": null
}
This is the query.
query {
songs {
song_file_name
song_type
song_size
user_name
creator {
email
}
This is my resolver file, and this is the schema.
const { buildSchema } = require('graphql');
module.exports = buildSchema(`
type Song {
_id: ID!
song_file_name: String!
song_type: String!
song_size: Int!
user_name: String!
creator: User!
}
type User {
_id: ID!
email: String!
password: String
createdSongs: [Song!]
}
input SongInput {
song_file_name: String!
song_type: String!
song_size: Int!
user_name: String!
}
input UserInput {
email: String!
password: String!
}
type RootQuery {
songs: [Song!]!
}
type RootMutation {
createSong(songInput: SongInput): Song
createUser(userInput: UserInput): User
}
schema {
query: RootQuery
mutation: RootMutation
}
`);
Could anyone help me figure out what I'm doing wrong? It looks like creator is returning null, but it is properly defined as far as I can tell. What's causing this?
Also, can anyone suggest a good place to learn GraphQL? I'm following this guide and though I find it useful and informative, it quickly gets to a level where I don't feel I fully understand what I'm doing although I'm following along.
I know the basics of GraphQL, but the relational concepts is hard for me to wrap my head around.

AWS Appsync schema query resolver response mapping

After performing a create mutation, i expect to get the entire response object as shown in
https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html
But all i manage to get is the auto generated id (part of the schema) which is of uuid type.
Below is my schema and response mapping :-
Schema:-
type Apple{
id: ID!
type: String
price: Float
}
Mutation used :-
mutation MyMutation {
updateApple(input: {id: "0a9aa23f-3017-4b67-8dcd-354ef8f609d8", type: "green", price: 10.5}) {
id
}
}
======================================================================
type Mutation {
updateApple(input: UpdateAppleInput!): Apple
}
======================================================================
input UpdateAppleInput {
id: ID!
type: String
price: Float!
}
======================================================================
Response mapping template which returns only id
## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
Sample response below (I am expecting the updated "Apple" object):-
{
"data": {
"updateApple": {
"id": "0b8bb23f-5613-4b67-8dcd-354ef8f617r5"
}
}
}
I am referring the example given in :
https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-rds-resolvers.html#run-queries
And i specifically need the entire Apple object as response when i perform create or update mutations..
EDIT 1: Below response template works fine with a query. Why is it not working with a mutation? :
$utils.toJson($utils.rds.toJsonObject($ctx.result)

Error Cannot return null for non-nullable type: 'String' within parent MyModelType' (/createMyModelType/id)

I am trying to trigger a mutation in the aws console. I have linked my resolver function to a None type data source.
However, when I define my mutation with an input type as a parameter, the error " Error Cannot return null for non-nullable type: 'String' within parent MyModelType' (/createMyModelType/id)." occurs. Everything is fine though if I replace the input type with key word arguments.
I am certain it has to do with my resolver mapping template.
Just if you're wondering why I am using a None type, I want to be able to trigger a subscription without making real database changes or mutations.
I am not sure how to make it work with input types. Here is my code for the template:
{
"version": "2017-02-28",
"payload": $util.toJson($context.args)
}
My Schema:
input CreateMyModelType5Input {
title: String
}
type Mutation {
createMyModelType5(input: CreateMyModelType5Input!): MyModelType5
}
type MyModelType5 {
id: ID!
title: String
}
type Subscription {
onCreateMyModelType5(id: ID, title: String): MyModelType5
#aws_subscribe(mutations: ["createMyModelType5"])
}
Query I am trying to run:
mutation createMyModelType($createmymodeltypeinput: CreateMyModelTypeInput!) {
createMyModelType(input: $createmymodeltypeinput) {
id
title
}
}
Query Variables for the mutation query
{
"createmymodeltype5input": {
"title": "Hello, world!"
}
}
So I have been working on passing my arguments in the graphql mutation and using the input type seemed the only straight forward way around.
However, I have been able to do it with this way:
mutation = """mutation CreateMyModelType($id: String!, $title: String!){
createMyModelType(id: $id, title: $title){
id
title
}
}
"""
input_params = {
"id": "34",
"title": "2009-04-12"
}
response = app_sync.createMyModelType(mutation, input_params)
this can be a good guide

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

Graphql with mutation spring boot

My schema file is
type Mutation {
createCustomer(name: String!, email: String!, product: [Product]): Customer
}
input Product {
id: ID!
name: String!
price: Int
}
interface Person {
id: ID!
name: String!
email: String!
}
type Customer implements Person {
id: ID!
name: String!
email: String!
product: [Product]
}
I want to insert customer detail here which has product list as input. My query is
mutation {
createCustomer(
name: "kitte",
email: "kitte#gmail.com",
product: [
{
name: "soap",
price: 435,
}
]
)
{
id
name
email
product{name}
}
}
But I am getting exception
{
"data": null,
"errors": [
{
"validationErrorType": "WrongType",
"message": "Validation error of type WrongType: argument value ArrayValue{values=[ObjectValue{objectFields=[ObjectField{name='name', value=StringValue{value='dars76788hi'}}, ObjectField{name='price', value=IntValue{value=123}}]}, ObjectValue{objectFields=[ObjectField{name='name', value=StringValue{value='darr'}}, ObjectField{name='price', value=IntValue{value=145}}]}]} has wrong type",
"locations": [
{
"line": 5,
"column": 5
}
],
"errorType": "ValidationError"
}
]
}
I don't understand what is the error. And how to pass list to mutation. I have referred some examples but not able to insert product as list.
Make sure you are passing the right type of objects to your mutation. GraphQL needs separate types for input fields. In your schema, Product types should be something like this and you should change the mutation accordingly.
type Product {
id: ID!
name: String!
price: Int
}
input ProductInput {
name: String!
price: Int
}
input CustomerInput {
...
products: [ProductInput]
}
There are couple of very useful examples in the docs, see Mutations and Input Types

Resources