Graphql schema:
type SDKConfig #model
#key(name: "byPublisher", fields: ["publisher_id", "id"]){
id: ID!
publisher_id: ID!
facebook_app_id: String
adjust_app_token: String
}
type GameConfig #model
#auth(rules: [
{allow: owner},
{allow: groups, groupsField: "groups"}]){
id: ID!
game_name: String!
bundle_identifier: String!
sdkConfigs: [SDKConfig] #connection(keyName: "byPublisher", fields: ["id"])
groups: [String]
}
Mutations:
export const createGameConfig = /* GraphQL */ `
mutation CreateGameConfig(
$input: CreateGameConfigInput!
$condition: ModelGameConfigConditionInput
) {
createGameConfig(input: $input, condition: $condition) {
id
game_name
bundle_identifier
sdkConfigs {
items {
id
publisher_id
facebook_app_id
adjust_app_token
createdAt
updatedAt
}
nextToken
}
groups
createdAt
updatedAt
owner
}
}
`;
React function:
async function createGame() {
try {
const newgame = {
"game_name": "deneme",
"bundle_identifier": "com.magiclab.deneme",
sdkConfigs: [
{ "publisher_id": 5,
"facebook_app_id": "fb12313",
"adjust_app_token": "adjusttoken123123",
}
]
}
await API.graphql(graphqlOperation(createGameConfig, {input: newgame}))
} catch (err) {
console.log('error creating game sdk config:', err)
}
}
Error message:
"The variables input contains a field name 'sdkConfigs' that is not defined for input object type 'CreateGameConfigInput' "
I want to create an array of objects within the object. How to fix input object for graphql ?
You should run two different mutations, one for creating the GameConfig and anorther one for create the SDKConfig it will be something like this
async function createGame() {
try {
const newgame = {
game_name: 'deneme',
bundle_identifier: 'com.magiclab.deneme',
};
const sdk = {
publisher_id: null,
facebook_app_id: 'fb12313',
adjust_app_token: 'adjusttoken123123',
};
const {
data: {
createGameConfig: { id: publisher_id },
},
} = await API.graphql(
graphqlOperation(createGameConfig, { input: newgame })
);
sdk.publisher_id = publisher_id;
await API.graphql(graphqlOperation(createSDKConfig, { input: sdk }));
} catch (err) {
console.log('error creating game sdk config:', err);
}
}
then you will use the id return by the first mutation as an input for the second mutation, this identifier will bound these two entries and when you query any gameConfig it will pull in an array any SDKConfig that their publisher_id matches with the gameConfig.
You could expand these information in this section of the official documentation https://docs.amplify.aws/cli/graphql-transformer/directives#belongs-to
Related
I am having an issue with getting my resolver function to work properly.
Here is my resolver function:
const resolvers = {
Query: {
info: () => `This is the API of a Hackernews Clone`,
// 2
feed: () => links,
},
// 3
Mutation: {
// 2
post: (parent, args) => {
const link = {
id: `link-${idCount++}`,
description: args.description,
url: args.url,
}
links.push(link)
return link
},
deleteLink: (parent, args) => {
const id = args.id
//delete links[id1]
return id
}
}
}
Here is my schema:
type Query {
info: String!
feed: [Link!]!
}
type Mutation {
post(url: String!, description: String!): Link!
deleteLink(id: ID!): Link
}
type Link {
id: ID!
description: String!
url: String!
}
When I use this block to run my deleteLink resolver:
mutation {
deleteLink(
id: "link-1"
){
id
}
}
I get an error like this one:
{
"data": {
"deleteLink": null
},
"errors": [
{
"message": "Cannot return null for non-nullable field Link.id.",
"locations": [
{
"line": 3,
"column": 5
}
],
"path": [
"deleteLink",
"id"
]
}
]
}
Please let me know what I am doing wrong. I am not sure why i get the error: cannot return null for non-nullable field Link.id. Is this a result of the wrong way to query the mutation or is this a result of a bad resolver function?
According to your schema, your deleteLink mutation returns a Link object type and a Link returns id, description, url as required fields.
In your resolver, you are only returning a id and null for all the rest.
The best approach in my opinion would be to change your mutation return type into a String or ID type. When you delete a record, you can't (should not) return the same record, but should return a status/id message.
Something like:
type Mutation {
post(url: String!, description: String!): Link!
deleteLink(id: ID!): String! // Or ID! if you want to return the input id
}
I presently have a page with a dynamically created form. I am having trouble understanding how to manipulate the state and GraphQL query to handle nested queries.
With my present implementation it does not seem to be able to create any new entries. I want to create 1 "target" with several sub "addr" tied to it in one mutation.
This is the state definitions:
state = {
name:'',
addr:[{
mobilepkg:'',
target_url:'',
target_ip: '',
idCars:[]
}],
category:'',
date: '',
location:''
}
Handler for Graph:
handleTarget = async e => {
e.preventDefault()
const { name,
target_url,
target_ip,category,
mobilepkg,date,location } = this.state
let idCars = this.state.idCars
let adras = this.state.addr
await this.props.createTargetMutation({
variables: {
data: {
name,
addr:{
create:
[{
target_url,
target_ip,
mobilepkg,
cars: {
connect: idCars
},
}]
},
date,
location,
category
}
}
})
this.props.history.replace('/targets')
}
}
My create mutation
const CREATE_DRAFT_MUTATION = gql`
mutation CreateTargetMutation($data: TargetCreateInput!) {
createTarget(data: $data) {
id
name
addr
category
}
}
`
GraphQL datamodel
type Target {
id: ID! #unique
name: String!
addr: [Addr!]!
category: String!
date:String!
location:String!
}
type Addr {
id: ID! #unique
target_url:String!
target_ip:String!
mobilepkg:String!
cars: [Car!]!
}
How do I put my ReactJS state which has a nested array into GraphQL?
PS:I am new to GraphQL and ReactJS.
EDIT: In playground im able to create my items but its not working in my actual application.
mutation CreateTargetMutation($data: TargetCreateInput!) {
createTarget(data: $data) {
id
name
addr{
target_ip
target_url
mobilepkg
cars{
id
}
}
category
date
location
}
}
{
"data": {
"name":"testerquery",
"addr": {
"create": {
"target_ip":"123",
"target_url":"123",
"mobilepkg":"asd",
"cars":{"connect":{"id":"cjs3yd83u004a0781jffzaqqr"}}
}
},
"category":"simple",
"date":"2019-03-12",
"location":"kl"
}
}
Bro, you are one the right path. You just need to iterate your values in order to solve this problem. Once you iterate through the values you simply need to make a call to the new array which contains everything and it will work. As your values are nested you will need to add "{}" to your car variable and within that contain your connect since you wish to create new "addr" whilst connecting to existing "car".
let create = []
for(let i=0; i < this.state.addr.length; i++){
create.push({
'mobilepkg':this.state.addr[i].mobilepkg,
'target_url':this.state.addr[i].target_url,
'target_ip':this.state.addr[i].target_ip,
'cars': {
'connect': this.state.addr[i].cars}
})
}
await this.props.createTargetMutation({
variables: {
data: {
name,
addr: {
create
},
category,
date,
location
}
}
})
this.props.history.replace('/targets')
}
}
Your values should now successfully pass into GraphQL and create targets with many "addr" whilst connecting to many "car"
Given I have a schema that has types: User, Comment, Post, Image; Is it possible to determine the GraphQL types being used in a query, given the query and schema?
e.g. if a client had a query
{
user(userName: "username") {
email
comments
}
}
In this case, the query has types User and Comment. Is it possible to determine the programmatically using either the graphql-js or graphql packages for node.js?
For anyone else who runs into this, I found the answer in visit and TypeInfo
Here's a function that takes the GraphQL query (document) and a schema and returns which data types from the schema are being used.
import { visit } from 'graphql/language/visitor'
import { parse } from 'graphql/language'
import { TypeInfo, visitWithTypeInfo } from 'graphql'
import { schema as _schema } from '../src/schema/schema'
const getQueryTypes = (schema, query) => {
const typeInfo = new TypeInfo(schema)
var typesMap = {}
var visitor = {
enter(node) {
typeInfo.enter(node)
typesMap[typeInfo.getType()] = true
},
leave(node) {
typesMap[typeInfo.getType()] = true
typeInfo.leave(node)
}
}
visit(parse(query), visitWithTypeInfo(typeInfo, visitor))
return Object.keys(typesMap)
}
const _query = `
query {
...
}
`
console.log(getQueryTypes(_schema, _query))
Given a valid document string representing some GraphQL operation, you can parse the string into an AST.
import { parse, validate } from 'graphql'
const document = parse(someDocumentString)
// if you want to validate your document to verify it matches your schema
const errors = validate(schema, document)
AFAIK, there's no utility function for getting an array of the types in a document, if that's what you're asking for, but you can just traverse the AST and gather whatever information from it. As an example, here's how GraphiQL does just that to generate a map of variables to their corresponding types:
import { typeFromAST } from 'graphql'
export function collectVariables(schema, documentAST) {
const variableToType = Object.create(null);
documentAST.definitions.forEach(definition => {
if (definition.kind === 'OperationDefinition') {
const variableDefinitions = definition.variableDefinitions;
if (variableDefinitions) {
variableDefinitions.forEach(({ variable, type }) => {
const inputType = typeFromAST(schema, type);
if (inputType) {
variableToType[variable.name.value] = inputType;
}
});
}
}
});
return variableToType;
}
You have to create types using GraphQLObjectType, for example:
export default new GraphQLObjectType(
({
name: 'User',
description: 'Represents a User',
fields: () => ({
_id: {
type: GraphQLNonNull(GraphQLString),
resolve: user => user._id,
},
name: {
type: GraphQLNonNull(GraphQLString),
description: 'Name of the user',
resolve: user => user.name,
},
createdAt: {
type: GraphQLString,
description: '',
resolve: user => user.createdAt.toISOString(),
},
updatedAt: {
type: GraphQLString,
description: '',
resolve: user => user.updatedAt.toISOString(),
},
}),
}: GraphQLObjectTypeConfig<User, GraphQLContext>),
);
Then you can use this UserType on another type, by declaring type: GraphQLNonNull(UserType) for example.
https://graphql.org/graphql-js/constructing-types/
Hope it helps :)
I have a Mongo database with a collection called 'words' which contains documents like this:
{
_id: "xxxx",
word: "AA",
definition: "Cindery lava"
}
I have a node app that I am using to query and display information from the words collection, with GraphQL. I have created a GraphQL schema and Mongoose model, as shown below.
// Schema
const WordType = new GraphQLObjectType({
name: 'Word',
fields: () => ({
id: {type: GraphQLID},
word: { type: GraphQLString },
definition: { type: GraphQLString },
})
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
detailsForWord: {
type: WordType,
args: {word: {type: GraphQLString}},
resolve(parent, args) {
return Word.find({word: args.word});
}
},
allWords: {
type: new GraphQLList(WordType),
resolve(parent, args) {
return Word.find({}).limit(100);
}
}
}
});
// model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const wordSchema = new Schema({
word: String,
definition: String,
});
My problem is that the "allWords" query works perfectly but the "detailsForWord" does not work at all, and I have no idea why.
In GraphiQL I am using these queries:
{
allWords {
word
definition
}
}
... and
{
detailsForWord(word: "AA") {
word
definition
}
}
The former returns records, but the latter always returns the following in GraphiQL:
{
"data": {
"detailsForWord": {
"id": null,
"word": null,
"definition": null
}
}
}
Any ideas why the "detailsForWord" query is failing?
Obviously find returns an array of documents while findOne returns a single document. Therefore the query might be successful you are getting an array no matter what with find. findOne returns the document you are looking for. Your query didn't fail, it returned a promise with an array.
if you do
resolve(parent, args) {
return Word.find({word: args.word}).then(c=>{console.log(c);return c})
}
You'll see an array containing the document in the console.
After a mutation I can't seem to receive the pubSub data on my react front end
I have the following Graphql schema:
type SSiD {
id: ID!
name: String
status: String
hidden: Boolean
}
type Subscription {
SSiDAdded: SSiD
}
On my apolloServer after the mudation I send the pubSub data like this
const data = result.dataValues
data.__typename = 'SSiD'
console.log(data)
context.pubsub.publish('SSiDAdded', data)
That console.log will output:
{ id: 2208,
name: 'FooBar',
hidden: true,
status: 'broadcasting',
updatedAt: 2016-10-27T22:07:09.119Z,
createdAt: 2016-10-27T22:07:09.119Z,
__typename: 'SSiD' }
And finally on my react front end I have the following:
const query = gql`
subscription ssidList{
SSiDAdded{
id
name
hidden
}
}
`
this.subscriptionObserver = this.props.client.subscribe({
query
})
.subscribe({
next (data) {
console.log('The subscription data', data)
},
error (err) {
console.error('Error subscription', err)
}
})
}
On the console.log above subscription data is always null.
Am I wrapping the response wrong or something like that?
Here a couple things to check. On my schema, it has some syntax I'm not seeing in yours at the moment. See the : instant_message after the query strings?
const typeDefinitions = [`
type instant_message {
id: Int
fromID: String
toID: String
msgText: String
}
type Query {
instant_message(id: Int, fromID: String, toID: String, msgText: String): [instant_message]
}
type Mutation {
createIM(
fromID: String!
toID: String!
msgText: String!
): instant_message
}
type Subscription {
# Subscription fires on every comment added
IMAdded(id: Int, fromID: String!, toID: String!): instant_message
}
schema {
query: Query,
mutation: Mutation
subscription: Subscription
}
`];
I've got some different syntax on the client as well:
subscribe(fromID, toID, updateQueryViaSubscription) {
const SUBSCRIPTION_QUERY = gql`
subscription getIMsViaSubscription($fromID: String!, $toID: String!){
IMAdded(fromID:$fromID, toID: $toID){
id,
fromID,
toID,
msgText
}
}
`;
this.subscriptionObserver = this.props.client.subscribe({
query: SUBSCRIPTION_QUERY,
variables: { fromID: this.fromID, toID: this.toID },
}).subscribe({
next(data) {
const newMsag = data.IMAdded;
updateQueryViaSubscription((previousResult) => {
// if it's our own mutation, we might get the subscription result
// after the mutation result.
// if (isDuplicateIM(newComment, previousResult.entry.comments)) {
// return previousResult;
// }
// update returns a new "immutable" list with the new comment
// added to the front.
return update(
previousResult,
{
instant_message: {
$push: [newMsag],
},
}
);
});
},
error(err) {
console.error('err', err); },
});
}
Please check that and let me know if the updated code gets rid of that error yet.
UPDATE: Per our discussion on Slack, you found that you needed your executableSchema to be like this:
const executableSchema = makeExecutableSchema({
typeDefs: typeDefinitions,
resolvers: Resolvers,
connectors: Connectors,
logger: console,
});
export default executableSchema;
Here's the resolver I'm using:
Subscription: {
IMAdded(IMThatWasAdded) {
var ret = IMThatWasAdded;
return ret;
}
}