Apollo GraphQL Client 'Network error: Can't find field XXXX on object undefined' with disableOffline: false - apollo-client

I am having trouble using apollo-client version 2.4.6 to query my AWS AppSync endpoint.
I can successfully query the AWS AppSync endpoint using a curl command, but the exact same
GraphQL executed over the Apollo client is returning "Can't find field getTickets on object undefined."
I am a newby at GraphQL and Apollo. Am I doing something stupid to cause that error? Why does it say NetworkError? Why is the object undefined?
EDIT: I noticed that if I pass disableOffline: true in the constructor to AWSAppSyncClient then it starts working. Why? Why is the default client behavior with disableOffline: false not working?
Here is my super simple schema.graphql deployed at AWS:
schema {
query: Query
}
type Query {
getTickets: [EmmDDavidTickets]
#aws_api_key
}
type EmmDDavidTickets #aws_api_key {
ticketNumber: ID!
pnrNumber: String
}
Here is the curl command that works to query that endpoint at AWS. Note the valid response:
$ curl -X POST -H "x-api-key: --REDACTED--" https://wm3mz6anrjbrfpgbewnyyrio3u.appsync-api.us-east-1.amazonaws.com/graphql -d '{ "query": "query list {\ngetTickets { ticketNumber\n pnrNumber\n }\n}"}'
{"data":{"getTickets":[{"ticketNumber":"12345","pnrNumber":null},{"ticketNumber":"0001020202020","pnrNumber":"ABC123"}]}}
Here is my NodeJS code to execute the same query using Apollo:
const apiKey ='--REDACTED--';
const region = 'us-east-1';
const type = 'API_KEY';
const url = 'https://wm3mz6anrjbrfpgbewnyyrio3u.appsync-api.us-east-1.amazonaws.com/graphql';
const gql = require('graphql-tag');
const query = gql(`
query list {
getTickets {
ticketNumber
}
}`);
// Set up Apollo client
const client = new AWSAppSyncClient({
url: url,
region: region,
auth: {
type: type,
apiKey: apiKey,
},
disableOffline: false
});
client.hydrated().then(function (client) {
//Now run a query
client.query({ query: query })
.then(function logData(data) {
console.log('results of query: ', data);
})
.catch(console.error);
});
Here is the error response from Apollo:
ApolloError: Network error: Can't find field getTickets on object undefined.
at new ApolloError (/Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:85:32)
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:1039:45
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:1411:21
at Array.forEach (<anonymous>)
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:1410:22
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (/Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:1405:26)
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-client/bundle.umd.js:988:35 {
graphQLErrors: [],
networkError: Error: Can't find field getTickets on object undefined.
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:429:27
at Array.forEach (<anonymous>)
at StoreReader.diffQueryAgainstStore (/Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:426:36)
at StoreReader.readQueryFromStore (/Users/dyoung/workspace//appsync_javascript_test/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:401:25)
at processOfflineQuery (/Users/dyoung/workspace//appsync_javascript_test/node_modules/aws-appsync/lib/link/offline-link.js:154:34)
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/aws-appsync/lib/link/offline-link.js:110:28
at new Subscription (/Users/dyoung/workspace//appsync_javascript_test/node_modules/zen-observable/lib/Observable.js:183:34)
at Observable.subscribe (/Users/dyoung/workspace//appsync_javascript_test/node_modules/zen-observable/lib/Observable.js:262:14)
at /Users/dyoung/workspace//appsync_javascript_test/node_modules/aws-appsync/lib/client.js:182:67,
message: "Network error: Can't find field getTickets on object undefined.",
extraInfo: undefined
}

According Apollo Docs on local state management:
We need to write an initial state to the cache before the query is run
to prevent it from erroring out.
I had the almost same error thrown out when I did not initialise the state in the Apollo's InMemoryCache.
To initialize the state for AWSAppSyncClient, you can refer to React-Native + Apollo-Link-State + AWS Appsync : Defaults are not stored in cache, and https://github.com/awslabs/aws-mobile-appsync-sdk-js/pull/96

Related

GraphQL with ApolloServer says "GET query missing." even though I have playground set to true the way another post advised

I am trying to use Postman to hit a graphQL endpoint to teach myself how it works. I have a database with user data prepopulated and two Postman routes that should work but don't work.
The requests I am trying to send via Postman:
(1) Using GraphQL mode under the Body tab
{
User {
first_name
last_name
}
}
(2) using the raw mode under the Body tab
{
user {
first_name
last_name
}
}
In both cases I have correctly set the headers Content-Type to application/graphql. So it's not that.
I found two posts about this while Googling. Both are on StackOverflow.
(1) apollo-server returning GET query missing when playground is disabled
This one says basically, "do this":
const server = new ApolloServer({
introspection: true, // i inserted this line & the next one as specified
playground: true,
typeDefs,
resolvers,
})
(2) GET query missing: Implementing GraphQL Using Apollo On an Express Server
This one references the prior link. It's also for graph-server-express, and I'm using apollo-server-fastify
I also found Apollo Graphql with Fastify who also has "GET query missing." issue but no solution. It says to downgrade to fastify v2 but that's an old answer from 2020. This is 2022, we can do better.
Again the issue is that Postman says "GET query missing." to all my requests.
My server:
async function startApolloServer(typeDefs, resolvers) {
const apolloServer = new ApolloServer({
// introspection: true,
// playground: true, // to resolve "GET query missing." in Postman
typeDefs,
resolvers,
plugins: [
fastifyAppClosePlugin(fastify),
ApolloServerPluginDrainHttpServer({ httpServer: fastify.server }),
ApolloServerPluginLandingPageGraphQLPlayground(),
],
context: ({ request, reply }) => {
//Invaluable for debugging
if (env === "development") {
console.log("GOT A REQUEST: ", request.body);
}
return { knex, reply };
},
});
await apolloServer.start();
fastify
.register(helmet)
.register(require("fastify-sensible"))
.register(require("fastify-healthcheck"))
.register(require("fastify-formbody"))
.register(apolloServer.createHandler());
await fastify.listen(serviceListeningPort);
console.log(
`🚀 Server ready at http://localhost:${serviceListeningPort}${apolloServer.graphqlPath}`
);
}
startApolloServer(typeDefs, resolvers);
Also, the updated version of Apollo Server seems to replace playground: true with ApolloServerPluginLandingPageGraphQLPlayground(), which for me enables a page that says "Loading GraphQL Playground" at the server root but never finishes loading.

How to convert a GraphQL file to a Postman Collection?

I want to convert a GraphQL file to a Postman collection. I tried with a JavaScript library to do that (https://www.npmjs.com/package/graphql-to-postman).
But I'm getting the following error:
Unhandled Rejection (TypeError): Cannot set property
'includeDeprecatedFields' of undefined
function convert() {
var postmanJson = fileReader.result,
fileDownload = require('js-file-download');
const graphQlToPostman = require('graphql-to-postman');
const collection = graphQlToPostman.convert(postmanJson);
fileDownload(
JSON.stringify(collection),
'postman collection',
);
}
This is the function where I used the library.
To convert graphql to postman collection, first you need graphql schema. Graphql schema can be downloaded by running introspection query on the graphql server end point. Here is how to do it.
Install graphql-cli
npm i -g apollo
Download schema from the graphql server
apollo schema:download --endpoint=http://localhost:4000/graphql schema.json
Convert schema into graphql collection
const fs = require('fs')
const converter = require('graphql-to-postman')
const schema = fs.readFileSync('./schema.json')
converter.convert({
type: 'string',
data: schema.toString(),
}, {}, async function (error, result) {
if (error) {
log.error('Conversion failed')
} else {
const outputData = result.output[0].data
fs.writeFileSync('output-collection.json', JSON.stringify(outputData))
console.log('Conversion success');
}
})
I've built an easy-to-use command-line tool that allows you to automatically generate your Postman collection from your GraphQL endpoint. (https://www.npmjs.com/package/graphql-testkit)
It also comes with out-of-the-box support to control the maximum depth and add headers to all the requests in the collection.
Simply doing this, after replacing the endpoint, and adding or removing headers based on your requirement will auto-generate a Postman Collection with support for variables.
graphql-testkit \
--endpoint=https://api/spacex.land/graphql\
--header="Authorization:123,x-ws-system-id=10" \
--maxDepth=4

Unable to query dynamodb GSI in lambda locally

So I added a lambda function category using the amplify CLI, in order to query data from the GSI(Global secondary Index) I created using the #key directive in the graphql schema. Whenever I try mocking the function locally using the amplify mock function <functionName> the callback function of the query keeps on returning null. The function can be seen below
const AWS = require("aws-sdk");
const db = new AWS.DynamoDB.DocumentClient({
region: process.env.REGION,
apiVersion: "2012-08-10",
});
const params = {
// ProjectionExpression: ["province", "gender", "updatedAt", "createdAt"],
ExpressionAttributeValues: {
":provinceVal": "Sichuan",
},
IndexName: "RegistreesByProvince",
KeyConditionExpression: "province = :provinceVal",
TableName: process.env.API_PORTAL_SUBMISSIONSTABLE_NAME,
};
const calculateStatistics = async () => {
try {
const data = await db.query(params).promise();
console.log(data);
} catch (err) {
console.log(err);
}
};
const resolvers = {
Query: {
getStatistics: () => {
return calculateStatistics();
},
},
};
exports.handler = async (event) => {
// TODO implement
const typeHandler = resolvers[event.typeName];
if (typeHandler) {
const resolver = typeHandler[event.fieldName];
if (resolver) {
var result = await resolver(event);
return result;
}
}
}; // };
I then tried to capture the whole event and logged it to the console as can be seen in the calculateStatistics function, which now showed me a bit more explicit error as follows.
{ UnknownEndpoint: Inaccessible host: `dynamodb.us-east-1-fake.amazonaws.com'. This service may not be available in the `us-east-1-fake' region.
at Request.ENOTFOUND_ERROR (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/event_listeners.js:501:46)
at Request.callListeners (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/request.js:688:14)
at ClientRequest.error (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/event_listeners.js:339:22)
at ClientRequest.<anonymous> (/Users/apple/Documents/work/web/portal/amplify/backend/function/calcStatistics/src/node_modules/aws-sdk/lib/http/node.js:96:19)
at ClientRequest.emit (events.js:198:13)
at ClientRequest.EventEmitter.emit (domain.js:448:20)
at TLSSocket.socketErrorListener (_http_client.js:401:9)
at TLSSocket.emit (events.js:198:13)
message:
'Inaccessible host: `dynamodb.us-east-1-fake.amazonaws.com\'. This service may not be available in the `us-east-1-fake\' region.',
code: 'UnknownEndpoint',
region: 'us-east-1-fake',
hostname: 'dynamodb.us-east-1-fake.amazonaws.com',
retryable: true,
originalError:
{ Error: getaddrinfo ENOTFOUND dynamodb.us-east-1-fake.amazonaws.com dynamodb.us-east-1-fake.amazonaws.com:443
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:56:26)
message:
'getaddrinfo ENOTFOUND dynamodb.us-east-1-fake.amazonaws.com dynamodb.us-east-1-fake.amazonaws.com:443',
errno: 'ENOTFOUND',
code: 'NetworkingError',
syscall: 'getaddrinfo',
hostname: 'dynamodb.us-east-1-fake.amazonaws.com',
host: 'dynamodb.us-east-1-fake.amazonaws.com',
port: 443,
region: 'us-east-1-fake',
retryable: true,
time: 2020-08-12T10:18:08.321Z },
time: 2020-08-12T10:18:08.321Z }
Result:
null
Finished execution.
I then did more research and came across this thread about inaccessible-dynamodb-host-when-running-amplify-mock which I followed and tried implementing to but to no avail. Any help on this would be very much appreciated.
PS: It is worth mentioning that I was able to successfully query for this data through the Appsync console, which led me to strongly believe the problem lies in the function itself.
After doing more research and asking around, I finally made sense of the answer that was provided to me on github that
When running mock on a function which has access to a dynamodb
table generated by API. It will populate the env with fake values. If
you would like to mock your lambda function against your deployed
dynamodb table you can edit the values in the sdk client so it can
make the call accurately.
In summary, if you are running things locally, then you wouldn't have access to your backend variables which you might try mocking. I hope this helps someone. Thanks!

How to use ApolloQuery Vue component to query an Elasticsearch endpoint?

I use Apollo Client on my Nuxt projects to query GraphQL endpoints and it works great. But now, I need to query an Elasticsearch endpoint on AWS. How would I query this endpoint using my existing Apollo client?
You should not query the ElasticSearch directly from your Vue component. You should query your ApolloGraph Server that will query your ElasticSearch end point and return the result from that query.
You can query the Elasticsearch from inside your resolver. The ApolloGraphQL resolver accepts an promise as result and will wait for this promise get resolved.
If you have a resolver like this:
const resolvers = {
Query: {
user(parent, args, context, info) {
let bar = args.id;
return queryElasticSearch(bar)
}
}
}
(For reference: https://www.apollographql.com/docs/apollo-server/data/resolvers/)
The ElasticSearch have this client wich you can connect to query your endpoint: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/client-usage.html
and in queryElasticSearch you can have something like this:
async function queryElasticSearch(bar){
const result = await client.search({
index: 'my-index',
body: { foo: bar }
})
return result;
}

Google Cloud Search Query via node.js Error: Invalid JSON payload received

googleapis node.js library is returning the below error when trying to query cloud search API.
Error: Invalid JSON payload received. Unknown name \"requestOptions[searchApplicationId]\": Cannot bind query parameter. Field 'requestOptions[searchApplicationId]' could not be found in request message."
The payload is exactly as documented here, https://developers.google.com/cloud-search/docs/reference/rest/v1/query/search. requestOptions[searchApplicationId] is present and if I remove it I get an error saying searchApplicationId is required.
Code:
const {google} = require('googleapis');
const service = google.cloudsearch({version: 'v1'});
service.query.search({
auth: jwtClient,
requestOptions: {
searchApplicationId: 'searchapplications/default',
debugOptions:{enableDebugging: true}
},
query: 'My query'
}).then((res) => {
console.log(JSON.stringify({results:res.results.length}));
console.log(JSON.stringify({resultsInfo:res.results[0]}));
}).catch((err) => {
console.error('Unexpected error with cloud search API.');
console.error(err.toString());
});
The query works from the API explorer.
https://developers.google.com/apis-explorer/#search/cloudsearch/m/cloudsearch/v1/cloudsearch.query.search?_h=1&resource=%257B%250A++%2522requestOptions%2522%253A+%250A++%257B%250A++++%2522searchApplicationId%2522%253A+%2522searchapplications%252Fdefault%2522%250A++%257D%252C%250A++%2522query%2522%253A+%2522Testing%2522%250A%257D&
Am I missing something simple? Is this an issue with Google's client library? (https://github.com/googleapis/google-api-nodejs-client) Any assistance would be greatly appreciated.
Finally figured it out. Had to wrap the request in a requestBody JSON.
service.query.search({
auth: jwtClient,
requestBody: {
requestOptions: {
searchApplicationId: 'searchapplications/default',
debugOptions:{enableDebugging: true}
},
query: 'My query'
}
})

Resources