google cloud trace not working in express - google-app-engine

I'm trying to implement google trace in my MERN project, hosted in google cloud platform.
I've followed the guide and the examples offered by Google, but I can't make it work in any way.
import gCloudTrace from '#google-cloud/trace-agent';
// initialize gcloud trace
if (process.env.NODE_ENV === 'production') {
console.log(`Starting google cloud trace...`);
const tracer = gCloudTrace.start();
console.log(JSON.stringify(tracer,null,2));
};
import express from 'express';
import apiRoute from './api.js';
import indexRoute from './index/indexRoute.js';
try {
// setup
const host = process.env.HOST || '0.0.0.0';
const port = process.env.PORT || 5050;
const app = express();
// routes
app.use('/', indexRoute);
app.use('/api', checkGloblalSettings, apiRoute);
// create and run HTTP server
app.listen(port, host, () => {
console.log(`EXPRESS SERVER HTTP BACKEND UP AND RUNNING ON ${host} : ${port}`);
});
} catch(error) {
globalErrorHandler(error);
};
When I deploy (google app engine, flex env) everything works great, all route works but I can't find any trace in my project.
I've double checked gcloud settings: Trace API is enabled, no need to create custom credentials.
Any suggestion?

I finally solved the problem.
While the code in the question seems correct, the problem inherently lies in how NodeJS handles ES6 import() instead of require().
Throughout the Google Trace documentation you will find only examples with require().
Here an example:
if (process.env.NODE_ENV === 'production') {
require('#google-cloud/trace-agent').start();
}
const express = require('express');
const got = require('got');
const app = express();
const DISCOVERY_URL = 'https://www.googleapis.com/discovery/v1/apis';
// This incoming HTTP request should be captured by Trace
app.get('/', async (req, res) => {
// This outgoing HTTP request should be captured by Trace
try {
const {body} = await got(DISCOVERY_URL, {responseType: 'json'});
const names = body.items.map(item => item.name);
res.status(200).send(names.join('\n')).end();
} catch (err) {
console.error(err);
res.status(500).end();
}
});
// Start the server
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
The fact is that while using require the import order is respected (so if you place require('# google-cloud/trace-agent').start(); in the first place everything works), using import() the code runs AFTER importing the express module as well, so nothing is tracked.
To solve, I recreated the require syntax (here the documentation), then using require() everything worked perfectly:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
if (process.env.NODE_ENV === 'production') {
// [START trace_setup_nodejs_implicit]
require('#google-cloud/trace-agent').start({
logLevel: 2,
flushDelaySeconds: 30,
ignoreUrls: ['/_ah/health', '/api/status'],
ignoreMethods: ['OPTIONS'],
samplingRate: 10,
bufferSize: 1000,
onUncaughtException: 'ignore',
});
// [END trace_setup_nodejs_implicit]
};
const express = await require('express');
I hope this can serve others and avoid the headaches it caused me :)

Related

React app deployed on Heroku gives 404. Using json-server and express

I am using json-server alongside express to create a mock database. The app works correctly when the base url and the api endpoints are hit but when any other url is hit Heroku returns 404. So for example if https://invoice-app-88.herokuapp.com/something is hit then Heroku returns 404 and app.get('*') will not put me back to the static index.html.
The base url is https://invoice-app-88.herokuapp.com/
The api endpoints are
https://invoice-app-88.herokuapp.com/invoices
and
https://invoice-app-88.herokuapp.com/invoices:id
This is my server.js file containing my express and json-server code
const jsonServer = require("json-server");
const path = require("path");
const express = require("express");
const app = express();
const router = jsonServer.router("db.json");
const PORT = process.env.PORT || 3000;
app.use("/", express.static("build"), router);
if (process.env.NODE_ENV === "production") {
app.get("*", function (req, res) {
res.sendFile(path.resolve(__dirname + "./build/index.html"));
});
}
app.listen(PORT, () => console.log(`Server started on PORT ${PORT}`));
EDIT: Made edit for clarification of issue.

Third-party API won't work after deployment

I have built and deployed a basic portfolio website to Heroku. In this app, I use a third-party API for sending emails (sendgrid) in the contact form. When I was running my app locally on localhost:3000, the API was working as anticiapted and would send me emails. However, once I deployed my app to Heroku, I recieve this error. When I run heroku logs --tail in my terminal, I get this.
Inexcplicably, the API has now stopped working when I run it locally as well. I see error 503 means network error so I am somehow not making a request to the server? The only thing I have changed that I could think would break it is:
I removed the react router to change pages and instead used react-scroll. Maybe this messed with the routes I use in my contact.js file to send the object to sendgird?
I moved my API key to .env rather than storing it in my global environmental variables. I've used dotenv to transpile this but maybe i went wrong somewhere? When I run console.log(process.env.REACT_APP_SENDGRID_API_KEY), it logs the correct key.
This is where I call the API from the contact.js form:
emailApi = () => {
let sendgridObj = {
to: 'caseyclinga#gmail.com',
from: this.state.from,
subject: this.state.subject,
text: this.state.text
}
this.resetState();
axios.post('/', sendgridObj)
.then(res => console.log(`CONTACT.JS RESPONSE: ${res}`))
.catch(err => console.log(`CONTACT.JS ERROR: ${err}`));
}
This is the server.js file:
const express = require('express');
var app = express();
const SgMail = require('#sendgrid/mail');
const path = require('path');
require('dotenv').config();
SgMail.setApiKey(process.env.REACT_APP_SENDGRID_API_KEY);
//Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
const PORT = process.env.PORT || 5000;
//Serve static assets if in production
if(process.env.NODE_ENV === 'production') {
//Set static folder
app.use(express.static('client/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
app.post('/', (req, res) => {
console.log(req.body)
SgMail.send(req.body)
.then(res => console.log(`SERVER.JS RESPONSE: ${res}`))
.catch(err => console.log(`SERVER.JS ERROR: ${err}`));
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
Here is my deployed site: https://still-sands-49669.herokuapp.com/
Here is my code: https://github.com/caseycling/portfolio

Deploying react/apollo-server app onto Heroku

What I'm trying to do is deploy react / apollo-server full stack app to heroku. Thus I' trying to serve static client files from express/apollo-server backend, like this:
const path = require('path');
const express = require('express');
const app = express();
const cors = require('cors');
const { ApolloServer } = require('apollo-server');
const { schema } = require('./schema');
const { resolvers } = require('./resolvers');
app.use(cors());
app.use(express.static('public'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'index.html'));
});
const server = new ApolloServer({
typeDefs: schema,
resolvers,
});
server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
For some reason I don't understand client is not served while deployed to heroku. On heroku URL I'm getting: GET query missing. If I set graphql to enabled in production I can see it, I can play with resolving data. But client is not rendered. I'm guseing app.get with * is not working and then index.html is not catched.
How can I fix that?
Thanks!
The error you are getting is because you are only exposing the server from ApolloServer to the port 4000 and not exposing the app with the frontend client application.
In order to have the fullstack app deployed, you also have to expose the app, in order to do that you can use applyMiddleware from ApolloServer and bind both apollo server and the frontend client, something like:
.....
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'index.html'));
});
const server = new ApolloServer({
typeDefs: schema,
resolvers,
});
server.applyMiddleware({
path: '/my-frontend', // you should change this to whatever you want
app,
});
app.listen({ port: process.env.PORT || 4000 }, () => {
console.log(`🚀 Server ready at http://localhost:4000`);
});
Now you should be able to navigate to http://localhost:4000/my-frontend and see your client application.

Localhost not connecting to mongodb after deploying to Heroku

I was running off localhost fine with mongodb and then switched to mLab and that was fine too, but now trying to run the database off localhost again I get a 422 error when it tries to connect.
GET http://localhost:3000/api/wines 422 (Unprocessable Entity)
I deployed this site once before and was able to switch back and forth so not sure what is wrong.
This is my server.js code:
const express = require("express");
const path = require("path");
const PORT = process.env.PORT || 3001;
const app = express();
const mongoose = require("mongoose");
const routes = require("./routes");
// Connect to the Mongo DB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/wineDB', { useNewUrlParser: true });
mongoose.connection.on("open", function (ref) {
console.log("Connected to mongo server.");
});
mongoose.connection.on('error', function (err) { console.log(err) });
// require("./models/wine");
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
// Add routes, both API and view
app.use(routes);
// Define API routes here
// Send every other request to the React app
// Define any API routes before this runs
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
app.listen(PORT, () => {
console.log(`🌎 ==> API server now on port ${PORT}!`);
});
I'm wondering if somehow my code is trying to do production mode that is needed to connect to mlab and no longer allowing me to connect to DB through localhost?
Actually, I was wrong about the source. It was because I had port 3001 running somewhere in the background so when I killed it that fixed the problem!

Getting 404 error when fetching service-worker on next.js + firebase hosting pwa app

Getting 404 error when fetching service-worker on next.js + firebase hosting pwa app
I am trying to make PWA application by adding service-worker to this next.js + firebase hosting application introduced in this repository.
https://github.com/zeit/next.js/tree/canary/examples/with-firebase-hosting
However, although it runs up to hosting, I am having trouble running service-worker.js.
I think that the cause is path to service-worker.js file, which is in dist/functions/next folder. So I tried change pass, but I cannot solve it.
Service worker registration failed, error1: TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.
Is there any idea to solve this probrem?
below is the functions/index.js file.
const functions = require('firebase-functions');
const next = require('next');
import * as admin from 'firebase-admin';
const { join } = require('path');
const { parse } = require('url');
const express = require('express');
const routes = require('./routes');
// Region hosting next.js to cloud function
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev, conf: { distDir: 'next' } });
const handle = routes.getRequestHandler(app);
const server = express();
server.get('*', (req, res) => handle(req, res));
server.get('/service-worker', (req, res) => {
// try change pass but not worked
// app.serveStatic(req, res, '_next/service-worker.js'),
// app.serveStatic(req, res, 'next/service-worker.js');
// app.serveStatic(req, res, '/service-worker.js');
// app.serveStatic(req, res, '/next/service-worker.js');
const filePath = join(__dirname , 'service-worker.js')
app.serveStatic(req, res, filePath);
});
exports.next = functions.https.onRequest((req, res) => {
console.log('File: ' + req.originalUrl); // log the page.js file that is being requested
return app.prepare().then(() => {
server(req, res);
});
});
https://github.com/hanford/next-offline seems to be the recommended approach to PWAs with next.js. You need to instruct workbox to put the generated service-worker.js in the public directory when your app is built.
Here's an example next.config.js:
const withOffline = require('next-offline')
const config = {
workboxOpts: {
swDest: '../public/service-worker.js',
},
// rest of your config
}
module.exports = withOffline(config)
I solved this problem.
The reason why I fail is the order of server.get method, which means that firstly I should write server.get('/service-worker') then servet.get(*).
And, change file path from join(__dirname , 'service-worker.js') to 'next/service-worker.js')

Resources