This is my first question here, and I just started writing Node JS code recently. Right now I am using NodeJS and EJS as a templating engine for my app. My database is DynamoDB and I wanted to make one the tables Realtime by using AWS Appsync. Using Appsync, I can query and mutate the fields in my table but I cannot seem to subscribe to any mutations. No MQTT websockets are open as well when I call my subscribe.
I tried to follow the example for Appsync subscribe on the Appsync documentation but it does not seem to work.
Note: I have defined my infoID variable already, the type information{} is created in the schema and the mutate and query both work. Its just that the subscription doesn't work and no MQTT websocket is created on the template (is this even possible using NodeJS and EJS?).
My schema is as follows:
type Mutation {
deleteInfo(infoID: String!): information
}
type Subscription {
onDeleteInfo(infoID: String): information
#aws_subscribe(mutations: ["deleteInfo"])
}
and the code I used to query and subscribe is like this:
const query = gql(`
query($infoID: String!){
getInformation(infoID: $infoID) {
infoID
noOfDays
noOfItems
infoName
}
}`);
// Set up a subscription query
const subquery = gql(`
subscription ($infoID: String) {
onDeleteInfo(infoID: $infoID) {
infoID
noOfDays
noOfItems
infoName
}
}`);
const client = new AWSAppSyncClient({
url: url,
region: region,
auth: {
type: type,
credentials: credentials,
}
});
client.hydrated().then(function (client) {
//Now run a query
client.query({ query: query, variables: {infoID : infoID} })
.then(function logData(data) {
console.log('results of query: ', data);
var responseData = data;
res.send(responseData);
})
.catch(console.error);
//Now subscribe to results
const realtimeResults = function realtimeResults(data) {
console.log('realtime data: ', data);
console.log('subcribe is called');
};
const observable = client.subscribe({
query: subquery,
variables: {infoID : infoID} });
observable.subscribe({
next: realtimeResults,
complete: console.log,
error: console.log
});
};
and my mutation code is:
const mutation = gql(`
mutation($infoID: String!){
deleteInfo(infoID: $infoID) {
infoID
noOfDays
noOfItems
infoName
}
}`);
const client = new AWSAppSyncClient({
url: url,
region: region,
auth: {
type: type,
credentials: credentials,
}
});
client.hydrated().then(function (client) {
//Now run a query
client.mutate({ mutation: mutation, variables:{infoID: infoID} })
.then(function logData(data) {
console.log('results of mutate: ', data);
})
.catch(console.error);
});
Thanks to anyone who answers or read or helps in any way!
It sounds like you are successfully connected to AppSync if you can make mutations and queries from your app. To Debug the subscription problem, you will need to look at the network response for the request. The service will send an error message if something is incorrectly setup.
One common thing to check for is whether the subscription is defined in your schema.
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
If this is the case, the error message you would receive when making the subscription request is: Schema is not configured for subscriptions.
Here is a sample code snippet how to call subscription:
// Set up a subscription query
const subquery = gql`
subscription onAccept {
onUpdateDeveloperJob(developerId: "ce261427-84ad-450b-91d1-83b78532dfe6") {
id
jobId
developerId
selected
}
}
`;
// Set up Apollo client
const client = new AWSAppSyncClient({
url,
region,
auth: {
type,
apiKey
}
});
client
.hydrated()
.then(appsyncClient => {
// Now run a query
// appsyncClient
// .query({ query })
// .then(data => {
// console.log('results of query: ', data);
// })
// .catch(console.error);
// Now subscribe to results
const observable = appsyncClient.subscribe({ query: subquery });
const realtimeResults = function realtimeResults(data) {
console.log('realtime data: ', data);
};
observable.subscribe({
next: realtimeResults,
complete: console.log,
error: console.log
});
})
.catch(console.error);
Hope this will be helpful for you.
Related
I'm having a problem with a firebase function, using admin api to read then write to firestore.
See the code below:
I've commented by 2 console logs, which takes between 2-5 minutes to execute. The dataset in firestore is small (only a few records). Any advise on what I'm clearly doing wrong please?
Thanks
(edited as requested with rest of code. This is taken direct from Stripe's github examples)
const processTheOrderApp = express();
processTheOrderApp.post(
'/',
bodyParser.raw({ type: 'application/json' }),
(
request: { headers: { [x: string]: any }; rawBody: any },
response: {
status: (
arg0: number
) => { (): any; new (): any; send: { (arg0: string): any; new (): any } };
json: (arg0: { received: boolean }) => void;
}
) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret);
} catch (err) {
console.log(err);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
addPaymentDataToOrder(session); // Here we can proccess the order data after successfull payment
// (e.g. change payment status in Firebase Database and call another function)
}
// Return a response to acknowledge receipt of the event
response.json({ received: true });
}
);
// Exporting our http function
exports.processTheOrder = functions.https.onRequest(processTheOrderApp);
function addPaymentDataToOrder(session: any) {
console.log('adding payment'); ////between this console log
admin
.firestore()
.collection('orders')
.where('paymentSessionId', '==', session.id)
.limit(1)
.get() // getting the order which matches the session id, should be only one so limited to one result
.then((query: any) => {
console.log('found item'); ////and this console log
const thing = query.docs[0];
var orderDoc = thing.data();
thing.ref.update({
checkedOut: true,
payment: session,
});
});
}
It's slow because if a function in the firebase cloud functions is not called/invoked in 2 minutes, it goes into a cold start. Check out this reference.
https://medium.com/#siriwatknp/cold-start-workaround-in-firebase-cloud-functions-8e9db1426bd3
People, how are you? I have a query, I just implemented my API made with apollo server in an AWS Lambda. I used the official documentation as a guide, but I'm noticing that the context handling varies a bit. I have a doubt with the latter, since I made certain changes and everything works fine locally using "serverless offline", but once I deploy it doesn't. Apparently the authentication context that I generate does not finish reaching my query. If someone can guide me a bit with this, I will be very grateful.
This is my API index:
const { ApolloServer, gql } = require('apollo-server-lambda');
const typeDefs = require('./db/schema');
const resolvers = require('./db/resolvers');
const db = require('./config/db');
const jwt = require('jsonwebtoken');
require('dotenv').config({ path: 'variables.env' });
db.conectDB();
// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
endpoint: "/graphql"
},
context: ({ event, context }) => {
try {
const token = event.headers['authorization'] || '';
if(token){
context.user = jwt.verify(token.replace('Bearer ',''), process.env.KEY_TOKEN);
}
return {
headers: event.headers,
functionName: context.functionName,
event,
context,
}
} catch (error) {
console.error(error);
}
}
});
exports.graphqlHandler = server.createHandler({
cors: {
origin: '*',
credentials: true,
},
});
This is my query:
getUserByToken: async (_, {}, { context }) => {
if(context)
throw new Error((context ? 'context' : '') + ' ' + (context.user ? 'user' : ''));
let user = await db.findOne('users',{ _id: ObjectId(context.user._id) });
if(user.birthdate)
user.birthdate = user.birthdate.toString();
if(user.password)
user.password = true;
else
user.password = false;
return user;
}
My API response:
API response
From what I can see, you're not calling getUserByToken in your context. Is that correct? So, I'm not sure how you're encountering this error.
Can I give you some pointers?
Connecting to your DB is probably (or it should be) asynchronous. For that, I'd run your code like this:
db.connect()
.then(() => {
... handle your request in here
})
.catch(console.error);
I think you meant to call your getUserByToken in this line:
context.user = jwt.verify(token.replace('Bearer ',''), process.env.KEY_TOKEN);
Any idea how we can write graphQL resolver so that I can cache API response in the redis and on the next call it takes data from the redis instead of hitting the backend API response ?
Here user name is unique in the API. i.e. 1. getify and 2. bradtraversy
/// Middleware Function to Check Cache
checkCache = (username) => {
redis.get(username, (err, data) => {
if (err) {
console.log("====111111111111==========");
console.log(err);
}
if (data !== null) {
personInfo = data;
// console.log(data);
console.log("============222222222222=========");
return personInfo;
}
});
};
// Running Code
const resolvers = {
Query: {
getPerson: async (_, { username }) => {
await checkCache(username);
console.log(username);
if(null != personInfo) {
console.log("=======333333333=======")
console.log(personInfo);
return JSON.parse(personInfo);
}
else {
console.log("Fetching Data from API")
console.log(username);
const response = await fetch(`https://api.github.com/users/${username}`).then(response => response.json());
redis.SETEX(username, 300, JSON.stringify(response));
// console.log(response);
return response;
}
}
}
I think you might want something like Apollo's Data Sources, assuming you are getting data from other REST APIs. They have a section specifically about using Redis/memcached as a cache instead of an in-memory one.
So the gist of this answer is, if you're using Apollo Server and wanting to cache responses from REST APIs, you can use Data Sources with apollo-server-cache-redis
I have some code using GOT querying a graphQL endpoint:
// set up params for call to weather cache
const queryQL = `
query weather {
weather(where: {idLatLong: {_eq: "${latLong}"}}) {
id
idLatLong
updated_at
lat
long
requestedByUserId
data
created_at
}
}
`
const query = {query: queryQL};
const options = {
headers: {
'X-Hasura-Admin-Secret': process.env.HASURA_KEY
},
responseType: 'json'
}
// see if there's an existing record for the lat long
try {
const response = await got.post(process.env.GQL_ENDPOINT, query, options);
console.log('query weather hasura');
console.log(response.body);
} catch(error) {
console.log(error);
}
I am getting a response from Hasura {"errors":[{"extensions":{"path":"$","code":"invalid-headers"},"message":"Missing Authorization header in JWT authentication mode"}]}
How do I see what GOT is sending out to the GQL endpoint? FYI, this call works fine in the GQL console and also in Postman.
The got() library has hooks that allow you to see the headers it's about to send. Here's an example that you can run and then insert the same thing into your code:
const got = require('got');
got("http://www.google.com", {
hooks: {
beforeRequest: [function(options) {
console.log(options);
}]
}
}).then(result => {
let i = 1;
}).catch(err => {
console.log(err);
});
You can also get a network analyzer like Wireshark to put on your client computer and watch the actual network traffic.
Recently Apollo Client released a websocket subscription feature, but so far I've only seen it used by launching a query using subscribeToMore inside the componentWillMount lifecycle hook.
Here is an example taken from https://dev-blog.apollodata.com/tutorial-graphql-subscriptions-client-side-40e185e4be76#0a8f
const messagesSubscription = gql`
subscription messageAdded($channelId: ID!) {
messageAdded(channelId: $channelId) {
id
text
}
}
`
componentWillMount() {
this.props.data.subscribeToMore({
document: messagesSubscription,
variables: {
channelId: this.props.match.params.channelId,
},
updateQuery: (prev, {subscriptionData}) => {
if (!subscriptionData.data) {
return prev;
}
const newMessage = subscriptionData.data.messageAdded;
// don't double add the message
if (!prev.channel.messages.find((msg) => msg.id === newMessage.id)) {
return Object.assign({}, prev, {
channel: Object.assign({}, prev.channel, {
messages: [...prev.channel.messages, newMessage],
})
});
} else {
return prev;
}
}
});
}
But subscribeToMore is specific to Apollo Client React integration. In VanillaJS there is a watchQuery, but it's stated it should not be used for subscriptions. There is also a subscribe that might be what I'm searching for, but is not documented.
Is there any way using Apollo GraphQL client to handle subscriptions, without being inside a React Component?
Turns out it is the subscribe method. I found a description here: https://dev-blog.apollodata.com/graphql-subscriptions-in-apollo-client-9a2457f015fb#eeba
ApolloClient.subscribe takes a query and variables, and returns an observable. We then call subscribe on the observable, and give it a next function which will call updateQuery. updateQuery specifies how we want our query result to be updated when given the subscription result.
subscribe(repoName, updateQuery){
// call the "subscribe" method on Apollo Client
this.subscriptionObserver = this.props.client.subscribe({
query: SUBSCRIPTION_QUERY,
variables: { repoFullName: repoName },
}).subscribe({
next(data) {
// ... call updateQuery to integrate the new comment
// into the existing list of comments
},
error(err) { console.error('err', err); },
});
}