fastify-request-context is not working for POST API calls - fastify

This is my fastify server written in typescript while using the fastify-request-context plugin I am able to access the context of fastify request when I am requesting the resource using the GET method,
But I am not able to access the context of fastify request if I am requesting the resource using POST method
import fastify from 'fastify';
const { fastifyRequestContextPlugin } = require('fastify-request-context');
const server = fastify({
logger: true
});
const preValidationFn = (req, reply, done): any => {
req.requestContext.set('hello', 'hello world');
console.log(req.requestContext.get('hello'));
done();
};
const handlerFn = (req, reply): any => {
console.log(req.requestContext.get('hello'));
reply.send('hello world');
};
server.route({
url: '/test',
method: ['GET', 'POST'],
preValidation: preValidationFn,
handler: handlerFn
});
server.register(fastifyRequestContextPlugin);
console.log('Starting fastify Typescript server ');
server.listen(8085, '0.0.0.0', (err, address): void => {
if (err) {
console.warn(err);
process.exit(1);
}
console.info(`Server listening at ${address}`);
});
process.on('uncaughtException', error => {
console.error(error);
});
process.on('unhandledRejection', error => {
console.error(error);
});```

Related

Simple Fetch API Post Request Fails No Matter What

I have been working on this issue for 2 days, looked at various pages and cannot find a single solution that would work.
Please only reply if you know how to write them with async await functions and please reply if you know the answer of fetch api. I am not looking for axios solutions for the time being.
I have a backend server which runs on port 8000 of localhost, frontend runs on port 3000. Front end is written in React, backend is written in Node/Express.
I am able to successfully make a GET request from backend server but the POST request fails for some reason with the error "VM942:1 POST http://localhost:8000/frontend-to-backend 500 (Internal Server Error)"
Backend server has this error: SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse ()
// React-To-Node-Connection
// React "App.js" file
// "package.json" file contains this
// "proxy": "http://localhost:8000"
useEffect(() => {
const getBackend = async () => {
const res = await fetch('backend-to-frontend');
const data = await res.json();
if (!res.ok) {
throw new Error(`Cannot get data from backend server. HTTP Status: ${res.status}`);
}
console.log(data.message);
// Prints "Hi from backend!"
}
getBackend();
const postBackend = async () => {
try {
const res = await fetch('http://localhost:8000/frontend-to-backend',
{
method: 'POST',
mode: 'no-cors',
body: JSON.stringify({ message: 'Hi from frontend!' }),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
);
if (res.ok) {
const data = await res.json();
console.log(data);
}
} catch (error) {
console.error(error);
}
}
postBackend();
}, []);
Now the backend code:
app.get('/backend-to-frontend', (req, res) => {
res.json({ message: 'Hi from backend!' });
});
app.post('/frontend-to-backend', (req, res) => {
try {
const reactMessage = JSON.parse(req.body.data);
console.log(`message: ${reactMessage}`);
} catch (err) {
console.error(err);
}
});
How to fix this? Please help!
Full backend server code can be found here:
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: true }));
app.get('/backend-to-frontend', (req, res) => {
res.json({ message: 'Hi from backend!' });
});
app.post('/frontend-to-backend', (req, res) => {
try {
const reactMessage = JSON.parse(req.body.data);
console.log(`message: ${reactMessage}`);
} catch (err) {
console.error(err);
}
});
const port = process.env.PORT || 8000;
app.listen(port, function () {
console.log(`Backend server started on port ${port}.`);
});
with no-cors, you can only use simple headers, so you cannot POST JSON (see: Supplying request options)
Try urlencoded:
const postBackend = async() => {
try {
const res = await fetch('http://localhost:8000/frontend-to-backend', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'message': 'Hi from frontend!'
})
});
if (res.ok) {
const data = await res.json();
console.log(data);
}
} catch (error) {
console.error(error);
}
}
postBackend();
and on the server, don't parse req.body, as it's already done by middleware:
app.post('/frontend-to-backend', (req, res) => {
console.log('req.body: ', req.body);
try {
const reactMessage = req.body.message;
req.body.data may be an object (check with debugger). If so, you might try to stringify before parsing :
JSON.parse(JSON.stringify(req.body.data))
I finally found the answer, here is my sample code. I did not change the React code that much so it is pretty much same, I removed the no cors section and added cors to Express JS code.
Here is my React code.
// React-To-Node-Connection
// "package.json" file has the following line
// "proxy": "http://localhost:8000"
// React Code
useEffect(() => {
const getBackend = async () => {
const res = await fetch('/backend-to-frontend');
const data = await res.json();
if (!res.ok) {
throw new Error(`Cannot get data from backend server. HTTP Status: ${res.status}`);
}
console.log(data.message);
}
getBackend();
const postBackend = async () => {
try {
await fetch('http://localhost:8000/frontend-to-backend',
{
method: 'POST',
body: JSON.stringify({ message: 'Hi from frontend!' }),
headers: {
'Content-Type': 'application/json'
}
}
);
} catch (err) {
console.error(err);
}
}
postBackend();
}, []);
And here is my Express JS code.
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: true }));
// Express JS Code
const cors = require('cors');
app.use(express.json());
app.use(cors());
app.get('/backend-to-frontend', (req, res) => {
res.json({ message: 'Hi from backend!' });
});
app.post('/frontend-to-backend', (req, res) => {
try {
console.log(req.body.message);
} catch (err) {
console.error(err);
}
});
const port = process.env.PORT || 8000;
app.listen(port, function () {
console.log(`Backend server started on port ${port}.`);
});
Thanks.

Not receiving the API call output from a fetch function in NodeJS

I want to retrieve data from a API call and send the results to the user in NodeJS. There are separate functions for the API call and for the route call. The data is print to the console within the API call function, but not been sent to the route call. Please be kind to help me up with this. The API needs Basic auth headers.
import express from 'express';
import bodyParser from'body-parser';
import fetch from 'node-fetch';
// This server is used to get the issues of the project and store them in an array
/* App config */
const app = express()
const port = process.env.PORT || 9000;
/* Middleware */
app.use(bodyParser.json())
var issues = []
var issueIds = []
async function getIssueIds()
{
try
{
const response = await fetch('API link',
{
method: 'GET',
headers:
{
'Authorization': `Basic ${Buffer.from(
'authorization data'
).toString('base64')}`,
'Accept': 'application/json',
}
})
.then(res => res.json())
.then(json => {
issues = json.issues;
for(let i = 0; i < issues.length; i++)
{
issueIds.push(issues[i].id)
}
return issueIds;
})
.then(function(data) {
return data;
})
.catch(err => console.error(err));
}
catch(err)
{
return err;
}
}
app.get('/api/issues', (req, res) =>
{
getIssueIds().then(issues => {
console.log("Issue array: "+ issues);
});
res.set('Access-Control-Allow-Origin', '*');
//If success, send the data
res.status(200).send(issueIds);
});
/* Listen */
app.listen(port, () => console.log(`Listening on port : ${port}`));

How can I make my Hapi route wait for data before returning a value?

I am using Hapi.js and have a route that I want to use to fetch data and then return a result.
I have tried to use async/await, but I must be doing something wrong because while the function I am calling eventually prints a result to the console, the route is returning without waiting for that function to return a value.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
const getWalletBalance = async () => {
web3.eth.getBalance(`${walletAddress}`, async function(err, result) {
if (err) {
console.log('There was an error: ' + err);
return ({ error: 'The wallet balance call failed.' });
} else {
ethBalance = await web3.utils.fromWei(result, "ether");
console.log("This should be first: The wallet balance via API call is " + ethBalance + " ETH.");
return ethBalance; // I expect the walletbalance route to wait for this to be returned
}
});
};
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
let result = null;
try {
result = await getWalletBalance();
console.log('This should be second, after the getWalletBalance function has printed to the console.'); // this prints first, so await isn't working as expected
return ({ ethBalance: result });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Any idea where I have gone wrong here? This is the first time I have used async/await.
ETA: My console looks like this:
[nodemon] starting `node index.js`
Server running on http://localhost:3000
This should be second, after the getWalletBalance function has printed to the console.
This should be first: The wallet balance via API call is 4061.894069996147660079 ETH.
And this is the JSON I get back when I use the wallet balance route:
{}
Based on the answer I was given, I was able to get the results I wanted with this:
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
async function getWalletBalance(){
let ethBalance = await web3.eth.getBalance(`${walletAddress}`);
if (ethBalance.err) {
console.log('error in the called function');
} else {
return ethBalance;
}
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
const ethBalanceInWei = web3.utils.fromWei(result, "ether");
return ({ balance: ethBalanceInWei });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Thank you for the help! That got me going in the right direction.
Basically your getWalletBalance function is using multiple concepts. callback style functions and inside that you are using await. I have restructured your code a little bit. Hopefully that should fix the issue which you are facing.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
function getWalletBalance() {
return Promise((resolve, reject) => {
web3.eth.getBalance(`${walletAddress}`, (err, result) => {
if (err) {
console.log('There was an error: ' + err);
reject({ error: 'The wallet balance call failed.' });
} else {
resolve(result);
}
});
});
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
ethBalance = await web3.utils.fromWei(result, "ether");
return ethBalance;
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();

Use fastify-redis from controllers in node.js

How to use fastify-redis plugin from other controllers or other.js while declaring the redis connection in server.js
server.js
const fastify = require('fastify')({ logger: false })
const routes = require('./routes')
fastify.register(require('fastify-redis'), { host: '127.0.0.1' })
routes.forEach((route, index) => {
fastify.route(route)
})
const start = async () => {
try {
await fastify.listen(3000)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
//const { redis } = fastify
//console.log(redis)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
Controller -> books.js
exports.getBooks = async (request, reply) => {
//console.log(redis)
let data = {
book: 'Book 1',
author: 'Author 1'
}
//return data
return redis.get('key1') // Not Defined
//return redis.get('key1')
}
So, simply how can I use the Redis instance in other files to set some values in Redis as a to implement caching database data.
If you write your handler with a simple function (aka no arrow function), the this object will be binded to the fastify server:
exports.getBooks = async function (request, reply) {
console.log(this)
let data = {
book: 'Book 1',
author: 'Author 1'
}
return this.redis.get('key1')
}
i would do something like this: basically passing app instance over to the exported function in route that are used to bootstrap routes.
in route
module.exports = fastify => [
{
url: '/hello',
method: 'GET',
handler: async (request, reply) => {
const { redis } = fastify
// ...
}
},
{
url: '/hello',
method: 'POST',
handler: async (request, reply) => {
const { redis } = fastify
// ...
}
}
]
and bootstraping routes at start
app.register(plugin1)
.register(plugin2)
.after(() => {
// bootstraping routes
// route definition should either be done as a plugin or within .after() callback
glob.sync(`${__dirname}/routes/*.js`, { cwd: __dirname })
.map(f => path.resolve(__dirname, f))
.forEach(file => {
const routeConfigs = require(file)(app)
for (let i = 0; i < routeConfigs.length; i += 1) {
const routeConfig = routeConfigs[i]
app.route(routeConfig)
}
})
})
Send instances of fastify to decorateRequest when calling .after () then you will be able to use redis when calling request in the route handler.

LdapJS exop Request timeout

I have an LdapJS server which implements standard operation and an extended operation to check health:
const server = ldap.createServer();
server.exop('healthcheck', (req, res, next) => {
res.end();
console.log('ended');
return next();
});
...
Then I wrote a simple client script to ping healthcheck service:
const { createClient } = require('ldapjs');
const client = createClient({
url: 'ldap://localhost:1389',
timeout: 2000,
connectTimeout: 2000
});
client.exop('healthcheck', (err, value, res) => {
if (err) {
console.log(`ERROR: ${err.message}`);
process.exit(1);
}
else {
console.log(`STATUS: ${res.status}`);
process.exit(0);
}
});
The problem is that the exop is correctly received by server (I can see the log inside its callback), but the client always logs: ERROR: request timeout (client interrupt).
Why the request is not correctly terminated?
EDIT
I wrote a mocha test for the exop and it works. Seems that the problem is related to the standalone call in healthcheck script.
describe('#healthcheck()', function () {
before(function () {
server = createServer();
server.listen(config.get('port'), config.get('host'), () => {});
});
after(function () {
server.close();
});
it('should return status 0', function (done) {
const { createClient } = require('ldapjs');
const client = createClient({
url: 'ldap://localhost:1389',
timeout: 2000,
connectTimeout: 2000
});
client.exop('healthcheck', (err, value, res) => {
should.not.exist(err);
res.status.should.be.equal(0);
client.destroy();
return done();
});
});
});

Resources