I have a shared hosting provider that enables hosting node apps. It has a restriction that the app entry file must be called app.js and it must be in folder /usr/home/username/domains/domain/public_nodeapp. The app is started automatically (probably by something like node app.js) upon first access from the web to the domain.
Is it possible to host a next.js app on such a provider as a server-side rendered app (not as static HTML site produced by next export)?
After running next build which makes a production version of the app, the production version has no app.js file and should be started by next start. I am not sure whether and how it could be tweaked (perhaps some file moving or renaming) to match the restrictions mentioned above.
You can put a app.js file that is placed in the required place, and then call the function that will be called when you trigger next start from the cli.
That means that this file should require https://github.com/zeit/next.js/blob/canary/packages/next/cli/next-start.ts and invoke nextStart.
// app.js
const startServer = require('next/dist/cli/next-start');
startServer();
Yes you can do it using a custom server eg. express.js
I am doing the same thing for Azure, the only difference is that I need a file called server.js
Example:
// app.js
const express = require('express');
const next = require('next');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.all('*', (req, res) => handle(req, res));
server.listen(port, (err) => {
if (err) throw err;
/* eslint no-console: "off" */
console.log(`> Ready on http://localhost:${port}`);
});
});
I hope this helps.
Related
I am having an issue with a website I am making for my portfolio. It is hosted on firebase hosting, using the firebase cloud functions for the API calls. my single page react app calls an express router api to query a mongodb database for a list of restaurants. while the firebase emulator is running, I can retrieve the array by going to the api url in my browser, but the web component isn't able to fetch the list and returns an SSL protocol error. Everything I have been able to find so far points to this being a cors issue, but cors() is definitely being use()'d in the server, so I'm a bit lost.
I have tried:
Using the cors npm package in my server, code below:
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const cors = require('cors');
require('dotenv').config({path : './server/config.env'});
const path = require('path');
const publicPath = path.join(__dirname, '..', 'public');
app.use(express.static(publicPath));
app.use(cors());
app.use(express.json());
app.use(require('./server/routes/record'));
exports.api = functions.https.onRequest(app);
Using a mode:cors option in the fetch request: fetch('https://API_URL_HERE', {mode : 'cors'});
Using axios and sending a header with the request: axios.get('https://API_URL', {headers: {'Access-Control-Allow-Origin' : '*'}});
Here is the route I am using as well - I'm using ExpressRouter, not plain Express.js. The problem persisted when I translated it to express.js.
//Returns a list of all restaurants present in the database. The restaurants are objects structured as
// {'name': name,
// 'style': (the type of cuisine),
// 'menu' : (the available items)}
recordRoutes.route('/restaurants').get(async function (req, res) {
console.log('connecting to restaurants db')
await client.connect();
client.db()
.collection('Restaurants')
.find({})
.toArray(function (err, result) {
if (err)
console.log(err);
res.json(result);
});
});
toArray is marked as deprecated in VSC but I can't think of any reason that would be an issue - all the other routes to the database that don't use toArray are nonfunctional.
I'm using react with nextjs. I decided to use CDN and added assetPrefix to next.config file with cdn domain. It works, now I get files from cdn but the problem is: routing does not work. I use next-routes library and change url with Router.pushRoute method. I can open a page I need if change url manually. Also I have noticed that if to pass {shallow: true} option to this method, then url changes as expected. Server on nodejs with express. getInitialProps for data fetching
Maybe these code samples will be helpfull
routes
const nextRoutes = require('next-routes');
const routes = require('./config/routes');
/**
* #type {Registry}
*/
module.exports = nextRoutes()
// first add static routes
.add(routes.EXAMPLES)
.add(routes.LOGIN)
.add(routes.SIGNIN)
.add(routes.SIGNUP)
.add(routes.STATIC_PAGE)
.add(routes.SETTINGS)
.add(routes.APPLY)
.add(routes.PRIVACY_POLICY)
.add(routes.TERMS_AND_CONDITIONS)
// then add dynamic routes
.add(routes.DOCUMENT)
.add(routes.HOME);
server.js
const routes = require('./routes');
const { DEV, PORT, HOST, GRAPHQL_APP_URL } = require('./config/vars');
// Create body-parser json middleware
const bodyParserJSON = bodyParser.json();
// Create the Express-Next App
const app = next({ dev: DEV });
const handle = routes.getRequestHandler(app);
// Start the app
app
.prepare()
// Start Express server and serve the
.then(() => {
// eslint-disable-next-line no-console
console.log(`using ${GRAPHQL_APP_URL} as API URL`);
const server = express()
// use proxy middleware to send graphql requests to api server
.use(GRAPHQL_APP_URL, bodyParserJSON, graphqlProxyMiddleware)
.use(secure)
.use((_req, res, nextRedirect) => {
newrelic.setTransactionName(res.req.path);
nextRedirect();
})
.get('/apply-now', createRateLimiter())
.get('/apply-now/:refId', createRateLimiter())
.use(handle)
.listen(PORT, HOST, (err) => {
if (err) throw err;
// eslint-disable-next-line no-console
console.log(`> Ready on ${server.address().address}:${server.address().port}`);
});
})
thank you in advance
I am working on a project that is using Vercel's NextJS build with Firebase Hosting. I've added Firebase functions which I have working well. In the firebaseFunctions.js file I created an export so that I could share the connection to Firebase with other files. However, when trying to use the export I am getting the following error.
The file structure of the project looks like the following.
The following file is the firebaseFunctions.js file that includes the Firebase app initialization and Firestore initialization. All code in this file works when using functions or trying to connect to Firestore.
const { join } = require('path'); // From NextJS Vercel Base Build
const { default: next } = require('next'); // From NextJS Vercel Base Build
const isDev = process.env.NODE_ENV !== 'production'; // From NextJS Vercel Base Build
const nextjsDistDir = join('src', require('./src/next.config.js').distDir); // From NextJS Vercel Base Build
const admin = require('firebase-admin'); // Firebase Admin SDK for NodeJS.
const functions = require('firebase-functions'); // For NextJS + Firebase Functions + Firebase Hosting.
const serviceAccount = require('./serviceAccountKey.json'); // Service account key for Firebase Admin SDK.
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const db = admin.firestore();
const nextjsServer = next({
dev: isDev,
conf: {
distDir: nextjsDistDir,
},
});
const nextjsHandle = nextjsServer.getRequestHandler();
// Nextjs Cloud Function to allow for Firebase Hosting.
exports.nextjsFunc = functions.https.onRequest((req, res) => {
return nextjsServer.prepare().then(() => nextjsHandle(req, res));
});
exports.getCustomToken = functions.https.onCall(async (data, context) => {
// Do something here.
});
module.exports.admin = admin;
module.exports.db = db;
You can see that I export both admin and db here.
In CreateTimer I require db. However this does not allow me access to db.
const db = require('./../firebase/firebase');
Any help with why this may be would be appreciated.
I've done the following.
Verified that Firebase is installed.
Tested Firebase from firebaseFunctions.js
My issue was that I was trying to use the Firebase Admin SDK with React which is a front side language. Firebase Admin can only be used with backend environment like Nodejs. I created a web project in Firebase and then configured this for use in the app.
I'm trying to implement Nuxt with SSR in Firebase hosting (using Firebase functions), but after my function is triggered I keep getting an '504 timed out waiting for function to respond'.
My Firebase function:
const functions = require("firebase-functions");
const { Nuxt } = require("nuxt");
const express = require("express");
const app = express();
const config = {
dev: false,
buidlDir: 'src',
build: {
publicPath: '/'
}
};
const nuxt = new Nuxt(config);
function handleRequest(req, res){
console.log('handling request');
//res.set('Cache-Control', 'public, max-age=600, s-maxage=1200')
nuxt.renderRoute('/')
.then(result => {
console.log('result: ' + result.html);
res.send(result.html);
})
.catch(e => {
res.send(e);
console.log(e);
})
}
app.get('*', handleRequest);
exports.ssrApp = functions.https.onRequest(app);
I also tried with:
function handleRequest(req, res) {
console.log("log3");
res.set("Cache-Control", "public, max-age=300, s-maxage=600");
return new Promise((resolve, reject) => {
nuxt.render(req, res, promise => {
promise.then(resolve).catch(reject);
});
});
}
I also have node vs8 as default for my functions because I read that that could give problems. :
"engines": {
"node": "8"
},
But with the same result. My function is being triggered but it always times out, btw: I have this problem serving locally and when trying to deploy to Firebase itself.
Let me know if you need more information/code to try to help and see what the problem could be.
First, if you want to find out what caused it, use debug option.
Second, if you face the timeout error, check the path is valid.
If you success build Nuxt and nuxt.render, the error is processed by Nuxt, and Nuxt show this error page.
In other words, if you don't see Nuxt error page, the cause may be not related with Nuxt.
I also stuck 4 hours due to timeout error, and I finally found out the cause was the contents of publicPath.
Please check these 2 things.
buidlDir is valid ?
The path of buildDir is valid ? You should check .nuxt folder is deployed to your cloud functions successfully.
publicPath contents is uploaded successfully?
The builded contents in .nuxt/dist must be uploaded to Firebase Hosting. You should check it manually.
Type URL to address bar ex) https://test.firebaseapp.com/path/to/file.js
Finally, I post a URL of
my sample project, using Nuxt and Firebase.
I also stucked like you and it made me rally tired. I'm really happy if this answer helps someone like me.
PS: When you build Nuxt in functions folder, nuxt start is failed. Be careful. In my project, I build it in root, and when deploy, I copied it.
Nuxt SSR with Firebase Integration
I got the same problem because Nuxt is not ready yet (promise is undefined)
So you can try to add nuxt.ready() after new Nuxt()
Example:
const functions = require('firebase-functions');
const express = require('express');
const { Nuxt } = require('nuxt');
const config = {
dev: false
// Your config
};
const nuxt = new Nuxt(config);
const app = express();
nuxt.ready(); // <---------- Add this!
async function handleRequest(req, res) {
res.set('Cache-Control', 'public, max-age=1, s-maxage=1');
await nuxt.render(req, res);
}
app.get('*', handleRequest);
app.use(handleRequest);
exports.ssrApp = functions.https.onRequest(app);
Ref: https://github.com/nuxt/nuxt.js#using-nuxtjs-programmatically
I'm building my web app on top of Firebase cloud functions, and hosting. My client was built with create-react-app, and I have a small express server running in a cloud function that serves the app build.
Everything works fine in a normal browser window however, I am having an issue where if I go to any url that is not '/' in incognito mode or a mobile device I get a 404 and "Not Found"
ie: hitting "https://144blocks.com/week" directly will load fine in a normal browser window but throws the following error in incognito and mobile.
My firebase function for serving the site is:
functions/index.js
const functions = require('firebase-functions');
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
app.get('/*', (req, res) => {
const fullPath = path.normalize(__dirname + '/../react-ui/build/index.html');
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
res.sendFile(fullPath);
});
exports.app = functions.https.onRequest(app);
In my client my firebase.json file:
firebase.json
{
"hosting": {
"public": "react-ui/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "app"
}
]
}
}
I'm not sure why I can't hit any route directly aside from '/' in an incognito window or a mobile device. Ideally, I want all my routes to flow through the one app.get(/* on my express server and have my client handle the routing.
There is currently a problem with Firebase Hosting, which appears to be affecting a number of sites, including two of my own. You can stay up-to-date with their status updates via this link.
I am still not 100% sure as to why this is working however I fixed the issue by:
1. In my functions/index.js file, updating the fullPath because I believe that the __dirname + '/../react-ui/build/index.html' wasn't resolving.
My name functions/index.js file:
const functions = require('firebase-functions');
const express = require('express');
const path = require('path');
const app = express();
app.get('/*', (req, res) => {
const fullPath = path.normalize(__dirname + '/build/index.html');
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
res.sendFile(fullPath);
});
exports.app = functions.https.onRequest(app);
On my build process, I'm moving the build directory into the functions folder to get deployed to Firebase with the functions so my project directory when I'm getting ready to deploy looks like where the build directory in functions and react-ui is identical:
If anyone has more insight into why this is exactly working I would appreciate additional information.
Thanks!
Try running firebase login. I had this exact issue and it was because my login session had expired.
Alternatively, try firebase login --no-localhost if you're having trouble with the standard login.
If you're using ci/cd, use firebase login:ci --no-localhost, save your ci token to your environment under FIREBASE_TOKEN and deploy with firebase deploy --only functions --token $FIREBASE_TOKEN.