diff --git a/.changeset/fastify-port-and-pino-pretty.md b/.changeset/fastify-port-and-pino-pretty.md new file mode 100644 index 0000000..9a8c597 --- /dev/null +++ b/.changeset/fastify-port-and-pino-pretty.md @@ -0,0 +1,13 @@ +--- +'seamless-templates': patch +--- + +Fix two boot-time footguns in the Fastify API starter. + +`PORT` is read with `||` rather than `??`, so an empty `PORT=` in `.env` falls back to 3000. It +previously reached `Number("")`, which is `0`, and Fastify binds port 0 to a random free port, so the +API came up somewhere nobody was looking. + +`pino-pretty` moves from `devDependencies` to `dependencies`. The logger loads it for any `NODE_ENV` +other than `production`, so an install that omitted dev dependencies, which is the usual shape of a +staging deploy, failed to boot on a missing transport target. diff --git a/templates/api/fastify/package-lock.json b/templates/api/fastify/package-lock.json index 6f3b2fe..9b0ccf8 100644 --- a/templates/api/fastify/package-lock.json +++ b/templates/api/fastify/package-lock.json @@ -17,6 +17,7 @@ "pg": "^8.16.3", "pg-hstore": "^2.3.4", "pino": "^10.3.1", + "pino-pretty": "^13.1.3", "sequelize": "^6.37.8" }, "devDependencies": { @@ -27,7 +28,6 @@ "eslint": "^9.39.1", "globals": "^16.5.0", "jiti": "^2.6.1", - "pino-pretty": "^13.1.3", "sequelize-cli": "^6.6.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", @@ -1765,7 +1765,6 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -1835,7 +1834,6 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -1975,7 +1973,6 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -2214,7 +2211,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.4.tgz", "integrity": "sha512-eVAiWVNPSEGIzDl5yPuLrx8fNMogScXvD9xp1Kzd41FjRIz2I3sSIcxsFeM5EzFfHAfobdvs8ZySffUopljvIA==", - "dev": true, "license": "MIT" }, "node_modules/fast-decode-uri-component": { @@ -2334,7 +2330,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -2670,7 +2665,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", - "dev": true, "license": "MIT" }, "node_modules/ignore": { @@ -2830,7 +2824,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3160,7 +3153,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3239,7 +3231,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -3515,7 +3506,6 @@ "version": "13.1.3", "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", - "dev": true, "license": "MIT", "dependencies": { "colorette": "^2.0.7", @@ -3540,7 +3530,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", - "dev": true, "license": "MIT", "engines": { "node": ">=14.16" @@ -3631,7 +3620,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -4503,7 +4491,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/xtend": { diff --git a/templates/api/fastify/package.json b/templates/api/fastify/package.json index 524e639..4568303 100644 --- a/templates/api/fastify/package.json +++ b/templates/api/fastify/package.json @@ -25,6 +25,7 @@ "pg": "^8.16.3", "pg-hstore": "^2.3.4", "pino": "^10.3.1", + "pino-pretty": "^13.1.3", "sequelize": "^6.37.8" }, "devDependencies": { @@ -35,7 +36,6 @@ "eslint": "^9.39.1", "globals": "^16.5.0", "jiti": "^2.6.1", - "pino-pretty": "^13.1.3", "sequelize-cli": "^6.6.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", diff --git a/templates/api/fastify/src/index.ts b/templates/api/fastify/src/index.ts index 0938cb0..e6d5f3a 100644 --- a/templates/api/fastify/src/index.ts +++ b/templates/api/fastify/src/index.ts @@ -21,7 +21,9 @@ dotenv.config(); assertEnvironment(); -const PORT = Number(process.env.PORT ?? 3000); +// `||` rather than `??`: an empty PORT= in .env is a missing value, not a +// request for port 0, which is what Number("") would bind. +const PORT = Number(process.env.PORT || 3000); const logger = getLogger("index"); const rawOrigins = process.env.UI_ORIGINS; diff --git a/templates/api/fastify/src/lib/logger.ts b/templates/api/fastify/src/lib/logger.ts index 17ec9e9..4a29281 100644 --- a/templates/api/fastify/src/lib/logger.ts +++ b/templates/api/fastify/src/lib/logger.ts @@ -10,8 +10,10 @@ const isProduction = process.env.NODE_ENV === "production"; * keeps its diagnostics in the same stream as the rest of the API instead of a * second one nobody is watching. * - * pino-pretty is a devDependency and is only wired up outside production, where - * logs stay as JSON for a log pipeline to parse. + * pino-pretty is only wired up outside production, where logs stay as JSON for a + * log pipeline to parse. It is a runtime dependency rather than a dev one + * because any NODE_ENV other than production loads it, so an install that + * omitted dev dependencies would fail to boot on a staging deploy. */ export const rootLogger: Logger = pino({ level: isProduction ? "info" : "debug",