Array of Objects Apollo Server and post from react - graphql

So i'm trying to figure out how to pass an array of objects from a POST request to apollo server on AWS lambda.
I've checked this out but it's not the same problem
Array of objects convert into object of objects when I use Apollo
The post request looks like this...
api.post('query', { query : `mutation {saveNewItem(description: "${description}", specials: ${JSON.stringify(specials)}){name}}`})
// comment to get rid of silly scroll bar overlapping code
Schema looks like this...
const { gql } = require('apollo-server-lambda')
const typeDefs = gql`
type ShoppingItem {
description: String
specials: [Specials]
}
input Specials {
description: String
price: String
qty: String
saved: String
}
type Mutation {
saveNewItem(description: String!, specials: [Specials]) : ShoppingItem
}
`
example Specials looks like this...
[{ // Object One
description: "First One"
price: "1.00"
qty: "1"
saved: "false"
},{ // Object two
description: "Second One"
price: "1.00"
qty: "1"
saved: "false"
}]
The error I get currently is...
'Error: The type of ShoppingItem.specials must be Output Type but got: [Specials].',
'at assertValidSchema (/Users/me/Desktop/Projects/app/build/node_modules/graphql/type/validate.js:71:11)',
If I change it to a normal "type" it complains about it not being Input type.
I've also been through the apollo server docs and can't quite see what I'm doing wrong?
Please that as mentioned by Daniel in comments whilst technically the "duplicate" answer given is correct the information offered here is far more high quality and useful to people facing the problem(in my opinion)

You can only use input types for input (GraphQLInputObjectType) and object types for output (GraphQLObjectType). You are using Specials as both: As output type for the field specials in ShoppingItem and as input type in mutation argument specials. To do this you need two types. The reason for this is that output types (can) have resolvers (this is actually abstracted away from apollo server in your case). You will have to create two different types:
type ShoppingItem {
description: String
specials: [Specials]
}
type Specials {
description: String
price: String
qty: String
saved: String
}
input SpecialsDraft {
description: String
price: String
qty: String
saved: String
}
type Mutation {
saveNewItem(description: String!, specials: [SpecialsDraft]) : ShoppingItem
}

Related

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

How to combine multiple field in GraphQL Object

I am trying my first Graphql Schema design. Is it possible that single field inside object type refer to a complex object?
enum KeyPrefix {
WS
WK
SI
ENT
}
input generalKey {
keyPrefix:KeyPrefix!
key:Int!
}
type Item
{
pk: generalKey!
data: String!
name: String!
}
It gives me error as below.
The type of Item.pk must be Output Type but got: generalKey!
input is a keyword reserved for describing input to a GraphQL query. Your schema should look like this:
enum KeyPrefix {
(your values)
}
type PrimaryKey {
prefix: KeyPrefix!
key: Int!
}
type Item {
pk: PrimaryKey!
data: String!
name: String!
}
When you define a Query in your GraphQL schema, you will want to use an input, like so:
input PrimaryKeyInput {
prefix: KeyPrefix!
key: Int!
}
type Query {
getItemByPrimaryKey(input: PrimaryKeyInput!): Item
}
This will allow a client to ask for an Item using the same fields you have on PrimaryKey.

GraphQL no object type?

I've read everything, understood no solution and concrete explanation (even here: Apollo / GraphQl - Type must be Input type)
I want to create an object System that contains Suns. So I do:
type System {
_id: ID
name: String! #unique
diameter: Int
suns: [Sun]
}
type Sun {
_id: ID
name: String
diameter: Int
}
type Mutation {
createSystem(name: String!, diameter: Int, suns: [Sun]): System!
}
And I write in playground:
mutation {
createSystem(name:"new system", suns: [{ name: "John" }]) {
name
suns
}
}
But I got a terminal error: "Error: The type of Mutation.createSystem(suns:) must be Input Type but got: [Sun]."
I understand that Sun isn't received as an object. How to declare it as an object?
Thank you very much for your answers
The GraphQL spec. does not allow using type (i.e. output type) as the input argument.It only allows the input arguments to be enum , Scalar and Input . That means you have to create a SunInput
input SunInput {
_id: ID
name: String
diameter: Int
}
You need to make a custom "Type" for sun with its own resolver.
suns: { type: new GraphQLList(SunType) } // just an example
mutation {
createSystem(name:"new system", suns-names: "John") {
name
}
}
It will have resolver that writes a new system to the database called new system that adds a sun of "SunType" to the a database collection with the name of "Sun" for example.

Share structure between GraphQL schemas

I have a Apollo GraphQL server talking to an API returning responses with roughly the following structure:
{
"pagination": {
"page": 1,
// more stuff
},
sorting: {
// even more stuff
},
data: [ // Actual data ]
}
This structure is going to be shared across pretty much all responses from this API, that I'm using extensively. data is going to be an array most of the time, but can also be an object.
How can I write this in an efficient way, so that I don't have to repeat all these pagination and sorting fields on every data type in my schemas?
Thanks a lot!
I've sorted your problem by creating a lib called graphql-s2s. It enhances your schema by adding support for type inheritance, generic types and metadata. In your case, creating a generic type for your Paginated object could be a viable solution. Here is an example:
const { transpileSchema } = require('graphql-s2s')
const { makeExecutableSchema } = require('graphql-tools')
const schema = `
type Paged<T> {
data: [T]
cursor: ID
}
type Node {
id: ID!
creationDate: String
}
type Person inherits Node {
firstname: String!
middlename: String
lastname: String!
age: Int!
gender: String
}
type Teacher inherits Person {
title: String!
}
type Student inherits Person {
nickname: String!
questions: Paged<Question>
}
type Question inherits Node {
name: String!
text: String!
}
type Query {
students: Paged<Student>
teachers: Paged<Teacher>
}
`
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
I've written more details about this here (in Part II).
When you define your schema, you will end up abstracting out pagination, sorting, etc. as separate types. So the schema will look something like:
type Bar {
pagination: Pagination
sorting: SortingOptions
data: BarData # I'm an object
}
type Foo {
pagination: Pagination
sorting: SortingOptions
data: [FooData] # I'm an array
}
# more types similar to above
type Pagination {
page: Int
# more fields
}
type SortingOptions {
# more fields
}
type BarData {
# more fields
}
So you won't have to list each field within Pagination multiple times regardless. Each type that uses Pagination, however, will still need to specify it as a field -- there's no escaping that requirement.
Alternatively, you could set up a single Type to use for all your objects. In this case, the data field would be an Interface (Data), with FooData, BarData, etc. each implementing it. In your resolver for Data, you would define a __resolveType function to determine which kind of Data to return. You can pass in a typename variable with your query and then use that variable in the __resolveType function to return the correct type.
You can see a good example of Interface in action in the Apollo docs.
The downside to this latter approach is that you have to return either a single Data object or an Array of them -- you can't mix and match -- so you would probably have to change the structure of the returned object to make it work.

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