Nuxt.js on Google App Engine Standard Environment - 500 errors - express

I am serving Nuxt.js SSR application from Google App Engine, from F2 instance. All is deployed ok and works well, but sometimes I hit a weird 500 server error. I think the problem is because GAE shuts down my instance because it is rarely used. When the instance is created again, my website fails to load (timeouts), resulting in 500 error. I am using serverMiddleware with Express.
/server/middleware.js
const express = require('express')
const app = express()
// this fetches a CMS token and creates a CMS object used in the request below
const CMS = require('../cms')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// get Page from the CMS
app.post('/page', async (req, res, next) => {
try {
let result = await CMS.getPage(req.body.route)
res.status(200).json(result)
} catch (error) {
console.log('Error', error)
}
})
app.yaml
runtime: nodejs12
instance_class: F2
handlers:
- url: /_nuxt
static_dir: .nuxt/dist/client
secure: always
- url: /(.*\.(gif|png|jpg|ico|txt))$
static_files: static/\1
upload: static/.*\.(gif|png|jpg|ico|txt)$
secure: always
- url: /.*
secure: always
redirect_http_response_code: 301
script: auto
env_variables:
HOST: "0.0.0.0"
NODE_ENV: "production"
automatic_scaling:
min_idle_instances: 1
max_idle_instances: 1
min_instances: 1
max_instances: 1
target_cpu_utilization: 0.9
target_throughput_utilization: 0.9
max_concurrent_requests: 50
And here is the relevant log screenshot:
0: {
logMessage: "Request was aborted after waiting too long to attempt to service your request."
severity: "ERROR"
time: "2022-01-14T08:55:22.057499Z"
}
Am I doing something wrong here? I would not mind keeping the costs down and scaling back to 0 instances when they are not needed, but the website would still need to be served as fast as possible.

Related

NUXT Redirect issue when making a POST request to external API

I have a form that I built in Nuxt. I'm trying to submit it to an external API. The expected response is a JWT token.
async login() {
const res = await this.$axios.$post(`/api/token`, {
username: this.username,
password: this.password
}, this.headers )
console.log(res)
}
Trying to call the API directly gets me a CORS error, so I use proxy settings in my nuxt.confix.js.
...
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
axios: {
baseURL: '/',
proxy: true
},
proxy: {
'/api/': { target: 'https://<apiurl>.com/', changeOrigin: true }
},
...
Now when I check the network tab, it shows a 301 redirect, but the data that was sent in the post request gets thrown away, and it makes a get request to the API which returns a 405 error (because it's expecting a POST request with a data and not an empty GET request).
How can I make a POST request to an external API using NUXT? Is this an option at all?
I tried changing changeOrigin: false, and that seems to get rid of the issue, but it throws a 500 server error instead and an npm error that says
ERROR [HPM] Error occurred while proxying request localhost:3000/api/token to https://<apiurl>.com/ [ERR_TLS_CERT_ALTNAME_INVALID] (https://nodejs.org/api/errors.html#errors_common_system_errors)
Thank you
The problem is not how to send api to an external API ... the problem is in the external API it self ... make sure the external API has no credentials required to make any action ...
if you can make a request to https://jsonplaceholder.typicode.com/posts and get results that means there is no problems in your code ... cors erros in most cases are backend issue ... which means .. the backend developer who worked on it should fix it

Frequent timeout with app using Serverless Framework (AWS Lambda/Gateway), Express, Mongoose/MongoDB Atlas

Trigger warning : Beginner question.
I built an api using Express and Mongoose with a MongoDB Atlas DB.
Most of the time, it works normally, but often I get timeout errors. This seems to happen very randomly and concerns all routes, etc... Precisely, I get :
`502 Internal server error via POSTMAN`
and in the Serverless Dashboard, I get :
invocation
time invoked 1 day ago, mar 08 at 1:38pm
fatal error Function execution duration going to exceeded configured timeout limit.
cold start
duration 48.9 s
memory used na
request
endpoint /{proxy+}
method POST
status 502
message Internal server error
latency 27 ms
and span & log :
I used this tutorial to wrap my express app to deploy it with serverless framework : https://dev.to/adnanrahic/a-crash-course-on-serverless-apis-with-express-and-mongodb-193k
Serverless.yml file :
service: serviceName
app: appName
org: orgName
provider:
name: aws
runtime: nodejs12.x
stage: ${env:NODE_ENV}
region: eu-central-1
environment:
NODE_ENV: ${env:NODE_ENV}
DB: ${env:DB}
functions:
app:
handler: server.run
events:
- http:
path: /
method: ANY
cors: true
- http:
path: /{proxy+}
method: ANY
cors: true
plugins:
- serverless-offline # Utiliser pour tester localement
- serverless-dotenv-plugin
server.js file :
const sls = require('serverless-http')
const app = require('./app')
module.exports.run = sls(app)
app.js file :
const express = require('express')
const cors = require('cors')
const bodyParser = require('body-parser')
const newRoutes = require('./routes/file')
const app = express()
app.use(bodyParser.json())
const helmet = require('helmet')
app.use(helmet())
app.options('*', cors())
app.use(cors({ allowedHeaders: 'Content-Type, Authorization' }))
app.use('/new-route', newRoutes)
app.use((error, req, res, next) => {
console.log(error)
const status = error.status || 500
const message = error.message
res.status(status).json({
status: status,
message: message
})
})
// Gère la connexion à la base de donnée :
require('./db')
module.exports = app
and finally db.js file :
const mongoose = require('mongoose')
mongoose
.connect(
process.env.DB, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log('connected')
})
.catch(err => console.log(err))
From what I have read, it is related to cold start in Lambda and the way API Gateway handles timeouts (!?). I have read this on mongoose documentation (https://mongoosejs.com/docs/lambda.html), and read also other tutorials, but I don't how exaclty I should adapt it to my situation.
Thank you for your help
Under your provider add timeout, maximum value of timeout in lambda is
900 seconds, place it according to your execution time like 30 seconds
and see what happens
provider:
timeout: 30
The error is clearly saying that it's execution exceeded timeout, since you have not configured timeout so it was using default timeout of 3 seconds, hopefully it will solve the issue
The issue is likely due to your open database connection. While this connection is established any calls to callback won't be returned to the client and your function will timeout.
You need to set context.callbackWaitsForEmptyEventLoop to false.
Here is the explanation from the docs:
callbackWaitsForEmptyEventLoop – Set to false to send the response right away when the callback executes, instead of waiting for the Node.js event loop to be empty. If this is false, any outstanding events continue to run during the next invocation.
With serverless-http you can set this option quite easily within your server.js file:
const sls = require('serverless-http')
const app = require('./app')
module.exports.run = sls(app, { callbackWaitsForEmptyEventLoop: false })

Axios get request working locally but timing out on serverless

I'm trying to scrape some websites, but for some reason it works locally (localhost) with express, but not when I've deployed it to lambda. Tried w/ the ff serverless-http and aws-serverless-express and serverless-express plugin. Also tried switching between axios and superagent.
Routes work fine, and after hrs of investigating, I've narrowed the problem down to the fetch/axios bit. When i don't add a timeout to axios/superagent/etc, the app just keeps running and timing out at 15/30 sec, whichever is set and get an error 50*.
service: scrape
provider:
name: aws
runtime: nodejs10.x
stage: dev
region: us-east-2
memorySize: 128
timeout: 15
plugins:
- serverless-plugin-typescript
- serverless-express
functions:
app:
handler: src/server.handler
events:
- http:
path: /
method: ANY
cors: true
- http:
path: /{proxy+}
method: ANY
cors: true
protected async fetchHtml(uri: string): Promise<CheerioStatic | null> {
const htmlElement = await Axios.get(uri, { timeout: 5000 });
if(htmlElement.status === 200) {
const $ = Cheerio.load(htmlElement && htmlElement.data || '');
$('script').remove();
return $;
}
return null;
}
As far as i know, the default timeout of axios is indefinite. Remember, API gateway has hard limit of 29 sec timeout.
I had the same issue recently, sometimes the timeouts are due to cold starts. So I basically had to add a retry logic for the api call in my frontend react application.

express cookie not set in response, not shown in next request

There are several moving parts, so it's difficult to know what to debug here.
I have a web application on one localhost port, and a simple helper on another localhost running an express NodeJS application with a couple of endpoints.
The basic issue I'm seeing is that my cookie session on the express application is empty for subsequent calls, and I don't even see it being sent back in the first response.
The setup
The client makes basic GET ajax calls (jQuery at the moment) to the express application.
I have set http allowance for session cookies:
app.use(cookieSession({
name: 'session',
keys: ['abcdefg'],
maxAge: 24 * 60 * 60 * 1000, // 24 hours,
secure: false
}))
I have set cross-origin requests on the express application:
app.use((req, res, next) => {
const corsWhitelist = [
'http://localhost:8000',
'http://localhost:8777'
];
if (corsWhitelist.indexOf(req.headers.origin) !== -1) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
}
next();
});
And the requests are completed seemingly without issue, as the response bodies are what I expect.
The meat of the request handler is:
app.get('/initialize', (req, res) => {
console.log(req.session);
//if we have a session with verified status
if (req.session.hasOwnProperty("userId") && req.session.userId){
res.send({result: 'OK', message: 'good - 1'});
return;
}
const id = uuid.v4();
req.session.userId = id;
res.send({result: 'OK', message: 'good - 2'});
return;
});
I always always get the second response 'good - 2' from the ajax call. The log always shows the session as {}
It's probably worth noting that Chrome devtools shows "Provisional headers are shown" for the request headers, and set-cookie is not shown in the response headers. The AJAX is a simple GET to an endpoint with one parameter passed in.
Update
Just now occurred to me to try without using the AJAX call. Hitting the URL directly gets the cookie and keeps the session as expected. That will probably change the dynamic of the issue.
The fix was to use jsonp request/response to get cookies to pass around the ajax call. Nothing to do with express really.
https://samueleresca.net/2015/07/json-and-jsonp-requests-using-expressjs/

How to serve data for AJAX calls in a Vue.js-CLI project?

I have a Vue.js CLI project working.
It accesses data via AJAX from localhost port 8080 served by Apache.
After I build the project and copy it to a folder served by Apache, it works fine and can access data via AJAX on that server.
However, during development, since the Vue.js CLI website is being served by Node.js which is serving on a different port (8081), I get a cross-site scripting error) and want to avoid cross-site scripting in general.
What is a way that I could emulate the data being provided, e.g. some kind of server script within the Vue.js-CLI project that would serve mock data on port 8081 for the AJAX calls during the development process, and thus avoid all cross-site scripting issues?
Addendum
In my config/index.js file, I added a proxyTable:
dev: {
env: require("./dev.env"),
port: 8081,
autoOpenBrowser: true,
assetsSubDirectory: "static",
assetsPublicPath: "/",
proxyTable: {
"/api": {
target: "http://localhost/data.php",
changeOrigin: true
}
},
And now I make my AJAX call like this:
axios({
method: 'post',
url: '/api',
data: {
smartTaskIdCode: 'activityReport',
yearMonth: '2017-09',
pathRewrite: {
"^/api": ""
}
}
But now I see in my JavaScript console:
Error: Request failed with status code 404
Addendum 2
Apparent axios has a problem with rerouting, so I tried it with vue-resource but this code is showing an error:
var data = {
smartTaskIdCode: 'pageActivityByMonth',
yearMonth: '2017-09'
}
this.$http.post('/api', data).then(response => {
this.pageStatus = 'displaying';
this.activity = response.data['activity'];
console.log(this.activity);
}, response => {
this.pageStatus = 'displaying';
console.log('there was an error');
});
The webpack template has its own documentation, and it has a chapter about API proxying during development:
http://vuejs-templates.github.io/webpack/proxy.html
If you use that, it means that you will request your data from the node server during development (and the node server will proxy< the request to your real backend), and the real backend directly in production, so you will have to use different hostnames in each environment.
For that, you can define an env variable in /config/dev.env.js & /config.prod.env.js