Hapi.js Cannot read property 'statusCode' of null - node.js

I'm creating a node.js api server using hapi.js and mongodb and I'm having some trouble to get it working on Amazon EC2.
Running it locally works, but if I run it on an EC2 instance I'm getting the error TypeError: Cannot read property 'statusCode' of null
The complete stacktrace is the following:
TypeError: Cannot read property 'statusCode' of null
at Request._finalize (/home/ec2-user/backend/node_modules/#hapi/hapi/lib/request.js:497:31)
at Request._reply (/home/ec2-user/backend/node_modules/#hapi/hapi/lib/request.js:434:18)
at Request._execute (/home/ec2-user/backend/node_modules/#hapi/hapi/lib/request.js:280:14)
at processTicksAndRejections (node:internal/process/task_queues:93:5)
The strange part is that GET requests are working while PUT, POST and DELETE are throwing the above error.
I've setup the server.js as follow:
...
const init = async () => {
const server = Hapi.server({
port: 3000,
});
//server.route(routes);
server.route([
{
method: "GET",
path: "/test",
handler: async (request, h) => {
return "workin GET";
},
},
{
method: "PUT",
path: "/test",
handler: async (request, h) => {
return "workin PUT";
},
},
{
method: "POST",
path: "/test",
handler: async (request, h) => {
return "workin POST";
},
},
{
method: "DELETE",
path: "/test",
handler: async (request, h) => {
return "workin DELETE";
},
},
]);
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Any solution?

I've found out that on the EC2 instance I had installed node version 15.5.0 which apparently is not compatible with the latest version of hapi.js (20.0.2).
To fix the issue just install node version 14.15.3.

This is fixed in #hapi/hapi v20.2.1: https://github.com/hapijs/hapi/issues/4319.

Just remove #hapi/hapi and re-install it

Related

How to move an #hapi plugin out to its own separate file

I am upgrading the Node version of my system and have got to the point of updating hapi to #hapi/hapi. I am using Node version 14.0.0 and #hapi/hapi version 20.2.2
I've written a simple server with just one route, which works perfectly:
'use strict';
const Hapi = require('#hapi/hapi');
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// await server.register(require('./plugins'))
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});
await server.start();
console.log(`'Test' server running on Node ${process.version} at:`, server.info.uri);
};
init();
However, if I uncomment the server.register call and comment out the server.route code I get an error. The file ./plugins/index.js looks like this:
'use strict';
const report = {
name: 'report',
version: '1.0.0',
register: async function (server, options) {
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});
}
}
The error I am getting from the #hapi server is this:
(node:4793) UnhandledPromiseRejectionWarning: Error: Invalid plugin options {
"plugin": {
"register" [1]: -- missing --
}
}
[1] "plugin.register" is required
It looks like a very simple issue, but I cannot find anything relatively modern on the Internet to help me out. Please can someone just point me in the right direction? - thank you.
EDITED: I have moved #hapi/hapi up to the latest version, 21.1.0, and Node up to 18.12.1 but still get the same message.

I can't create the pact-file, because the Query Path isn't correct

When I run the consumer part, Its get the following error:
1 failing
1) The Project API
"after each" hook for "returns the correct response":
Error: Pact verification failed - expected interactions did not match actual.
at new VerificationError (node_modules\#pact-foundation\pact\errors\verificationError.js:19:42)
at C:\Nexus\NovoTesteContrato\node_modules\#pact-foundation\pact\httpPact.js:102:23
at processTicksAndRejections (internal/process/task_queues.js:95:5)
And
Pact verification failed!
Actual interactions do not match expected interactions for mock MockService.
Missing requests:
GET /Project?Responsible=teste.k6%40k6.com&Name=Teste+para+a+pequisa
See C:/Nexus/NovoTesteContrato/logs/mockserver-integration.log for details.
My mock API and official API don't aceppt:
GET /Project?Responsible=teste.k6%40k6.com&Name=Teste+para+a+pequisa
My mock API and official API aceppt:
GET /Project?responsible=teste.k6%40k6.com&Name=Teste%20para%20a%20pequisa
How do I make PACT-JS send the request with "Test%20for%20a%20research" and not with "Test+for+the+research"?
My code is
import 'dotenv/config';
import { Matchers, Pact } from '#pact-foundation/pact';
import { eachLike, somethingLike, integer } from '#pact-foundation/pact/src/dsl/matchers';
import path from 'path';
import { GetProjectScenario1 } from '../../../api';
const mockProvider = new Pact({
consumer: 'TestProjectApi',
provider: 'Nexus',
log: path.resolve(process.cwd(), "__tests__/contract/logs", "pact.log"),
dir: path.resolve(process.cwd(), "__tests__/contract/pacts"),
logLevel: 'INFO',
pactfileWriteMode: 'overwrite',
spec: 2,
cors: true
});
let ProjectJsonSucessfull = ""
let responsibleSceario1 = "teste.k6#k6.com"
describe('Given - API TEST Nexus Project STAY ONLINE', () => {
beforeAll(async () => {
ProjectJsonSucessfull = require("./resources/tmp/Get_project_successfull_response.json")
})
describe('When I Search Project with sucessfull', () => {
beforeAll(async () =>
await mockProvider.setup().then(() => {
mockProvider.addInteraction({
uponReceiving: 'Only responsible',
withRequest: {
method: 'GET',
path: `/Project?Responsible=${responsibleSceario1}`
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json',
},
body: Matchers.like(ProjectJsonSucessfull).contents
}
});
})
);
afterEach(() => mockProvider.verify());
it('should return the expected data', async () => {
const response = await GetProjectScenario1(responsibleSceario1);
expect(response.headers['content-type']).toBe("application/json; charset=utf-8")
expect(response.status).toEqual(200);
expect(response.data).toEqual(ProjectJsonSucessfull);
});
})
afterAll(() => mockProvider.finalize())
})
Sed resquest with "/Project?responsible=teste.k6%40k6.com&Name=Teste%20para%20a%20pequisa"

Angular Universal SSR loading static assets in prerender stage

I'm looking for an approach to accessing assets in the /assets/ folder that is used to build the content in a component when prerendering an application. I'm using Angular 14 and the #nguniversal/express-engine package. I can't seem to get static assets to be read in the app when running npm run prerender.
I've seen the discussion at #858 however as the last comment points out this won't work when prerendering.
I have a minimal example of what I mean here:
https://stackblitz.com/edit/angular-ivy-dxb32y?file=src%2Fapp%2Fapp.service.ts
You see my service turns the path into an absolute URL:
public getContents(path: string): Observable<string> {
if (isPlatformServer(this.platformId) && path.includes('./')) {
path = `http://localhost:4200/${path.replace('./', '')}`
}
return this.http.get(path, {
observe: 'body',
responseType: 'text',
});
}
And the ssr:dev command serves this content correctly.
However, under prerender I get the following error:
⠸ Prerendering 1 route(s) to C:\Users\***\preloading\dist\preloading\browser...ERROR HttpErrorResponse {
headers: HttpHeaders {
normalizedNames: Map(0) {},
lazyUpdate: null,
headers: Map(0) {}
},
status: 0,
statusText: 'Unknown Error',
url: 'http://localhost:4200/assets/file.txt',
ok: false,
name: 'HttpErrorResponse',
message: 'Http failure response for http://localhost:4200/assets/file.txt: 0 Unknown Error',
I've tried a few things, such as:
Turning the relative URLs into absolute URLs (https://github.com/angular/universal/issues/858) however this doesn't work during prerender
Using fs to read the static assets however these node modules can't be found during the prerender stage:
if (isPlatformServer(this.platformId) && path.includes('./')) {
import("fs")
path = `http://localhost:4200/${path.replace('./', '')}`
}
Gives:
✔ Browser application bundle generation complete.
⠦ Generating server application bundles (phase: sealing)...
./src/app/app.service.ts:14:8-20 - Error: Module not found: Error: Can't resolve 'fs' in 'C:\Users\***\preloading\src\app'
Error: src/app/app.service.ts:12:14 - error TS2307: Cannot find module 'fs' or its corresponding type declarations.
12 import("fs")
Any other ideas at all about what I can do?
So I managed to crack this using the relatively hacky solution of running both ng serve and npm run prerender using a node script:
https://stackblitz.com/edit/angular-ivy-uy7wy9?file=prerender.js
var error = false;
function sleep(miliseconds) {
console.log(`Sleeping for ${miliseconds} ms`);
if (miliseconds == 0)
return Promise.resolve();
return new Promise(resolve => setTimeout(() => resolve(), miliseconds))
}
async function run() {
try {
console.log("Running Angular server");
var proc = require('child_process').spawn('ng', ['serve']);
await sleep(20000)
console.log("Running prerender");
var prerender = require('child_process').spawn('npm', ['run', 'prerender']);
var prerenderTimeoutSeconds = 120;
var timeoutObject;
var timeoutResolve;
var timeoutReject;
var timeout = new Promise((resolve, reject) => {
timeoutResolve = resolve;
timeoutReject = reject;
timeoutObject = setTimeout(() => {
console.log('Timed out, killing prerender');
try {
prerender.kill("SIGKILL")
reject(Error("Timed out running prerender"))
} catch (e) {
console.error(e)
reject(Error('Cannot kill prerender'));
}
}, prerenderTimeoutSeconds * 1000)
});
prerender.stdout.on('data', (data) => {
console.log(`prerender stdout: ${data}`);
});
prerender.stderr.on('data', (data) => {
console.error(`prerender stderr: ${data}`);
});
prerender.on('close', (code) => {
clearTimeout(timeoutObject);
console.log(`prerender exited with code ${code}`)
if (code === 0) {
timeoutResolve()
} else {
timeoutReject(Error(`prerender exited with code ${code}`));
}
});
await timeout
} catch (err) {
console.error(err);
console.error(err.stack);
error = true;
} finally {
if (proc) {
console.log("Killing Angular server");
var angularKilled = proc.kill("SIGKILL")
console.log(`kill -9 on Angular success [${angularKilled}]`)
}
}
}
(async () => await run())();
if (error) {
throw new Error("Exception during execution")
}

Parametized INSERT query with node-mssql

I want to parametrize an insert query with node.js for SQL Server. Unfortunately it will not work and I don't really know if it's a Node module issue or a syntax failure.
Code:
server.route({
method: 'POST',
path: '/',
handler: async (request, h) => {
try {
await pool.query("INSERT INTO sigfoxmessages(device,data,station,rssi,unix_timestamp) VALUES($1,$2,$3,$4,$5))"
[request.payload.device, request.payload.data, request.payload.station, request.payload.rssi, request.payload.time]);
return h.response('Callback received').code(200);
}
catch (err) {
console.log("SQL Err", err.stack);
return 'Error';
}
}
});
Error:
at exports.Manager.execute (C:\Users\A\sqltest\node_modules#hapi\hapi\lib\toolkit.js:60: 33)
at Object.internals.handler (C:\Users\A\sqltest\node_modules#hapi\hapi\lib\handler.js:46 :48)
at exports.execute (C:\Users\A\sqltest\node_modules#hapi\hapi\lib\handler.js:31:36)
at Request._lifecycle (C:\Users\A\sqltest\node_modules#hapi\hapi\lib\request.js:365:68)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at async Request._execute (C:\Users\A\sqltest\node_modules#hapi\hapi\lib\request.js:274: 9)
Used node modules:
hapi/hapi 19.0.5
mssql: 6.0.1
Does anyone have an idea or or a suggestion?
According to the documentation for mssql you can use es6 template literals in you INSERT statement.
pool.query`INSERT INTO sigfoxmessages (device,data,station,rssi,unix_timestamp) VALUES(${request.payload.device}, ${request.payload.data}, ${request.payload.station}, ${request.payload.rssi}, ${request.payload.time}))`
Docs:
https://www.npmjs.com/package/mssql

Hapijs getting started good good-console error reporter must specify events to filter on

I'm just starting to learn Hapijs
following getting started tutorial
with this example:
var Hapi = require('hapi');
var Good = require('good');
var server = new Hapi.Server();
server.connection({ port: 3000 });
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello, world!');
}
});
server.route({
method: 'GET',
path: '/{name}',
handler: function (request, reply) {
reply('Hello, ' + encodeURIComponent(request.params.name) + '!');
}
});
server.register({
register: Good,
options: {
reporters: [{
reporter: require('good-console'),
args:[{ log: '*', response: '*' }]
}]
}
}, function (err) {
if (err) {
throw err; // something bad happened loading the plugin
}
server.start(function () {
server.log('info', 'Server running at: ' + server.info.uri);
});
});
when I run
node server
I've got
/home/user/hapi/node_modules/good/node_modules/hoek/lib/index.js:683
throw new Error(msgs.join(' ') || 'Unknown error');
^
Error: reporter must specify events to filter on
Can you help me, please ?
The documentation is outdated. There were some breaking changes in good 6.0.0. The module good-console has a new version, however it is not published on npm yet. You can use the master branch though by specifying the GitHub repository in package.json:
"good-console": "hapijs/good-console"
You will also need to change the configuration to:
options: {
reporters: [{
reporter: require('good-console'),
events: {
response: '*',
log: '*'
}
}]
}
EDIT: Version 5.0.0 of good-console has been released. The documentation was also updated.

Resources