AWS-serverless-express never resolving with promises - node.js

can anyone shed light on my issue opened at the aws-serverless-express repo on github? https://github.com/awslabs/aws-serverless-express/issues/276
I am trying to run my previous express.js server using the aws-serverless-express package. When running without any special options I resolve, but the promises in the chain are never respected, meaning I dont execute all things in the event loop.
If I run the serverlessexpress with 'PROMISE' flag, I execute all my promises, but the program never resolves and times out after the maximum time set.
I even started a new project as per the example in that repo, same result.
My main executing file (index.js) when resolving but not respecting my promises
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app.js')
const server = awsServerlessExpress.createServer(app, null)
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context)
}
My main executing file (index.js) when NOT resolving, but respecting my promises
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app.js')
const server = awsServerlessExpress.createServer(app, null)
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context, 'PROMISE')
}
I also tried this:
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app.js')
const server = awsServerlessExpress.createServer(app, null)
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context, 'PROMISE').promise
}
My express server file (app.js)
const express = require('express')
const bodyParser = require('body-parser')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
const app = express()
const router = express.Router()
router.use(bodyParser.json())
router.use(bodyParser.urlencoded({ extended: true }))
router.use(awsServerlessExpressMiddleware.eventContext())
router.get('/', (req, res) => {
res.render('index', {
apiUrl: req.apiGateway ? `https://${req.apiGateway.event.headers.Host}/${req.apiGateway.event.requestContext.stage}` : 'http://localhost:3000'
})
})
router.get('/users', (req, res) => {
res.json(users)
})
const users = [{
id: 1,
name: 'Joe'
}, {
id: 2,
name: 'Jane'
}]
function myFunc () {
console.log('hey')
}
setTimeout(myFunc, 3000)
app.use('/', router)
module.exports = app

Make sure you are setting context.callbackWaitsForEmptyEventLoop to false
read more about it here https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html

The issue was regarding my testing tool:
https://www.npmjs.com/package/run-local-lambda
When I spoke with the AWS Support, they told me to use:
sam local invoke "Test" -e event.json
With a event.json file that includes the following:
{
"httpMethod": "OPTIONS",
"//body": "{\"name\": \"Sam\"}",
"path": "/api/auth",
"resource": "/{proxy+}",
"queryStringParameters": {},
"pathParameters": {
"proxy": "users"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch, br",
"Accept-Language": "en-US,en;q=0.8",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Content-Type": "application/json",
"Host": "xxxxxxxxxx.execute-api.us-east-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
"Via": "1.1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxx_xxxx==",
"X-Forwarded-For": "11.111.111.111, 11.111.111.111",
"X-Forwarded-Port": "111",
"X-Forwarded-Proto": "http",
"x-apigateway-event": "hej"
},
"requestContext": {
"accountId": "111111111111",
"resourceId": "xxxxxx",
"stage": "prod",
"requestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"identity": {
"cognitoIdentityPoolId": "",
"accountId": "",
"cognitoIdentityId": "",
"caller": "",
"apiKey": "",
"sourceIp": "11.111.111.111",
"cognitoAuthenticationType": "",
"cognitoAuthenticationProvider": "",
"userArn": "",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
"user": ""
},
"resourcePath": "/{proxy+}",
"httpMethod": "GET",
"apiId": "xxxxxxxxxx"
}
}
And a template file to use just for testing that looks like this:
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description:
A test project
Resources:
Test:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs10.x
Handler: index.handler
Timeout: 10
Environment:
Variables:
NODE_ENV: "test"
DB_NAME: "dbname"
DB_USER: "dbuser"
DB_PASSWORD: "secret"
DB_URL: "dburl"
The code I use looks like this in the handler:
'use strict'
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./main.js')
const server = awsServerlessExpress.createServer(app, null)
exports.handler = (event, context) => {
return awsServerlessExpress.proxy(server, event, context,'PROMISE').promise
}
Then my code executed wonderful with no timeouts ever.

Related

Nuxt3 and Firebase Cloud Function Hosting: How to access private key in .env file?

I have a Nuxt3 app that is using "server routes" to create backend APIs to use for the front-end.
I have the following server route:
server/api/imagekit/deleteFile.js:
import ImageKit from 'imagekit'
const imagekit = new ImageKit({
publicKey: useRuntimeConfig().public.imagekitPublicKey,
privateKey: useRuntimeConfig().imagekitPrivateKey,
urlEndpoint: useRuntimeConfig().public.imagekitBaseURL
})
export default defineEventHandler(async (event) => {
// Purge cache of file from Imagekit
// See detailed email from Rahul # imagekit dated aug 31, 2022
const body = await useBody(event)
const response = await imagekit.purgeCache(body.url)
return response
})
The above code works fine locally, but once I deploy to Firebase Hosting, I get the following server error when trying to access the API deleteFile:
description: ""
message: "Missing privateKey during ImageKit initialization"
statusCode: 500
statusMessage: "Internal Server Error"
url: "/api/imagekit/deleteFile"
In case it's relevant to this question, here is my code for Nuxt's nuxt.config.ts file where the runtimeConfig property is listed:
runtimeConfig: {
imagekitPrivateKey: '',
public: {
baseURL: process.env.NODE_ENV === 'production' ? 'https://example.com' : 'http://localhost:3000',
imagekitBaseURL: 'https://ik.imagekit.io/example/',
imagekitPublicKey: 'public_AdZM6u2+FvznG/LngYp7Ab3TJy4='
}
}
Also, my firebase.json uses 2 codebases for the functions: one for server and one for cloud functions:
{
"functions": [
{
"source": ".output/server",
"codebase": "nuxt"
},
{
"source": "functions",
"codebase": "functions"
}
],
"hosting": [
{
"site": "XXX",
"public": ".output/public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"cleanUrls": true,
"rewrites": [
{
"source": "**",
"function": "server"
}
]
}
]
}
I do have an .env file in project root that holds the imagekitPrivateKey value.
How would I provide this information to Firebase hosting deployment so ImageKit properly initializes with the private key?
You can read the variables from .env in nuxt.config.ts as shown below:
export default defineNuxtConfig({
runtimeConfig: {
// Uppercase preferred in .env file
imagekitPrivateKey: process.env.IMAGEKIT_PRIVATE_KEY,
},
});
Then you can access it in your API routes:
export default defineEventHandler((event) => {
const { imagekitPrivateKey } = useRuntimeConfig();
return { message: "success" };
});

Axios Unable to make request to IP address (Error: connect ECONNREFUSED)

I am trying to make a request to a 3rd party SOAP API, provided an IP address http://XXX.XXX.XXX.XXX:40871
I get the following error:
{
"success": false,
"error": "connect ECONNREFUSED XXX.XXX.XXX.XXX:40871",
"trace": "Error: connect ECONNREFUSED XXX.XXX.XXX.XXX:40871\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16)"
}
while testing locally I edited my hosts file and provided a host name to the IP
XXX.XXX.XXX.XXX my-host-name.com
then made a request to http://my-host-name.com:40871 and successfully got a response.
I can not do this in production since we are using heroku to deploy our app.
Our app is a middleware to make requests to the 3rd party API since they requires a static IP in its whitelist. we create a axios instance and recieve further options in the request body.
const agent = new HttpsProxyAgent(process.env.STATIC_URL!);
const baseOptions: AxiosRequestConfig = {
proxy: false,
httpsAgent: agent,
};
const instance = axios.create(baseOptions);
const response = await instance(req.body);
res.send({ success: true, response: response.data });
here is the request body:
{
"method": "POST",
"baseURL": "http://XXX.XXX.XXX.XXX:40871",
"url": "/IDORequestService/IDOWebService.asmx",
"data": {{xmlBody}},
"headers": {
"Content-type": "text/xml",
"SOAPAction": "http://frontstep.com/IDOWebService/CreateSessionToken",
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive"
}
}
Expected behavior: it should work with IP addresses
Environment:
Axios v0.27.2
https-proxy-agent: v5.0.1
Node.js v16.16.0
OS: Windows 11/Linux Mint 19.3
The problem was I did not have an httpAgent so it was not using the proxy for http requests, which is why the API server was probably refusing connection. Since it has the static IP in its whitelist and not our server IP.
const agent = new HttpsProxyAgent(process.env.STATIC_URL!);
const baseOptions: AxiosRequestConfig = {
proxy: false,
httpsAgent: agent,
httpAgent: agent,
};
const instance = axios.create(baseOptions);
const response = await instance(req.body);
res.send({ success: true, response: response.data });
adding the httpAgent fixed the issue.

AZ Custom Handler No connection could be made because the target machine actively refused it

Trying to add a Custom Handler to a simple AZ Function project. AZF works ok locally at VSC before adding. After adding, F5 starts ok like before:
[2021-12-30T19:33:14.402Z] Startup operation 'a9550626-ee3e-1234-b254-9facc08a3890' completed.
Functions:
select: [GET,POST] http://localhost:7071/api/select
then:
For detailed output, run func with --verbose flag.
[2021-12-30T19:33:16.324Z] Waiting for HttpWorker to be initialized.
Request to: http://127.0.0.1:49774/ failing with exception message: No
connection could be made because the target machine actively refused
it. (127.0.0.1:49774)
The port is random, next time can be 62974, then F5 stops itself.
Here is what's been added:
customHandler into root host.json.
a new folder middleware contains
app.js, myroute.js, host.json, mdw.js
root host.json:
{
...
"customHandler": {
"description": {
"defaultExecutablePath": "node",
"defaultWorkerPath": "middleware/app.js"
},
"enableForwardingHttpRequest": true
}
}
middleware/app.js:
const express = require('express')
const app = express()
const port = 3005;
app.listen(port, ()=>{console.log('================ My mdw is on 3005 ================');});
require("./myroute.js")(app);
middleware/myroute.js:
const express = require('express')
const mdw = require("./mdw");
module.exports = app =>
{
app.post("/api/testmdw", mdw.mytest);
};
middleware/mdw.js:
async function mytest(req, res, next)
{
const q = req;
return res.json({mdw: "ok"});
}
module.exports = { mytest }
middleware/host.json:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
I am following this Blog to use middleware on azure function via custom handler
Host.json
"customHandler": {
"description": {
"defaultExecutablePath": "node",
"defaultWorkerPath": "azexpresstest/app.js"
},
"enableForwardingHttpRequest": true,
},
"extensions": {"http": {"routePrefix": ""}}
The customHandler section points to a target as defined by the defaultExecutablePath. The execution target may either be a command, executable, or file where the web server is implemented.
"customHandler": {
"description": {
"defaultExecutablePath": "app/handler.exe",
"workingDirectory": "app"
}
…
Refer here for more information

Express or Axios Error: socket hang up code: ECONNRESET

This is the first time i post a question here, sorry if some data is missing.
I'm trying to do some web scraping to get some info of a table.
The page only responds with an index.php and when i use the search form, it makes a POST to index.php?go=le with some formData.
To avoid the CORS problem, im making the post with my own API running in localhost. I'm pointing my frontend to my API and i get the response from localhost.
No problem there.
My problem appears when i try to make a second request to my API. The first GET works fine but after that response it keeps failing.
When i restart the server, it works again but only one time.
Here is my API code. I use nodemon server.js to start my server.
server.js
const express = require("express");
const axios = require("axios");
const scrape = require("scrape-it");
const FormData = require("form-data")
const cors = require("cors")
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors())
const config = {
headers: {
'Content-type': 'multipart/form-data'
},
}
app.get("/get-projects", async (req,res) => {
const testJSON = await axios.post(baseURL +"/index.php?go=le",formData,config)
.then(res => {
console.log("Post successfull...");
return res
}
)
.catch(err => {
console.log("Server error");
return err
}
);
if(testJSON && testJSON.data){
res.send({status: 200, data: testJSON.data});
}else{
res.status(508).send({status: 508, msg: "Unhandled Server Error", failedResponse: testJSON || "empty"})
}
})
app.listen(PORT,()=>console.log(`App running in port: ${PORT}`))
And in my front-end i only have a button with an event that makes a get to my API (http://localhost:5000)
This is my fetch.js that is included by a script tag. Nothing fancy there.
fetch.js
const btn = document.getElementById("btn-fetch-proyects")
const axios = window.axios
const fetchProjects = async () => {
console.log("Fetching...")
axios.get("http://localhost:5000/get-projects")
.then(res=>
console.log("The server responded with the following data: ",res.data)
)
.catch(err => console.log("Failed with error: ",err)
)
return null
}
btn.addEventListener("click",fetchProjects);
In the console where im running the server, i get Server error with this err object:
{
"message": "socket hang up",
"name": "Error",
"stack": "Error: socket hang up\n at connResetException (internal/errors.js:607:14)\n at Socket.socketOnEnd (_http_client.js:493:23)\n at Socket.emit (events.js:327:22)\n at endReadableNT (internal/streams/readable.js:1327:12)\n at processTicksAndRejections (internal/process/task_queues.js:80:21)",
"config": {
"url": "http://186.153.176.242:8095/index.php?go=le",
"method": "post",
"data": {
"_overheadLength": 1216,
"_valueLength": 3,
"_valuesToMeasure": [],
"writable": false,
"readable": true,
"dataSize": 0,
"maxDataSize": 2097152,
"pauseStreams": true,
"_released": true,
"_streams": [],
"_currentStream": null,
"_insideLoop": false,
"_pendingNext": false,
"_boundary": "--------------------------935763531826714388665103",
"_events": {
"error": [
null,
null
]
},
"_eventsCount": 1
},
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "multipart/form-data",
"User-Agent": "axios/0.21.1"
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
},
"code": "ECONNRESET"
}
I hope someone has a clue about what's happening. I tried all day and i couldn't solve it.
I tried posting to other sites, and it works fine. I thing the problem is with the form POST.
Thanks for reading!!!
At a first glance I see an error in your front-end code. You are using async on the function but then you do not await but you use .then, try not mixing up styles, either you use async/await or .then .catch.
Check if that helps! :)
Obviously the socket is hanging!
Use node unirest and it closes the data stream.
var unirest = require('unirest');
var req = unirest('POST', 'localhost:3200/store/artifact/metamodel')
.attach('file', '/home/arsene/DB.ecore')
.field('description', 'We are trying to save the metamodel')
.field('project', '6256d72a81c4b80ccfc1768b')
.end(function (res) {
if (res.error) throw new Error(res.error);
console.log(res.raw_body);
});
Hope this helps!

Hapi.js Cannot read property 'statusCode' of null

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

Resources