I get this error while querying:
"Cannot return null for non-nullable field Transaction.createdAt."
This is the query:
query getMyTransactions {
getMyTransactions {
id
createdAt
}
}
The schema for this query is:
extend type Transaction #key(fields: "id") {
id: ID! #external
}
type Query {
getMyTransactions: [Transaction!]!
}
And the other schema has Transaction type:
type Transaction #key(fields: "id") {
id: ID!
createdAt: String!
}
What's the problem?
EDIT: If I query for:
getMyTransactions {
id
}
Works ok I and get all the id of the query, but if I include another attribute the query fails.
In simple words - you declare createdAt type to be non-null value, but somewhere in your data createdAt is null.
createdAt: String!
! after the type name. This means that our server always expects to
return a non-null value for this field, and if it ends up getting a
null value that will actually trigger a GraphQL execution error,
letting the client know that something has gone wrong. GraphQl docs
Example
For this remote/local data (createdAt missing from the first object):
const Transaction = [
{
id: "1",
/* createdAt: "01/09/2020" */ /* missing from the data */
},
{
id: "2",
createdAt: "02/09/2020"
}
];
Executing query
query{
getMyTransactions {
id
createdAt
}
}
throw error:
"message": "Cannot return null for non-nullable field
Transaction.createdAt.",
To solve this:
Option 1: Add some resolver logic and/or add validation related to your required data.
Option 2: Remove the require from createdAt (return null allowed):
Related
Let's assume we have the following graphql schema:
type Query {
getPost(id: ID!): Post!
getUserProfile(id: ID!): User!
}
type User {
id: ID!
name: String!
gender: String!
dob: Date!
}
type Post {
id: ID!
creator: User!
caption: String!
}
Here are the graphql queries that we are making on frontend:
getPost(id:"pid1") {
id
creator {
id
name
}
}
getUserProfile(id: "uid1") {
id
name
gender
dob
}
This is the data model class on the frontend side with which we are deserialising the server response:
class UserModel {
String id;
String name;
String? gender; // ? means nullable
String? dob; // ? means nullable
}
My issue is that we have to mark the gender & dob fields as nullable because we aren't fetching them in the getPost query hence absence of them in the server response will make them null. However, gender and dob are always non-nullable in the backend as well as in the GQL API contract defined above. So we cannot use the null-safety feature of the client-side language for these fields and are forced to check if there are null or not. Is there a workaround or better way to treat such fields as null safe?
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.
I'm using AWS AppSync's GraphQL server with the following (simplified) schema:
type Query {
getIssue(id: String!): Issue
}
type Issue {
id: String!
data: IssueData!
}
type Event {
id: String!
time: AWSDateTime!
status: [String]
}
type Payment {
id: String!
amount: Int!
status: String
}
union IssueData = Event | Payment
When I make a query that includes inline fragments to select the status as a child of either an Event or Payment type in the Issue/data field, I get a FieldsConflict error:
query getIssue($id: String!) {
getIssue(id: $id) {
id
data {
... on Event {
time
status
}
... on Payment {
amount
status
}
}
}
}
Validation error of type FieldsConflict: status: fields have different list shapes # 'getIssue/data'
This is presumably caused by the Event/status field returning an array of strings, while the Payment/status field returns a single string.
Why does GraphQL consider this to be a conflict? How should I construct my query to allow access to the status field on both data types?
Note that I'm using a union rather than an extended interface because the Issue and Payment types have no common data structure.
From the spec:
If multiple field selections with the same response names are encountered during execution, the field and arguments to execute and the resulting value should be unambiguous. Therefore any two field selections which might both be encountered for the same object are only valid if they are equivalent.
You can resolve the issue by providing a field alias for one or both fields:
query getIssue($id: String!) {
getIssue(id: $id) {
id
data {
... on Event {
time
eventStatus: status
}
... on Payment {
amount
status
}
}
}
}
Renaming one or both fields in your schema would obviously also resolve the issue.
i have an api for sign up which returns error of string or null,
error: 'Email already use' or error: null
how do i construct it in schema? what i have now is this:
const typeDefs = gql`
type Mutation {
signUp(email: String, password: String): String
}
`;
since typeof null is object, how can i make it like this in graphql?
signUp(email: String, password: String): String || Object
Help?
GraphQL has a standard syntax for returning error values and your schema does not directly need to account for this.
In your schema I would “unconditionally” return whatever type you’d normally expect to return:
type UserAccount { ... }
type Query {
me: UserAccount # or null if not signed in
}
type Mutation {
signUp(email: String!, password: String!): UserAccount!
}
If it’s unsuccessful, you will get back a null field value (even though the schema in theory claims it shouldn’t) and an error.
{
"errors": [
{
"message": "It didn’t work",
"locations": [ { "line": 2, "column": 3 } ],
"path": [ "signUp" ]
}
]
}
In GraphQL you can define what field can be null and what can't.
Take a look at the docs:
https://graphql.org/learn/schema/#object-types-and-fields
String is one of the built-in scalar types - these are types that
resolve to a single scalar object, and can't have sub-selections in
the query. We'll go over scalar types more later.
String! means that the field is non-nullable, meaning that the GraphQL service promises
to always give you a value when you query this field. In the type
language, we'll represent those with an exclamation mark.
So in case of your schema String is absolutely fine. It can be null
type Mutation {
signUp(email: String, password: String): String
}
I have this type:
type Profile {
id: ID! #isUnique
email: String! #isUnique
...
}
And a query:
profile(email: String!):Profile
When I run the query with a user that doesn't exist, my underlying resolver returns null and I was expecting GraphQL to like this.
But, I get this error:
Cannot return null for non-nullable field Profile.id.
This happens because the query is expected to return a Profile and a Profile must have id field.
But, the query's return type is not non-nullable Profile!, it is Profile which would mean the query might return nothing.
How to fix this properly?
Try returning null in your profile query resolver. If you return something like an empty object, the Profile resolver will pick that up and attempt to return undefined as Profile.id, which as you noted is a schema error.
Try something like this:
const queryProfileResolver = (_, { email }) => {
// getProfileByEmail should return null if no match is found
return getProfileByEmail(email)
}
your graphQL response will then look something like this
{
data: {
profile: null
}
}
First of all you need to drop your data base and use
types.graphql as it is
type Profile {
id: ID! #isUnique
email: String! #isUnique
...
}
deploy it and error would be gone.
Main reason for happening this is you've already deployed data with not null thus it can't changed you need to drop our database, that's it