I'm working in TypeScript with the KafkaJS library locally, with a single kafka broker. I've connected a producer successfully, have verified that my topic was created, and am generating messages with:
const changeMessage = {
key: id,
value: JSON.stringify(person),
headers: {
changeType: status,
},
};
Now when I go to send the message:
try {
const sendResponse = await producer.send({
topic: topicName2,
messages: [changeMessage],
});
log.responseFragment(
{ id, topicName2 },
`Sending changed/added person ${id} to topic ${topicName2}`
);
} catch (error) {
log.error(
{ error }, `Could not send personChangedAdded ${id} to topic ${topicName2}`
);
}
Here's the error that I get back:
Could not send personChange to topic topicName2
error: {
"name": "KafkaJSError",
"retriable": true
}
I had failed to define log.responseFragment(), and when I changed it to a simple log.info() the problem was resolved.
Related
I'm trying to send notifications based on business logic that runs (on nodejs) on my server via a cron.
Issue
Notifications aren't appearing on the device.
Description
I'm using the firebase admin node package.
My code looks something like this
import admin from "firebase-admin";
import serviceAccount from "../../firebase-admin.json" assert { type: 'json' };
import { getMessaging } from 'firebase-admin/messaging';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
...
console.log(message);
await getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
My log output is something like this
{
notification: {
title: 'This is a string',
body: 'This is another string'
},
token: 'aLphaNumeric:reallyLongAlphaNumericWithDashesAndUnderscores'
}
Successfully sent message: projects/<project-name>/messages/<id>
Everything I'm seeing suggests this should be sent!
sendMulticast and the Admin FCM APIs allow you to multicast a message to a list of device registration tokens. You can specify up to 500 device registration tokens per invocation.
sendMulticast take 2 arguments as input, 1st one is notification which contains the title and body of the message.
The other argument is fcmTokens with type array, so you must pass that argument as array even though there is only one fcmToken
//Import the file where you have imported the service file.
const adminApp = require("../firebase/firebaseConfig");
const notificationToAll = (title, body, tokens) => {
var notibody = {
notification: {
title: title,
body: body,
},
tokens: tokens,
};
return new Promise((resolve, reject) => {
adminApp
.messaging()
.sendMulticast(notibody)
.then((response) => {
console.log(response.responses);
if (response.responses[0].error != undefined) {
console.log(JSON.stringify(response.responses[0].error));
}
resolve(response);
})
.catch((error) => {
console.log(JSON.stringify(error));
reject(error);
});
});
};
module.exports = notificationToAll;
app.js
const notificationToAll = require("./helper/notification");
notificationToAll(
"This is a string",
`This is another string`,
["aLphaNumeric:reallyLongAlphaNumericWithDashesAndUnderscores"]
)
This is tested code and working in a live environment.
I am trying to verify Subscription Purchase from Android App ( Google Play ) on my server side using node and google-play-billing-validator package. I have followed the instruction and added services account along with accounts permission ( tried both only account and admin ) but its still returns me with insufficient permissions . I don't know why its doing that and whats wrong .
var Verifier = require('google-play-billing-validator');
var options = {
"email": '###########################',
"key": "PRIVATE",
};
try {
var verifier = new Verifier(options);
let receipt = {
packageName: "com.tech.ichat",
productId:
"mychat_bms1",
purchaseToken: "hajlffdcmmkdijnilkogpih.AO-J1OwYTQjVf57Exl8eJBRVNo4VLfwlWIOJykDfyASPLx9YbxvWwP0qDqls14Llcyt8cyslTCT4fN-Xy-0Vg-9BETVnTrxpPQ"
};
let promiseData = verifier.verifySub(receipt)
promiseData.then(function (response) {
// Yay! Subscription is valid
// See response structure below
console.log('API SUCCESS RESPONSE', error);
})
.then(function (response) {
// Here for example you can chain your work if subscription is valid
// eg. add coins to the user profile, etc
// If you are new to promises API
// Awesome docs: https://developers.google.com/web/fundamentals/primers/promises
})
.catch(function (error) {
// Subscription is not valid or API error
// See possible error messages below
console.error(error);
});
} catch (error) {
console.error('Exception Handled', error);
}
response I got .
{
isSuccessful: false,
errorCode: 401,
errorMessage: 'The current user has insufficient permissions to perform the requested operation.'
}
I'm fetching a query to a GraphQL server that throws an error if something goes wrong. Now, I want to have access to that error but instead Apollo Client is showing me the general Error: Network error: Response not successful: Received status code 500.
By adding the onError property to my query, I can see the Error object in the console but only if it's wrapped in {}, otherwise it's just text.
This is my code:
const { data, loadMore, loading, error, retry } = useRetryableQuery<
GetDevices,
DevicesQueryVariables
>(devicesQueries.GetDevices, {
onError: (networkError) => {
console.log(networkError); //this will show me the error as a whole text and not an object
console.log({ networkError }) // this will show me an object which its properties I cannot access
},
errorPolicy: "all",
notifyOnNetworkStatusChange: true,
paginationPath: ["paging"],
itemsPaths: [["filteredDevices", "rows"]],
variables: {
...queryVariables,
connectionPaging: {
offset: 0,
limit: PAGE_SIZE,
},
},
});
How can I access the properties nested inside the networkError object?
First screenshot is without {}
Second one is with {}
Thanks in advance!
The issue was related to this Apollo Client issue: useQuery() refetch throws on error instead of flowing through hook, so getting the error from the ApolloProvider directly allowed me to access the error sent from the backend:
const errorLink = useMemo(
() =>
onError((error) => {
const message = getApolloError({ graphQLErrors: error.graphQLErrors
});
if (message) {
localStorage.setItem("error", message);
} else {
localStorage.removeItem("error");
}
if (message === "Session expired.") {
logout(true);
}
}),
[logout]
);
const client = useMemo(
() =>
new ApolloClient({
cache,
link: ApolloLink.from([errorLink, authLink, httpLink]),
defaultOptions: {
watchQuery: {
fetchPolicy: "network-only",
},
query: {
fetchPolicy: "network-only",
},
},
}),
[httpLink, errorLink]
);
I was starting with GraphQL and I was unable to comprehend how we can throw errors in GraphQL
I went through a couple of articles on the web but almost all of them use Apollo and the code-structure looks very different than how I work.
Consider this piece of code, here where I am making a mutation, now how can send a response message with error and change headers status message in case of error?
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(error)
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(err)
}
}
}
}
What I have faced in one of my projects, it is hard to set the status code of the response. So, I made some custom error response to identify correct statusCode using express-graphql
Below is the example (What I have used in one of my projects):
--------app.js file--------
const graphqlHTTP = require('express-graphql')
app.use('/graphql', (req, res) => {
graphqlHTTP({
schema: GraphQLSchema, //A GraphQLSchema instance from GraphQL.js. A schema must be provided.
graphiql: true,
context: { req },
formatError: (err) => {
const error = getErrorCode(err.message)
return ({ message: error.message, statusCode: error.statusCode })
}
})(req, res)
})
--------getErrorCode function implementation--------
const { errorType } = require('../constants')
const getErrorCode = errorName => {
return errorType[errorName]
}
module.exports = getErrorCode
--------Constant.js file--------
exports.errorName = {
USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
SERVER_ERROR: 'SERVER_ERROR'
}
exports.errorType = {
USER_ALREADY_EXISTS: {
message: 'User is already exists.',
statusCode: 403
},
SERVER_ERROR: {
message: 'Server error.',
statusCode: 500
}
}
Now, we are ready to use our setup.
From your query or mutation, you need to require constant file and return custom error:
const { errorName } = require('../constant')
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(errorName.USER_ALREADY_EXISTS) // Here you can use error from constatnt file
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(errorName.SERVER_ERROR) // Here you can use error from constatnt file
}
}
}
}
--------Error response--------
{
error: [{
"statusCode": 403,
"message": "User is already exists."
}],
data: null
}
We just need to write custom error handling from FS side too.
Note:- formatError: is deprecated and replaced by customFormatErrorFn. It will be removed in version 1.0.0. You can refer customFormatErrorFn.
graphql should be an application level layer that shouldn't (see last paragraph why shouldn't and not doesn't) require http to work. Although in 99% of cases it runs on top of http, because of how convenient it is to do so, graphql is itself a layer 7 protocol.
What does that mean in your case? Well, it means you should not mix concepts from HTTP/REST with concepts from graphql and focus on the latter. The headers error code is a HTTP/REST concept, graphql sends errors in the errors field of the response and the nodejs implementation already catches all your errors and adds them to the list. The HTTP status will be always 200, and your clients shouldn't care and consume your graphql api and not a mix of REST with graphql.
That being said, there are couple of things that REST over HTTP does better. So people, including the developers of Apollo, kinda mixed concepts too, mainly because the graphql standard is not complete (aka, it doesn't have a standard/rule for solving all the problems you might encounter while building an API), so people improvised. I wouldn't recommend graphql yet for any serious project.
Reference
You can specify an error function inside graphqlHTTP like this:
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
customFormatErrorFn: err => {
try {
err.details = JSON.parse(err.message);
err.message = Array.isArray(err.details.error) ? err.details.error.join(",") : err.details.error;
return err;
} catch {
return err;
}
}
}));
where err.message might contain a JSON object or a string.
you can use those function to generate specific client and server error functions:
const clientError = error => new Error(JSON.stringify({
success: false,
code: 400,
error
}));
const serverError = ({ name, message, stack }) => new Error(JSON.stringify({
success: false,
error: "Server Error",
code: 500,
name,
message,
stack
}));
const userValidationError = err => {
if (err.name === "ValidationError") return clientError(Object.values(err.errors).map(({ message }) => message));
return serverError(err);
}
module.exports = {
clientError,
serverError,
userValidationError
};
userValidationError function is useful if you have a mongodb validation error.
so that you would use it inside resolve function like this:
try {
const createdDocument = await MongooseDoc.create(data);
return createdDocument;
} catch (err) {
throw userValidationError(err);
}
the response would be
{
"errors": [
{
"message": "error details 1,error details 2",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"document"
],
"details": {
"success": false,
"code": 400,
"error": [
"error details 1",
"error details 2"
]
}
}
],
"data": {
"document": null
}
}
if you want to throw a clientError you throw it outside try catch.
Hopefully this code helps someone send dynamic error messages in graphql.
while sending push notification i got ( Uncaught (in promise) ReferenceError: require is not defined(…)) error.here is my code
const endPoint = subscription.endpoint.slice(subscription.endpoint.lastIndexOf('/')+1);
console.log(endPoint);
var gcm = require('node-gcm');
var message = new gcm.Message({
notification: {
title: "Hello, World",
icon: "ic_launcher",
body: "This is a notification that will be displayed ASAP.",
tag:"hello"
}
});
var regTokens = [endPoint];
var sender = new gcm.Sender('AIzaSyD9Bcxd_MQZFoGjO1y_hPm-xUdgnM25Ny4'); //API Key
// Now the sender can be used to send messages
sender.send(message, { registrationTokens: regTokens }, function (error, response) {
if (error) {
console.error(error);
res.status(400);
}
else {
console.log(response);
res.status(200);
}
});
})
})
}
Screenshot of error
enter image description here
This code uses require, so it looks to me like you're trying to use node code in the browser. To do that you'll need to use something like Browserify, although I'm not sure that's going to work for node-gcm as it may have certain requirements about sending network requests without cross origin restrictions etc.