UNPKG

ran-boilerplate

Version:

React . Apollo (GraphQL) . Next.js Toolkit

162 lines (134 loc) 3.92 kB
/* eslint-disable no-console */ const express = require('express'); const next = require('next'); const compression = require('compression'); const LRUCache = require('lru-cache'); const path = require('path'); const fs = require('fs'); const cors = require('cors'); const helmet = require('helmet'); const dotenv = require('dotenv'); dotenv.config(); const router = require('./routes'); const logger = require('./server/logger'); const isDev = process.env.NODE_ENV !== 'production'; const isProd = !isDev; const ngrok = isDev && process.env.ENABLE_TUNNEL ? require('ngrok') : null; const customHost = process.env.HOST; const host = customHost || null; const prettyHost = customHost || 'localhost'; const port = parseInt(process.env.PORT, 10) || 3000; const publicEnvFilename = 'public.env'; const app = next({ dev: isDev }); const handle = app.getRequestHandler(); const ssrCache = new LRUCache({ max: 100, maxAge: 1000 * 60 * 60 // 1hour }); // share public env variables (if not already set) try { if (fs.existsSync(path.resolve(__dirname, publicEnvFilename))) { const publicEnv = dotenv.parse( fs.readFileSync(path.resolve(__dirname, publicEnvFilename)) ); Object.keys(publicEnv).forEach(key => { if (!process.env[key]) { process.env[key] = publicEnv[key]; } }); } } catch (err) { // silence is golden } const buildId = isProd ? fs.readFileSync('./.next/BUILD_ID', 'utf8').toString() : null; /* * NB: make sure to modify this to take into account anything that should trigger * an immediate page change (e.g a locale stored in req.session) */ const getCacheKey = function getCacheKey(req) { return `${req.url}`; }; const renderAndCache = function renderAndCache( req, res, pagePath, queryParams ) { const key = getCacheKey(req); if (ssrCache.has(key) && !isDev) { console.log(`CACHE HIT: ${key}`); res.send(ssrCache.get(key)); return; } app .renderToHTML(req, res, pagePath, queryParams) .then(html => { // Let's cache this page if (!isDev) { console.log(`CACHE MISS: ${key}`); ssrCache.set(key, html); } res.send(html); }) .catch(err => { app.renderError(err, req, res, pagePath, queryParams); }); }; const routerHandler = router.getRequestHandler( app, ({ req, res, route, query }) => { renderAndCache(req, res, route.page, query); } ); app.prepare().then(() => { const server = express(); server.use(compression({ threshold: 0 })); server.use( cors({ origin: prettyHost.indexOf('http') !== -1 ? prettyHost : `http://${prettyHost}`, credentials: true }) ); server.use(helmet()); server.use(routerHandler); server.get(`/favicon.ico`, (req, res) => app.serveStatic(req, res, path.resolve('./static/icons/favicon.ico')) ); server.get('/sw.js', (req, res) => app.serveStatic(req, res, path.resolve('./.next/sw.js')) ); server.get('/manifest.html', (req, res) => app.serveStatic(req, res, path.resolve('./.next/manifest.html')) ); server.get('/manifest.appcache', (req, res) => app.serveStatic(req, res, path.resolve('./.next/manifest.appcache')) ); if (isProd) { server.get('/_next/-/app.js', (req, res) => app.serveStatic(req, res, path.resolve('./.next/app.js')) ); const hash = buildId; server.get(`/_next/${hash}/app.js`, (req, res) => app.serveStatic(req, res, path.resolve('./.next/app.js')) ); } server.get('*', (req, res) => handle(req, res)); server.listen(port, host, err => { if (err) { return logger.error(err.message); } if (ngrok) { ngrok.connect(port, (innerErr, url) => { if (innerErr) { return logger.error(innerErr); } logger.appStarted(port, prettyHost, url); }); } else { logger.appStarted(port, prettyHost); } }); });