I have developed a react application using next.js.
Now I want to deploy it to Google Cloud App Engine.
The application does include the react frontend and a mock-API (including mock.db) in order to store data temporararily while in development.
The issue is the following:
The first instance I opened did work correctly. As soon as I opened it in another browser only the react app was served but the API was not there (resulting in the React app only showing frames of controls and no data). The API server is accessible through localhost:3033
The same persits for my colleage who tried to open it, only seeing whiteness.
I have not configured anything extra on Google Cloud App Engine, just vanilla basically.
Does this have something todo with App Engine spinning up extra instances ? I cant figure out what could cause this issue.
package.json
"prestart:api": "node createMockDb.js",
"start:api": "node apiServer.js",
"dev": "node server.js",
"build": "next build",
"start": "cross-env NODE_ENV=production node server.js & node createMockDB.js & node apiServer.js"
server.js
const express = require("express");
const next = require("next");
const port = parseInt(process.env.PORT, 10) || 8080;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.get("/products/overview", (req, res) => {
return app.render(req, res, "/products/overview", req.query);
});
server.get("/products/roadmap", (req, res) => {
return app.render(req, res, "/products/roadmap", req.query);
});
server.get("/strategy/goals", (req, res) => {
return app.render(req, res, "/strategy/goals", req.query);
});
server.get("/strategy/metrics", (req, res) => {
return app.render(req, res, "/strategy/metrics", req.query);
});
/* server.get("/posts/:id", (req, res) => {
return app.render(req, res, "/posts", { id: req.params.id });
}); */
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
apiServer.js
/* eslint-disable func-names */
/* eslint-disable no-console */
const jsonServer = require("json-server");
const server = jsonServer.create();
const path = require("path");
const router = jsonServer.router(path.join(__dirname, "server/db.json"));
const middlewares = jsonServer.defaults({
static: "node_modules/json-server/dist"
});
server.use(middlewares);
server.use(jsonServer.bodyParser);
server.use(function(req, res, next) {
setTimeout(next, 0);
});
function createSlug(value) {
return value
.replace(/[^a-z0-9_]+/gi, "-")
.replace(/^-|-$/g, "")
.toLowerCase();
}
function validateProduct(product) {
if (!product.title) return "Title is required.";
if (!product.tagline) return "Tagline is required.";
if (!product.description) return "Description is required.";
return "";
}
server.use((req, res, next) => {
if (req.method === "POST") {
req.body.createdAt = Date.now();
}
next();
});
server.post("/products/", function(req, res, next) {
const error = validateProduct(req.body);
if (error) {
res.status(400).send(error);
} else {
req.body.slug = createSlug(req.body.title);
next();
}
});
server.use(router);
const port = 3033;
server.listen(port, () => {
console.log(`JSON Server is running on port ${port}`);
});
You appear to be attempting to start multiple web servers from your npm start command in a single app engine instance:
"start": "cross-env NODE_ENV=production node server.js & node createMockDB.js & node apiServer.js"
I've never seen this before and I doubt very much GAE can deal with it. GAE expects a single node application serving requests on port process.env.PORT.
If you want multiple different servers running within the same GAE project, you should probably deploy them as independently as different services. Each of them should have an app.yaml that specifies a unique service name, and you'll deploy them each independently. The documentation here is fairly comprehensive.
Related
I've created a plugin in shopify using node.js & vite.js.
shopify app create node
After running using npm run dev, it generates a url like this: https://b136-0000-7400-56-bc78-5000-178b-d6f3-6000.ngrok.io/login?shop=shopname.myshopify.com
When I open this link, it start reloading infinite with error
This is my index.js:
import { resolve } from "path";
import express from "express";
import cookieParser from "cookie-parser";
import { Shopify, LATEST_API_VERSION } from "#shopify/shopify-api";
import "dotenv/config";
import applyAuthMiddleware from "./middleware/auth.js";
import verifyRequest from "./middleware/verify-request.js";
const USE_ONLINE_TOKENS = true;
const TOP_LEVEL_OAUTH_COOKIE = "shopify_top_level_oauth";
const PORT = parseInt(process.env.PORT || "8081", 10);
const isTest = process.env.NODE_ENV === "test" || !!process.env.VITE_TEST_BUILD;
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: LATEST_API_VERSION,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
Shopify.Webhooks.Registry.addHandler("APP_UNINSTALLED", {
path: "/webhooks",
webhookHandler: async (topic, shop, body) => {
delete ACTIVE_SHOPIFY_SHOPS[shop];
},
});
// export for test use only
export async function createServer(
root = process.cwd(),
isProd = process.env.NODE_ENV === "production"
) {
const app = express();
app.set("top-level-oauth-cookie", TOP_LEVEL_OAUTH_COOKIE);
app.set("active-shopify-shops", ACTIVE_SHOPIFY_SHOPS);
app.set("use-online-tokens", USE_ONLINE_TOKENS);
app.use(cookieParser(Shopify.Context.API_SECRET_KEY));
applyAuthMiddleware(app);
app.post("/webhooks", async (req, res) => {
try {
await Shopify.Webhooks.Registry.process(req, res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
if (!res.headersSent) {
res.status(500).send(error.message);
}
}
});
app.get("/products-count", verifyRequest(app), async (req, res) => {
const session = await Shopify.Utils.loadCurrentSession(
req,
res,
app.get("use-online-tokens")
);
const { Product } = await import(
`#shopify/shopify-api/dist/rest-resources/${Shopify.Context.API_VERSION}/index.js`
);
const countData = await Product.count({ session });
res.status(200).send(countData);
});
app.post("/graphql", verifyRequest(app), async (req, res) => {
try {
const response = await Shopify.Utils.graphqlProxy(req, res);
res.status(200).send(response.body);
} catch (error) {
res.status(500).send(error.message);
}
});
app.use(express.json());
app.use((req, res, next) => {
const shop = req.query.shop;
if (Shopify.Context.IS_EMBEDDED_APP && shop) {
res.setHeader(
"Content-Security-Policy",
`frame-ancestors https://${shop} https://admin.shopify.com;`
);
} else {
res.setHeader("Content-Security-Policy", `frame-ancestors 'none';`);
}
next();
});
app.use("/*", (req, res, next) => {
const { shop } = req.query;
// Detect whether we need to reinstall the app, any request from Shopify will
// include a shop in the query parameters.
if (app.get("active-shopify-shops")[shop] === undefined && shop) {
res.redirect(`/auth?${new URLSearchParams(req.query).toString()}`);
} else {
next();
}
});
/**
* #type {import('vite').ViteDevServer}
*/
let vite;
if (!isProd) {
vite = await import("vite").then(({ createServer }) =>
createServer({
root,
logLevel: isTest ? "error" : "info",
server: {
port: PORT,
hmr: {
protocol: "ws",
host: "localhost",
port: 64999,
clientPort: 64999,
},
middlewareMode: "html",
},
})
);
app.use(vite.middlewares);
} else {
const compression = await import("compression").then(
({ default: fn }) => fn
);
const serveStatic = await import("serve-static").then(
({ default: fn }) => fn
);
const fs = await import("fs");
app.use(compression());
app.use(serveStatic(resolve("dist/client")));
app.use("/*", (req, res, next) => {
// Client-side routing will pick up on the correct route to render, so we always render the index here
res
.status(200)
.set("Content-Type", "text/html")
.send(fs.readFileSync(`${process.cwd()}/dist/client/index.html`));
});
}
return { app, vite };
}
if (!isTest) {
createServer().then(({ app }) => app.listen(PORT));
}
The app is installing fine but it's getting refreshed again and again due to failed connection to ws (as mentioned in the screenshot). I tried a few things around changing the settings of the HMR but doesn't seem to be connecting.
I'm quite new to this whole backend business and I'm wondering if anyone can see where I'm going wrong. I've got an express server in my Nuxt app which is serving out an API. When I run the localhost:3000/api/salesforce/:id route - my vscode terminal generates a response - but it doesn't show up on the browser. Which in turn makes it inaccessible to Nuxt.
In my nuxt.config.js:
serverMiddleware: {
'/api': '~/api'
},
/api/index.js:
const express = require('express')
// Create express instance
const app = express()
// Require API routes
const users = require('./routes/users')
const test = require('./routes/test')
const salesforce = require('./routes/salesforce')
// Import API Routes
app.use(users)
app.use(test)
app.use(salesforce)
// Export express app
module.exports = app
// Start standalone server if directly running
if (require.main === module) {
const port = process.env.PORT || 3001
app.listen(port, () => {
// eslint-disable-next-line no-console
console.log(`API server listening on port ${port}`)
})
}
Then in /api/routes/salesforce.js:
const e = require('express')
const { Router } = require('express')
const router = Router()
const jsforce = require('jsforce');
// require the .env file for login
require('dotenv').config();
const { SF_USERNAME, SF_PASSWORD, SF_TOKEN, SF_LOGIN_URL } = process.env;
if (!(SF_USERNAME && SF_PASSWORD && SF_TOKEN && SF_LOGIN_URL)) {
console.error(
'Cannot start app: missing mandatory configuration. Check your .env file.'
);
process.exit(-1);
}
const conn = new jsforce.Connection({
loginUrl: SF_LOGIN_URL
});
conn.login(SF_USERNAME, SF_PASSWORD + SF_TOKEN, err => {
if (err) {
console.error(err);
process.exit(-1);
}
});
// get OPPORTUNITY specific
router.get('/salesforce/:id', function (req, res, next) {
const id = req.params.id
console.log(req.params.id)
const sfData = conn.query(`SELECT Id, Name, StageName FROM Opportunity WHERE Name = '` + id + `'`, (err, res)=>{
if(err){
console.log(err)
return "error";
} else {
console.log(res.records[0])
let sanitisedData = res.records[0]
return sanitisedData;
}
})
res.json(sfData.json)
})
If anyone can tell me where I'm going wrong that would be greatly appreciated, I'm kind of stuck here.
I have setup a express.js + next.js app, which is running fine in development environment. When I am trying to run its webpack bundle, Its throwing error
Error: Cannot find module '/Users/user/workspace/project/next.config.js'
I am trying to run its bundle as aws-lamda is not allowing me to upload zip size more than 50MB.
// server.js
const express = require('express');
const argv = require('yargs').argv;
const nextApp = require('./nextApp.js');
const handle = nextApp.getRequestHandler();
const pageRoutes = require('./routes/pages/index.js');
const port = argv.port || 3000;
const server = express();
// route to next.js web pages
server.use('/', pageRoutes);
server.get('*', (req, res) => {
return handle(req, res)
});
nextApp.prepare()
.then(() => {
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
});
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
});
module.exports = server;
So far I have found that we can/should extends next.config.js for adding additional bundle entries instead of creating a separate webpack.config.js. Following config will create a serverbundle.js file in build/server directory.
const merge = require('webpack-merge');
module.exports = {
distDir: 'build',
webpack (config, {isServer}) {
if (isServer) {
return merge(config, {
entry () {
return config.entry().then((entry) => {
return Object.assign({}, entry, { serverbundle: './server' })
})
},
output: {
filename: '[name].js'
}
});
}
return config;
}
}
Iam using next.js and express.js . if I use a middleware (server.use(req, res, next)) it gets hit dozen times while the page is compiling, which makes my app crash completly ...
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
console.log('page loaded')
app.prepare().then(() => {
const server = express()
server.use((req, res, next) => {
console.log('ping')
next();
})
server.get('/', (req, res) => {
return app.render(req, res, '/index', req.query)
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
If I try to load the page, it prints
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
of course if I put a real middleware here it makes everything crash ...
I wish for the middleware to run only if the page is actually requesting something ... So only once per 'real user' request
I had the similar trouble, but only on client-side. In my case there were a lot of parasitic requests (trackers, internal API, fonts, some _next_static things). They triggered the middleware and it was the reason of ERR_TOO_MANY_REDIRECTS.
So, as for me, filtering parasitic requests and redirects helped to solve the problem:
if (new RegExp(/^.*(fonts|_next|vk.com|favicon).*$/).test(request.url)) {
return NextResponse.next()
}
I'm trying to use the Express Router with Next.js using their custom-express-server example as my boilerplate. The only difference is that I'm trying to define the routes externally on routes/router.js as follows:
Code in server.js:
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const routes = require('./routes/router')
app.prepare()
.then(() => {
const server = express()
server.use('/', routes)
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
module.exports = app;
Code in routes/router.js:
const express = require('express'),
app = require('../server.js'),
router = express.Router();
router.get('/a', (req, res) => {
return app.render(req, res, '/b', req.query)
})
router.get('/b', (req, res) => {
return app.render(req, res, '/a', req.query)
})
router.get('/posts/:id', (req, res) => {
return app.render(req, res, '/posts', { id: req.params.id })
})
module.exports = router;
At this point, even when I'm importing "app" from server.js, app is not available within router.js.
Is my logic incorrect?
If it's not, then why is app not available within router.js?
Just solved it. This issue is known as a circular dependency, and it should be avoided at all costs... unless the pattern you're using (like the boilerplate I used, I guess...) requires it.
To solve it, just export from file "A" the dependency that file "B" uses before you require file "B" on file "A".
...And that's it pretty much.
You might also try using next-routes, which I use on all of my Next project:
// server.js
const { createServer } = require('http');
const next = require('next');
const routes = require('./routes');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handler = routes.getRequestHandler(app);
app.prepare().then(() => {
createServer(handler).listen(port, err => {
if (err) {
throw err;
}
console.log(`> Ready on http://localhost:${port}`);
});
});
Then you can configure your routes in the routes.js file without accessing the app:
// routes.js
const nextRoutes = require('next-routes');
const routes = (module.exports = nextRoutes());
routes
.add('landing', '/')
.add('blog', '/blog', 'blog')
.add('blog-post', '/blog/:postId', 'blog')