Graphql returning Cannot return null for non-nullable field Query.getDate. As I am new to graphql I want to know is my approach is wrong or my code? - aws-lambda

I have created resolver, schema and handler which will fetch some record from dynamoDB. Now when I perform query then I am getting "Cannot return null for non-nullable field Query.getDate" error. I would like to know whether my approach is wrong or there is any change required in code.
My code : https://gist.github.com/vivek-chavan/95e7450ff73c8382a48fb5e6a5b96025
Input to lambda :
{
"query": "query getDate {\r\n getDate(id: \"0f92fa40-8036-11e8-b106-952d7c9eb822#eu-west-1:ba1c96e7-92ff-4d63-879a-93d5e397b18a\") {\r\n id\r\n transaction_date\r\n }\r\n }"
}
Response :
{
"errors": [
{
"message": "Cannot return null for non-nullable field Query.getDate.",
"locations": [
{
"line": 2,
"column": 7
}
],
"path": [
"getDate"
]
}
],
"data": null
}
Logs of lambda function :
[ { Error: Cannot return null for non-nullable field Query.getDate.
at completeValue (/var/task/node_modules/graphql/execution/execute.js:568:13)
at completeValueCatchingError (/var/task/node_modules/graphql/execution/execute.js:503:19)
at resolveField (/var/task/node_modules/graphql/execution/execute.js:447:10)
at executeFields (/var/task/node_modules/graphql/execution/execute.js:293:18)
at executeOperation (/var/task/node_modules/graphql/execution/execute.js:237:122)
at executeImpl (/var/task/node_modules/graphql/execution/execute.js:85:14)
at execute (/var/task/node_modules/graphql/execution/execute.js:62:229)
at graphqlImpl (/var/task/node_modules/graphql/graphql.js:86:31)
at /var/task/node_modules/graphql/graphql.js:32:223
at graphql (/var/task/node_modules/graphql/graphql.js:30:10)
message: 'Cannot return null for non-nullable field Query.getDate.',
locations: [Object],
path: [Object] } ],
data: null }
2019-02-25T10:07:16.340Z 9f75d1ea-2659-490b-ba59-5289a5d18d73 { Item:
{ model: 'g5',
transaction_date: '2018-07-05T09:30:31.391Z',
id: '0f92fa40-8036-11e8-b106-952d7c9eb822#eu-west-1:ba1c96e7-92ff-4d63-879a-93d5e397b18a',
make: 'moto' } }
Thanks in advance!

This is your code:
const data = {
getDate(args) {
var params = {
TableName: 'delete_this',
Key: {
"id": args.id
}
};
client.get(params, function(err,data){
if(err){
console.log('error occured '+err)
}else{
console.log(data)
}
});
},
};
const resolvers = {
Query: {
getDate: (root, args) => data.getDate(args),
},
};
You're seeing that error because getDate is a a Non-Null field in your schema, but it is resolving to null. Your resolver needs to return either a value of the appropriate type, or a Promise that will resolve to that value. If you change data like this
const data = {
getDate(args) {
return {
id: 'someString',
transaction_date: 'someString',
}
}
}
you'll see the error go away. Of course, your goal is to return data from your database, so we need to add that code back in. However, your existing code utilizes a callback. Anything you do inside the callback is irrelevant because it's ran after your resolver function returns. So we need to use a Promise instead.
While you can wrap a callback with Promise, that shouldn't be necessary with aws-sdk since newer versions support Promises. Something like this should be sufficient:
const data = {
getDate(args) {
const params = //...
// must return the resulting Promise here
return client.get(params).promise().then(result => {
return {
// id and transaction_date based on result
}
})
}
}
Or using async/await syntax:
const data = {
async getDate(args) {
const params = //...
const result = await client.get(params).promise()
return {
// id and transaction_date based on result
}
}
}

Related

How to filter the data from dynamo db when the key is not a partition or Sort key with Node.js and typescript?

My table looks like [alias, inheritedLdap, LdapGroup ] here alias is the string and the LdapGroup is the List form eg: [{S:aws}]. So basically my use case is to get the list of aliases whose ldapGroup is aws. Here the alias is the partition key, we don't have the sort key. So I need to write a method which takes the ldapGroup as the parameter and filter the list of the alias when the ldapGroup is aws. But ldapGroup doesn't contain scalar values. I tried to implement the code but its failing when I try to compile,
public async getMemberList(): Promise<any> {
const input: any = {
TableName: UserInfoDao.TABLE_NAME, // use this from the constants
ProjectionExpression: "alias",
FilterExpression: "#l = :ldapGroups",
ExpressionAttributeNames: {
"#l": "ldapGroups"
},
ExpressionAttributeValues: {
":ldapGroups": "PPOA"
}
};
try {
const ddbClient = DynamDBClient.getInstance();
return await ddbClient.scan(input);
} catch (error) {
const message = `ERROR: Failed to retrieve alias for given ldapGroups:
ERROR: ${JSON.stringify(error)}`;
error.message = message;
throw error;
}
}
But when I use the ScanCommandOutput and ScanCommadInput in my code instead of any, its shows the error that the
Type 'Record<string, AttributeValue>[] | undefined' is not assignable to type 'ScanCommandInput'. Type 'undefined' is not assignable to type 'ScanCommandInput'
Property '$metadata' is missing in type 'Request<ScanOutput, AWSError>' but required in type 'ScanCommandOutput'.
Can someone help me with this one.
I am expecting whether my approach is correct or not
This works for me, I made some edits you your example:
import { DynamoDBClient } from "#aws-sdk/client-dynamodb";
import { ScanCommand, ScanCommandInput } from "#aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({
region: 'eu-west-1',
});
class MyClass {
public getMemberList(): Promise<any> {
const input: ScanCommandInput = {
TableName: 'Test1',
// ProjectionExpression: "alias",
FilterExpression: "contains(#l, :ldapGroups)",
ExpressionAttributeNames: {
"#l": "ldapGroups"
},
ExpressionAttributeValues: {
":ldapGroups": "aws"
}
};
try {
return client.send(new ScanCommand(input))
} catch (error) {
const message = `ERROR: Failed to retrieve alias for given ldapGroups: ERROR: ${JSON.stringify(error)}`;
error.message = message;
throw error;
}
}
}
const c = new MyClass();
c.getMemberList().then(res => console.log(res)).catch(err => console.log(err));

Getting "Cannot return null" error while querying API using Graphql? [duplicate]

This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 3 years ago.
I am not sure what I am doing wrong. I cant query this that seems to be a very easy API using GraphQL?
https://data.brreg.no/enhetsregisteret/enhet.json
I have tried different solutions. But I get the same error/problem.
import { ApolloServer, gql, IResolverObject } from "apollo-server";
import fetch from "node-fetch";
const typeDefs = gql`
type Company {
organisasjonsnummer: Int
navn: String
}
type Query {
companies: [Company!]!
}
`;
const resolvers: IResolverObject = {
Query: {
companies: async () => {
const response = await fetch(
"https://data.brreg.no/enhetsregisteret/enhet.json"
);
const data = await response.json();
return data.results;
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Query used in GraphQL Playground:
{
companies{
organisasjonsnummer
}
}
Error received from GraphQL Playground:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Query.companies.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"companies"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Cannot return null for non-nullable field Query.companies.",
" at completeValue (F:\\apollo-datasource-rest-example\\node_modules\\graphql\\execution\\execute.js:573:13)",
" at F:\\apollo-datasource-rest-example\\node_modules\\graphql\\execution\\execute.js:505:16",
" at process._tickCallback (internal/process/next_tick.js:68:7)"
]
}
}
}
],
"data": null
}
Because the JSON return from that link does not have a field called "results" , so null is resolved for the companies root query but it is expected to have a non-nullable value because its schema is defined as [Company!]!.
You should return data.data from this resolver based on the JSON structure return from that link.

Building GraphQL Resolver to Return List of Strings -- Receiving [object Object] Instead of Strings

I am developing a web application that queries an OrientDB Graph Database using GraphQL. It uses Apollo Server to resolve incoming GraphQL queries.
I want to build a query that will simply return the 'name' field for each "Topic" Object as a list of Strings. e.g.:
{
"data": {
"allTopicNames": [
"Topic 1",
"Topic 2",
"Topic 3",
"Topic 4"
]
}
}
To do so, I created a Type Definition:
// Imports: GraphQL
import { gql } from 'apollo-server-express';
// GraphQL: TypeDefs
const TYPEDEFS = gql`
type Query {
allTopics: [Topic]
topic(name: String): [Topic]
allTopicNames: [String] //This is the new Type Definition -- we want a list of Strings
}
type Topic {
name: String
}
`;
// Exports
export default TYPEDEFS;
And the associated Resolver:
//Connect to OrientDB
var OrientJs = require('orientjs');
var server = OrientJs({
host: "localhost",
port: "2424",
username: "root",
password: "root"
});
var db = server.use({
name: 'database',
username: 'root',
password: 'root'
});
// GraphQL: Resolvers
const RESOLVERS = {
Query: {
allTopics: () => {
return db.query('SELECT FROM Topic ORDER BY name');
},
allTopicNames: () => {
return db.query('SELECT name FROM Topic ORDER BY name'); //This is the new resolver
},
topic: (obj, args) => {
return db.query('SELECT FROM Topic WHERE name=\'' + args.name + '\' LIMIT 1');
}
}
};
// Exports
export default RESOLVERS;
However, when I try to implement the above Type Definition and Resolver, I receive a list of strings which are all "[object Object]" instead of the actual strings:
{
"data": {
"allTopicNames": [
"[object Object]",
"[object Object]",
"[object Object]",
"[object Object]"
]
}
}
I tried to add some code to the resolver that would iterate through each object and create a proper list of Strings to return:
// GraphQL: Resolvers
const RESOLVERS = {
Query: {
allTopics: () => {
return db.query('SELECT FROM Topic ORDER BY name');
},
allTopicNames: () => {
let the_list_of_records = db.query('SELECT name FROM Topic ORDER BY name').then(res => {
let the_list_of_names = []; //We'll return a List of Strings using this
for(var i = 0; i < res.length; i++){
the_list_of_names.push(res[i]['name']);
}
console.log(the_list_of_names);
return the_list_of_names;
});
},
topic: (obj, args) => {
return db.query('SELECT FROM Topic WHERE name=\'' + args.name + '\' LIMIT 1');
}
}
};
But this didn't work, resulting in a null value being returned instead:
{
"data": {
"allTopicNames": null
}
}
I'm frankly confused as to why I can't get a simple list of Strings to populate via this resolver. Perhaps I'm missing something obvious -- any insight is greatly appreciated!
Your initial approach didn't work as expected because you were returning an array of objects. Your second attempt returns null because you don't return anything inside your resolver. Your resolver should always return a value or a Promise that will resolve to that value, otherwise the resolved value for the field will always be null.
The value of the_list_of_records will be a Promise, so you can just return that and that should be sufficient. But we can make this code a little easier to read using map like this:
allTopicNames: () => {
return db.query('SELECT name FROM Topic ORDER BY name').then(res => {
return res.map(topic => topic.name)
})
}
// using async/await
allTopicNames: async () => {
await topics = await db.query('SELECT name FROM Topic ORDER BY name')
return topics.map(topic => topic.name)
}

I need help understanding Relay OutputFields, getFatQuery

This is the code from official docs of relay, This is for GraphQLAddTodoMutation
const GraphQLAddTodoMutation = mutationWithClientMutationId({
name: 'AddTodo',
inputFields: {
text: { type: new GraphQLNonNull(GraphQLString) },
},
outputFields: {
todoEdge: {
type: GraphQLTodoEdge,
resolve: ({localTodoId}) => {
const todo = getTodo(localTodoId);
return {
cursor: cursorForObjectInConnection(getTodos(), todo),
node: todo,
};
},
},
viewer: {
type: GraphQLUser,
resolve: () => getViewer(),
},
},
mutateAndGetPayload: ({text}) => {
const localTodoId = addTodo(text);
return {localTodoId};
},
});
I think mutateAndGetPayload executes first then outputFields? since it used localTodoId object as parameter, I see localTodoId object returned from mutateAndGetPayload.
and this is the code for relay mutation.please look at the getFatQuery
export default class AddTodoMutation extends Relay.Mutation {
static fragments = {
viewer: () => Relay.QL`
fragment on User {
id,
totalCount,
}
`,
};
getMutation() {
return Relay.QL`mutation{addTodo}`;
}
getFatQuery() {
return Relay.QL`
fragment on AddTodoPayload #relay(pattern: true) {
todoEdge,
viewer {
todos,
totalCount,
},
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'viewer',
parentID: this.props.viewer.id,
connectionName: 'todos',
edgeName: 'todoEdge',
rangeBehaviors: ({status}) => {
if (status === 'completed') {
return 'ignore';
} else {
return 'append';
}
},
}];
}
getVariables() {
return {
text: this.props.text,
};
}
getOptimisticResponse() {
return {
// FIXME: totalCount gets updated optimistically, but this edge does not
// get added until the server responds
todoEdge: {
node: {
complete: false,
text: this.props.text,
},
},
viewer: {
id: this.props.viewer.id,
totalCount: this.props.viewer.totalCount + 1,
},
};
}
}
I think the todoEdge is from the outputFields from GraphQL? I see a viewer query on it, why does it need to query the viewer? How do I create a getFatQuery? I would really appreciate if someone help me understand this more and about Relay mutation.
mutateAndGetPayload executes then returns the payload to the outputFields
mutationWithClientMutationId
Source-Code
starWarsSchema example
mutationWithClientMutationId
inputFields: defines the input structures for mutation, where the input fields will be wraped with the input values
outputFields: defines the ouptput structure of the fields after the mutation is done which we can view and read
mutateAndGetPayload: this function is the core one to relay mutations, which performs the mutaion logic (such as database operations) and will return the payload to be exposed to output fields of the mutation.
mutateAndGetPayload maps from the input fields to the output fields using the mutation
operation. The first argument it receives is the list of the input parameters, which we can read to perform the mutation action
The object we return from mutateAndGetPayload can be accessed within the output fields
resolve() functions as the first argument.
getFatQuery() is where we represent, using a GraphQL fragment, everything
in our data model that could change as a result of this mutation

GraphQL how to mutate data

I have a basic schema for mutating some data which looks like
const schema = new graphql.GraphQLSchema({
mutation: new graphql.GraphQLObjectType({
name: 'Remove',
fields: {
removeUser: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve(_, args) {
const removedData = data[args.id];
delete data[args.id];
return removedData;
},
},
},
})
});
Looking around google I cant find a clear example of the example query which needs to be sent to mutate.
I have tried
POST -
localhost:3000/graphql?query={removeUser(id:"1"){id, name}}
This fails with error:
{
"errors": [
{
"message": "Cannot query field \"removeUser\" on type \"Query\".",
"locations": [
{
"line": 1,
"column": 2
}
]
}
]
}
In order to post requests from the front-end application it is recommended to use apollo-client package. Say i wanted to validate a user login information:
import gql from 'graphql-tag';
import ApolloClient, {createNetworkInterface} from 'apollo-client';
client = new ApolloClient({
networkInterface: createNetworkInterface('http://localhost:3000/graphql')
});
remove(){
client.mutate({
mutation: gql`
mutation remove(
$id: String!
) {
removeUser(
id: $id
){
id,
name
}
}
`,
variables: {
id: "1"
}
}).then((graphQLResult)=> {
const { errors, data } = graphQLResult;
if(!errors && data){
console.log('removed successfully ' + data.id + ' ' + data.name);
}else{
console.log('failed to remove');
}
})
}
More information about apollo-client can be found here
Have you tried using graphiql to query and mutate your schema?
If you'd like to create a POST request manually you might wanna try to struct it in the right form:
?query=mutation{removeUser(id:"1"){id, name}}
(Haven't tried POSTing myself, let me know if you succeeded, i structured this out of the url when using graphiql)
You have to explicitly label your mutation as such, i.e.
mutation {
removeUser(id: "1"){
id,
name
}
}
In GraphQL, if you leave out the mutation keyword, it's just a shorthand for sending a query, i.e. the execution engine will interpret it as
query {
removeUser(id: "1"){
id,
name
}
}
cf. Section 2.3 of the GraphQL Specification
const client = require("../common/gqlClient")();
const {
createContestParticipants,
} = require("../common/queriesAndMutations");
const gql = require("graphql-tag");
const createPartpantGql = async (predictObj) => {
try {
let resp = await client.mutate({
mutation: gql(createContestParticipants),
variables: {
input: {
...predictObj,
},
},
});
let contestParticipantResp = resp.data.createContestParticipants;
return {
success: true,
data: contestParticipantResp,
};
} catch (err) {
console.log(err.message)
console.error(`Error creating the contest`);
return {
success: false,
message: JSON.stringify(err.message),
};
}
};

Resources