Why can I execute mutation function with null parameter with type being `String!`? - graphql

Simple table with one two rows. ID (incrementing int) and test_value (TEXT, nullable).
1. Query
query getData($test_value:String!) {
testtable(where: {test_value: {_eq: $test_value}}) {
test_value
}
}
Variables
{"test_value":null}
Result
{
"errors": [
{
"extensions": {
"path": "$.selectionSet.testtable.args.where.test_value._eq",
"code": "validation-failed"
},
"message": "unexpected null value for type \"String\""
}
]
}
This is a correct that I expect.
2. Mutation
mutation InsertData($test_value: String!) {
insert_testtable(objects: {test_value: $test_value}) {
affected_rows
}
}
Variables
{"test_value":null}
Result
{
data: {
insert_testtable: {
affected_rows: 1
}
}
}
I expect and error (because of test_value:String! declaration), but I don't get it.
Why?
P.S.
testable schema looks like this:
id: Int!
test_value: String

My understanding of your issue: Your schema has a mutation insert_testtable that takes a nullable String argument. When you submit a named mutation operation with a non-nullable String! variable, the GraphQL server does not respond with an error.
The GraphQL spec says that is the expected behaviour for mutations and queries. The spec says that if the type in the schema is nullable and of the same type as the variable, the operation is to be considered valid. This is what's happening for your mutation.
If you are not seeing the same behaviour for the query, it is possible that your GraphQL server implementation differs from the spec. You could check your server docs or their GitHub Issues.
For what it's worth, I checked that AppSync, AWS's GraphQL implementation, produces the expected behaviour for both queries and mutations.

Related

Document all potential errors on GraphQL server?

For a mutation addVoucher there are a limited list of potential errors that can occur.
Voucher code invalid
Voucher has expired
Voucher has already been redeemed
At the moment I'm throwing a custom error when one of these occurs.
// On the server:
const addVoucherResolver = () => {
if(checkIfInvalid) {
throw new Error('Voucher code invalid')
}
return {
// data
}
}
Then on the client I search the message description so I can alert the user. However this feels brittle and also the GraphQL API doesn't automatically document the potential errors. Is there a way to define the potential errors in the GraphQL schema?
Currently my schema looks like this:
type Mutation {
addVoucherResolver(id: ID!): Order
}
type Order {
cost: Int!
}
It would be nice to be able to do something like this:
type Mutation {
addVoucherResolver(id: ID!): Order || VoucherError
}
type Order {
cost: Int!
}
enum ErrorType {
INVALID
EXPIRED
REDEEMED
}
type VoucherError {
status: ErrorType!
}
Then anyone consuming the API would know all the potential errors. This feels like a standard requirement to me but from reading up there doesn't seem to be a standardises GraphQL approach.
It's possible to use a Union or Interface to do what you're trying to accomplish:
type Mutation {
addVoucher(id: ID!): AddVoucherPayload
}
union AddVoucherPayload = Order | VoucherError
You're right that there isn't a standardized way to handle user-visible errors. With certain implementations, like apollo-server, it is possible to expose additional properties on the errors returned in the response, as described here. This does make parsing the errors easier, but is still not ideal.
A "Payload" pattern has emerged fairly recently for handling these errors as part of the schema. You see can see it in public API's like Shopify's. Instead of a Union like in the example above, we just utilize an Object Type:
type Mutation {
addVoucher(id: ID!): AddVoucherPayload
otherMutation: OtherMutationPayload
}
type AddVoucherPayload {
order: Order
errors: [Error!]!
}
type OtherMutationPayload {
something: Something
errors: [Error!]!
}
type Error {
message: String!
code: ErrorCode! # or a String if you like
}
enum ErrorCode {
INVALID_VOUCHER
EXPIRED_VOUCHER
REDEEMED_VOUCHER
# etc
}
Some implementations add a status or success field as well, although I find that making the actual data field (order is our example) nullable and then returning null when the mutation fails is also sufficient. We can even take this one step further and add an interface to help ensure consistency across our payload types:
interface Payload {
errors: [Error!]!
}
Of course, if you want to be more granular and distinguish between different types of errors to better document which mutation can return what set of errors, you won't be able to use an interface.
I've had success with this sort of approach, as it not only documents possible errors, but also makes it easier for clients to deal with them. It also means that any other errors that are returned with a response should serve as an immediately red flag that something has gone wrong with either the client or the server. YMMV.
You can use scalar type present in graphql
just write scalar JSON and return any JSON type where you want to return it.
`
scalar JSON
type Response {
status: Boolean
message: String
data: [JSON]
}
`
Here is Mutation which return Response
`
type Mutation {
addVoucherResolver(id: ID!): Response
}
`
You can return from resolver
return {
status: false,
message: 'Voucher code invalid(or any error based on condition)',
data: null
}
or
return {
status: true,
message: 'Order fetch successfully.',
data: [{
object of order
}]
}
on Front end you can use status key to identify response is fetch or error occurs.

I get a GraphQL error when running this query using the apollo server. Anyone one knows what is the problem with it?

I am trying to fetch some data from the GitHub GraphQL but I get a GaphQLError. I have tried the same query on the developer section of github and it works. Anyone know what is the problem with it?
issueQuery = gql`
query search(first: 10, type: ISSUE, query: "repo:angular/angular is:issue state:open") {
issueCount
edges {
node {
... on Issue {
createdAt
title
body
url
comments(first: 10) {
nodes {
body
}
}
}
}
}
}
`;
Error Stack Trace:
"GraphQLError: Syntax Error: Expected $, found Name "first"
at syntaxError (http://localhost:4200/vendor.js:70270:10)
at expect (http://localhost:4200/vendor.js:75154:67)
at parseVariable (http://localhost:4200/vendor.js:73984:3)
at parseVariableDefinition (http://localhost:4200/vendor.js:73970:15)
at many (http://localhost:4200/vendor.js:75222:16)
at parseVariableDefinitions (http://localhost:4200/vendor.js:73959:82)
at parseOperationDefinition (http://localhost:4200/vendor.js:73926:26)
at parseExecutableDefinition (http://localhost:4200/vendor.js:73881:16)
at parseDefinition (http://localhost:4200/vendor.js:73845:16)
at many (http://localhost:4200/vendor.js:75222:16)"
New Error Stack Trace when adding $ before the parameters:
"GraphQLError: Syntax Error: Expected Name, found Int "10"
at syntaxError (http://localhost:4200/vendor.js:70270:10)
at expect (http://localhost:4200/vendor.js:75154:67)
at parseName (http://localhost:4200/vendor.js:73809:15)
at parseNamedType (http://localhost:4200/vendor.js:74385:11)
at parseTypeReference (http://localhost:4200/vendor.js:74364:12)
at parseVariableDefinition (http://localhost:4200/vendor.js:73971:83)
at many (http://localhost:4200/vendor.js:75222:16)
at parseVariableDefinitions (http://localhost:4200/vendor.js:73959:82)
at parseOperationDefinition (http://localhost:4200/vendor.js:73926:26)
at parseExecutableDefinition (http://localhost:4200/vendor.js:73881:16)"
Don't confuse the operation with the actual field being queried. The syntax should look like this:
operationType [operationName] [variableDefinitions] {
selectionSet
}
where operationType is one of query, mutation or subscription, operationName is an arbitrary name for your operation used in debugging, variableDefinitions are type definitions for any variables you reference inside the operation, and selectionSet is one or more fields you're actually querying.
In this case, search is a field we're querying, so it should not be proceeded by the query keyword. This works fine, provided you're authenticated:
query OptionalName {
search(first: 10, type: ISSUE, query: "repo:angular/angular is:issue state:open") {
issueCount
edges {
# more fields
}
}
}
If the operation type is query, you can omit the query keyword altogether. This is called "query shorthand":
{
search(first: 10, type: ISSUE, query: "repo:angular/angular is:issue state:open") {
issueCount
edges {
# more fields
}
}
}
If you use variables, define them inside parentheses beside your operation. Variable names are arbitrary, but by convention we use the input field names they will be used in:
query OptionalName ($first: Int, type: SearchType!, $query: String! ) {
search(first: $first, type: $type, query: $query) {
issueCount
edges {
# more fields
}
}
}

Graphql scalar type for a value that can be string or object?

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
}

Validation error of type UndefinedFragment: Undefined fragment

I've a graphql-jave v8.0 app running on Spring boot v1.5.10, and I'm trying to utilize 'fragment' feature of GraphQL to fetch limited number of fields with the following schema type definition:
type School {
id: ID
name: String
address: String
age: String
jobTitle: String
...
}
fragment UserFields on School {
age
jobTitle
}
type Query {
user (id: String!): School!
}
schema {
query: Query
}
When I execute this query:
{
user (id: "123")
{
... UserFields
}
}
The expected result should be:
{
"user": {
"age": "12",
"jobTitle": "student"
}
}
However, It results in the following error
"message": "Validation error of type UndefinedFragment: Undefined fragment
UserFields # 'user'",
Off course I can do this with explicitly passing the field name in the query but for the sake of example, I'm interested in utilizing fragment feature of GraphQL.
Any idea what I'm doing wrong please?
Fragments aren't defined in the schema, they're something for letting you create abstractions whilst building complex queries -- the main purpose is to allow you to avoid repetition when querying the same type in multiple parts of your query.
As Andrew said, and as the official docs exemplify, fragments are not defined in the schema but (ad hoc) in the query:
{
user (id: "123") {
... UserFields
}
}
fragment UserFields on School {
age
jobTitle
}
Unconditional fragments (like the one here) are used to avoid repetition. Imagine having multiple places where you want to select age and jobTitle in the same operation.
Conditional fragments, on the other hand, are used to make a conditional selection depending on the concrete interface implementation or union subtype.

GraphQL mutation variables

I'm trying to do a simple mutation using GraphQL with the GraphiQL interface. My mutation looks like this:
mutation M($name: String) {
addGroup(name:$name) {
id,
name
}
}
with variables:
{
"name": "ben"
}
But it gives me the error: Variable $name of type "String" used in position expecting type "String!"
If I change my mutation to mutation M($name: String = "default") it works as expected. This looks like it's related to the type system, but I can't seem to figure out what the problem is.
You probably defined the input name as a non-null string (something like type: new GraphQLNonNull(GraphQLString) if using js server, or String! in plain GraphQL).
So your input in the mutation must match, which means it must also be a non-null string. If you change to the following, it should work:
mutation M($name: String!) {
addGroup(name:$name) {
id,
name
}
}
Also if you define a default value as you did, it will be a non-null string.
Finally, you could drop the requirement of being a non-null in the server.
I think in you addGroup() mutation the args for name is of type String! that is new GraphQLNonNull(GraphQLString) but in your mutation you specify as String which conflicts with the type system.
When you have an error like this check your database model, in my case I checked my schema and as mongo pluralizes the words was causing me error but I could fix it, also check the documentation.
mutation {
createProject(
name:"project two",
description:"project two"
) {
name
}
}
=> works

Resources