I am creating a NextJS application and I want to copy ONLY the production files to my server and deploy the application. This works perfectly fine when I do NOT use Express, however when i do use Express the app no longer runs on the server as it says it can not find the "pages" directory.
Project structure
|- .next
|- client
|- polyfills.js
|- components
|- CompA.js
|- CompB.js
|- etc
|- node-modules
|- ...
|- pages
|- index.js
|- page1.js
|- page2.js
|- etc
|- public
|- appIcon.png
|- scripts
|- utils.js
|- stylesheets
|- main.css
|- next.config.js
|- package-lock.json
|- package.json
My steps
I develop the application locally and test it my running "npm run dev". When the app is ready to deploy to a server I copy JUST the production files to the server and deploy the application. It is my understanding that the built application is ALL contained in the ".next" directory, therefore this is the only directory I copy to my server.
Here are my full steps in order to get the application on the server
- Develop the application locally
- Test locally using "npm run dev"
- Test locally using "npm run build" and then "npm run start"
- Application runs perfectly fine
- Move the files to the server location and deploy
- cp .next ~/myTestFolder
- cp package.json ~/myTestFolder
- cp package-lock.json ~/myTestFolder
- cp next.config.js ~/myTestFolder
- cd ~/myTestFolder
- npm install
- npm run build
- npm run start
- Application runs perfectly fine
The process fails when i use Express
As soon as I use Express, this process no longer works. I need to use express and dotenv so I can have a customizable basePath for my application which can differ on the different servers I deploy my application to (see my other stack overflow question : NextJS deploy to a specific URL path)
I did the following:
Install the new dependencies
npm install --save express dotenv
Create a ".env" file
BASE_PATH=/my-test-application/path-for-my-app
Update "next.config.js" to read the variables from ".env" and pass to WebPack so it can be used in ".js" files
// support getting variables from the server environment
const { parsed: localEnv } = require('dotenv').config()
const webpack = require('webpack')
module.exports = {
// Stop being being able to access pages without using proper URL
useFileSystemPublicRoutes: false,
webpack: function(cfg) {
// pass the environment variables to webpack so JS files can access them
cfg.plugins.push(new webpack.EnvironmentPlugin(localEnv))
....
}
....
}
Update the scripts in "package.json"
"scripts": {
"dev": "node ssr-server.js",
"build": "next build",
"start": "node ssr-server.js"
},
Create a "ssr-server.js" file to do the routing
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()
app.prepare().then(() => {
const server = express()
server.get('/my-test-application/path-for-my-app', (req, res) => {
return app.render(req, res, '/index', req.query)
})
server.get('/my-test-application/path-for-my-app/page1', (req, res) => {
return app.render(req, res, '/page1', 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}`)
})
})
Update any "Link"s to use this path
render(){
let basePath = process.env.BASE_PATH? process.env.BASE_PATH: "";
...
<Link href={`${basePath}/page1`}>
...
I can run the application locally using "npm run dev" and "npm run build"/"npm run start".
However when I copy files to my server location it now fails. I copy all the same files as above with the addition of the 2 new files
- cp ssr-server.js ~/myTestFolder
- cp .env ~/myTestFolder
I run "npm build" OK, but when I run "npm run start" it fails with the following
$ npm run start
> React_SSR@1.0.0 start /home/myTestFolder
> node ssr-server.js
/home/myTestFolder/node_modules/next/dist/lib/find-pages-dir.js:3
if(existsSync(_path.default.join(dir,'..','pages'))){throw new Error('> No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?');}throw new Error("> Couldn't find a `pages` directory. Please create one under the project root");}
^
Error: > Couldn't find a `pages` directory. Please create one under the project root
at findPagesDir (/home/myTestFolder/node_modules/next/dist/lib/find-pages-dir.js:3:170)
at new DevServer (/home/myTestFolder/node_modules/next/dist/server/next-dev-server.js:1:3830)
at createServer (/home/myTestFolder/node_modules/next/dist/server/next.js:2:105)
at Object.<anonymous> (/home/myTestFolder/ssr-server.js:5:13)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! React_SSR@1.0.0 start: `node ssr-server.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the React_SSR@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/myTestFolder/.npm/_logs/2020-03-06T10_24_04_105Z-debug.log
Any idea what I am missing/doing wrong?
See Question&Answers more detail:
os