next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Docs: `output: "standalone"` conflicts with discouraged usage of Custom Servers

Open larsqa opened this issue 2 years ago • 2 comments

What is the improvement or update you wish to see?

The Custom Server documentation highlights

A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.

However, output:"standalone" builds a custom server inside .next/standalone/server.js which is used in the official Docker examples.

I'm not sure if this is intended, a bug, or requires additional documentation, but this seems very confusing.

Why would a Next.js feature yield something that is a Next.js "antipattern"?

larsqa avatar Sep 07 '22 08:09 larsqa

It's not that 'custom' as according to the documentation:

Additionally, a minimal server.js file is also output which can be used instead of next start. Reference

And in fact it does look like minimal version of nextjs server if you look inside server.js. I am assuming it does provide all nice features just like it is with 'normal' next start.

michal-mad avatar Nov 05 '22 12:11 michal-mad

Here's the minimal server.js which Next.js currently (12.2.2) outputs:


process.env.NODE_ENV = 'production'
process.chdir(__dirname)
const NextServer = require('next/dist/server/next-server').default
const http = require('http')
const path = require('path')

// Make sure commands gracefully respect termination signals (e.g. from Docker)
// Allow the graceful termination to be manually configurable
if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
  process.on('SIGTERM', () => process.exit(0))
  process.on('SIGINT', () => process.exit(0))
}

let handler

const server = http.createServer(async (req, res) => {
  try {
    await handler(req, res)
  } catch (err) {
    console.error(err);
    res.statusCode = 500
    res.end('internal server error')
  }
})
const currentPort = parseInt(process.env.PORT, 10) || 3000

server.listen(currentPort, (err) => {
  if (err) {
    console.error("Failed to start server", err)
    process.exit(1)
  }
  const nextServer = new NextServer({
    hostname: 'localhost',
    port: currentPort,
    dir: path.join(__dirname),
    dev: false,
    conf: {"env":{},"webpackDevMiddleware":null,"eslint":{"ignoreDuringBuilds":false},"typescript":{"ignoreBuildErrors":false,"tsconfigPath":"tsconfig.json"},"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","configOrigin":"next.config.js","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"target":"server","poweredByHeader":true,"compress":true,"analyticsId":"","images":{"deviceSizes":[480,768,992,1280,1536],"imageSizes":[16,32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","domains":["images.ctfassets.net"],"disableStaticImages":false,"minimumCacheTTL":60,"formats":["image/webp"],"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;"},"devIndicators":{"buildActivity":true,"buildActivityPosition":"bottom-right"},"onDemandEntries":{"maxInactiveAge":15000,"pagesBufferLength":2},"amp":{"canonicalBase":""},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"optimizeFonts":true,"excludeDefaultMomentLocales":true,"serverRuntimeConfig":{},"publicRuntimeConfig":{},"reactStrictMode":true,"httpAgentOptions":{"keepAlive":true},"outputFileTracing":true,"staticPageGenerationTimeout":60,"swcMinify":false,"output":"standalone","experimental":{"manualClientBasePath":false,"legacyBrowsers":true,"browsersListForSwc":false,"newNextLinkBehavior":false,"cpus":9,"sharedPool":true,"profiling":false,"isrFlushToDisk":true,"workerThreads":false,"pageEnv":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"swcFileReading":true,"craCompat":false,"esmExternals":true,"appDir":false,"isrMemoryCacheSize":52428800,"serverComponents":false,"fullySpecified":false,"outputFileTracingRoot":"","images":{"remotePatterns":[]},"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"trustHostHeader":false},"configFileName":"next.config.js"},
  })
  handler = nextServer.getRequestHandler()

  console.log("Listening on port", currentPort)
})
    

Above standalone server.js has similarities with:

However, what's the key element that removes/add's the Next.js features lost with a custom server:

Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.

Source

larsqa avatar Nov 07 '22 08:11 larsqa