-
Notifications
You must be signed in to change notification settings - Fork 0
Audit, upgrade and stabilize dependencies: Node 22, Express 5, Apollo Server 4, Prisma 7 #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mac-os-readme-fixes
Are you sure you want to change the base?
Conversation
- Update Dockerfile (dev) from node:20-slim to node:22-slim - Update Dockerfile.production from node:20-alpine to node:22-alpine (both stages) - Update CI workflow from node-version 20 to 22 Node 22 is the active LTS (maintained through April 2027) and brings require(esm) support, stable fetch API, and performance improvements. Co-authored-by: Cursor <cursoragent@cursor.com>
npm audit fix resolved: - critical: form-data unsafe random boundary - high: body-parser/qs/express DoS via url encoding and arrayLimit bypass - moderate: js-yaml and lodash prototype pollution - low: @eslint/plugin-kit ReDoS, tmp symlink write, diff DoS Co-authored-by: Cursor <cursoragent@cursor.com>
npm update applied safe semver-compatible updates to 35 packages including: - express 4.21.2->4.22.1, body-parser 2.2.0->2.2.2, cors 2.8.5->2.8.6 - pg 8.16.3->8.18.0, winston 3.17.0->3.19.0, graphql 16.11.0->16.12.0 - typescript 5.8.3->5.9.3, prettier 3.6.2->3.8.1, supertest 7.1.1->7.2.2 - typescript-eslint 8.35.1->8.54.0, ts-jest 29.4.0->29.4.6, tsx 4.20.3->4.21.0 Co-authored-by: Cursor <cursoragent@cursor.com>
- husky 8->9: simplified hook format (removed husky.sh sourcing), prepare script updated - dotenv 16->17: transparent for import dotenv/config usage - cross-env 7->10: drop-in replacement, bumps minimum Node version Co-authored-by: Cursor <cursoragent@cursor.com>
- @prisma/client + prisma 6.x -> 7.3.0: new major release, straightforward for this project - uuid 11.x -> 13.0.0: stable API (validate, v4), drop-in compatible - joi 17.x -> 18.0.2: standard validation schemas remain compatible Note: prisma 7 brings 8 moderate audit findings in its dev tooling (hono, lodash via @prisma/dev) - these are upstream issues, not runtime. Co-authored-by: Cursor <cursoragent@cursor.com>
xss-clean is abandoned and was never imported in any source file. The project already uses helmet for security headers. XSS protection is handled via output encoding and helmet CSP headers. Co-authored-by: Cursor <cursoragent@cursor.com>
- Complete Prisma 7 migration: add prisma.config.ts for datasource URL,
remove url from schema.prisma, use PrismaPg driver adapter
- Add @prisma/adapter-pg dependency for Prisma 7 driver adapter pattern
- Downgrade uuid v13 -> v11 (v13 is ESM-only, incompatible with Jest)
- Fix protobuf CJS/ESM interop using createRequire with process.cwd()
so disaster_pb.js loads in both Jest CJS and Node.js ESM contexts
- Fix Express 5 type errors: type route params as Request<{ id: string }>
- Modernize jest.config.ts: migrate deprecated globals to transform config
- Add eslint.config.js flat config for ESLint 9 compatibility
- Add .nvmrc pinning Node.js to v22 for consistent local development
- Add generated/ to .gitignore for Prisma generated client output
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace bodyParser.json() with built-in express.json(). Remove body-parser dependency since Express 5 includes body parsing natively. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace EOL apollo-server-express with @apollo/server and @as-integrations/express5. Replace ApolloError/UserInputError with GraphQLError from the graphql package. Remove --force from Dockerfile npm install now that the Express 4 peer dependency conflict is gone. Add @types/cors, @graphql-tools/utils, and graphql-tag as direct dependencies (previously transitive via apollo-server-express). Co-authored-by: Cursor <cursoragent@cursor.com>
Remove unused isGraphQLNotFoundError function from resolvers (leftover from Apollo v3 migration). Apply prettier formatting from pre-commit hook. Bump protobufjs to ^7.5.4 within existing range. Co-authored-by: Cursor <cursoragent@cursor.com>
- Fix NODE_ENV in docker-compose.yml (test → development) so the API server actually starts for local development; tests still run with NODE_ENV=test via jest.setup.ts - Switch healthcheck to wget /healthz and increase start_period - Add prisma generate and schema copy to Dockerfile so the container builds without manual post-build steps - Use process.env fallback in prisma.config.ts to avoid the dummy POSTGRES_URI hack during Docker build - Switch nodemon exec to npx tsx for correct ESM resolution - Remove dead apolloReady promise, API_KEY env validation, and orphaned Apollo init code from app.ts and index.ts - Add .env.example with documented environment variables - Update README with accurate Docker-first quick start and local dev instructions - Add !.env.example exception to .gitignore Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughNode.js upgraded from 20 to 22 across all configurations. Apollo Server Express migrated to Apollo Server 5 with express5 integration. Prisma upgraded from v6 to v7 with PG adapter. Major dependencies updated: Express 4→5, rate-limit and Joi versions bumped. New configuration files added: eslint.config.js, prisma.config.ts, .env.example. Error handling refactored from ApolloError to GraphQLError. Build tooling simplified via tsx and Jest transform updates. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
routes/disasters.ts (1)
39-42:⚠️ Potential issue | 🟡 MinorUUID regex only matches versions 1–5; will reject v6/v7 UUIDs.
The
[1-5]in the version nibble position means this regex rejects UUIDs v6, v7, and v8, which are valid RFC 9562 variants. Since the project usesuuidv11 (which supports v7 generation), this could become a real issue if the ID generation strategy changes.Note: the
router.paramon line 85 already validates usingvalidatefrom theuuidpackage (which accepts all versions), making the inlineisValidUuidcalls on lines 253, 303, 338 redundant and more restrictive.♻️ Suggested: widen the regex or remove the redundant check
-const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; +const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;Or better yet, remove
isValidUuidentirely and rely solely on therouter.paramguard that already uses theuuidpackage'svalidate.README.md (2)
183-202:⚠️ Potential issue | 🟡 MinorDuplicate "Environment Variables" section.
There are two
### Environment Variablesheadings — one at line 118 (concise table) and another here at line 183 (longer prose). This second section appears to be a leftover from the pre-refactor README. Consider removing it to avoid confusion and keep documentation DRY.
346-357:⚠️ Potential issue | 🟡 MinorGraphQL example references
_id— should beid.The example query uses
_id, which is a MongoDB convention. Since the project has migrated to PostgreSQL with Prisma, this should likely beidto match the actual schema field.📝 Suggested fix
data { - _id + id type status descriptionindex.ts (1)
29-42:⚠️ Potential issue | 🟠 MajorDuplicate SIGTERM/SIGINT handlers conflict with
app.ts.
app.ts(lines 454-459) registers SIGINT/SIGTERM handlers guarded byprocess.listenerCount('SIGINT') === 0. SincecreateApp()is called before these listeners are registered here (line 22 runs before lines 41-42), theapp.tshandlers will be installed first. Thenindex.tsadds additional listeners for the same signals.On SIGTERM, both handlers fire:
app.ts'sgracefulShutdowncallsprocess.exit(0)synchronously, likely preempting theserver.close()callback here. The server may not drain in-flight connections.Consider consolidating signal handling in one place — either
index.ts(preferred, since it owns theServerinstance) orapp.ts, but not both.app.ts (4)
175-175:⚠️ Potential issue | 🟠 Major
helmet()is applied three times in the middleware stack.
- Line 175:
app.use(helmet())— default config- Line 213:
app.use(helmet())— default config again (duplicate)- Lines 222-239:
app.use(helmet({...}))— custom CSP/HSTS configEvery response passes through all three, with later invocations overwriting headers set by earlier ones. The second call (line 213) appears accidental. Consolidate into a single
helmet()call with the custom config.Proposed fix: remove duplicate calls
- // Security headers - app.use(helmet()); - // Rate limiting: disable or relax for testsand:
// Only apply JSON body parsing to REST routes (built into Express 5) app.use('/api', express.json()); - app.use(helmet()); app.use(hpp());Keep only the fine-tuned helmet call at lines 222-239.
Also applies to: 211-214, 222-239
327-328:⚠️ Potential issue | 🟠 Major
POSTGRES_URIlogged to console — may contain credentials.Line 328 logs
process.env.POSTGRES_URI, which typically includes the username and password in the connection string (e.g.,postgresql://user:password@host/db). This is a credential leak risk in logs.Remove or redact the log
async function updateDisasterMetrics() { - console.log('[updateDisasterMetrics] Using POSTGRES_URI:', process.env.POSTGRES_URI); + console.log('[updateDisasterMetrics] Updating disaster metrics...');
329-403:⚠️ Potential issue | 🟠 Major
updateDisasterMetricscreates and destroys aPoolevery 30 seconds.This function runs on a 30-second interval (line 407) and creates a new
Pooleach time (line 331), then callspool.end()in thefinallyblock (line 401). Creating/tearing down a connection pool this frequently is wasteful and adds latency.Consider using a module-scoped pool or, better yet, accepting the pool from
createAppvia a closure or shared reference.Sketch: use a shared pool
+let metricsPool: Pool | undefined; + async function updateDisasterMetrics() { - console.log('[updateDisasterMetrics] Using POSTGRES_URI:', process.env.POSTGRES_URI); - let pool: Pool | undefined; try { - pool = new Pool({ - connectionString: process.env.POSTGRES_URI, - }); + if (!metricsPool) { + metricsPool = new Pool({ connectionString: process.env.POSTGRES_URI }); + } + const pool = metricsPool; // ... rest of queries using pool ... } catch (error) { logger.error('Error updating disaster metrics', { error }); - } finally { - if (pool) { - await pool.end(); - } } }
442-459:⚠️ Potential issue | 🟠 MajorSignal handlers registered at module scope conflict with
index.ts.As noted in the
index.tsreview, these module-scope SIGINT/SIGTERM handlers (which callprocess.exit(0)immediately) conflict with the shutdown logic inindex.tsthat attempts to drain the HTTP server first. ThelistenerCount === 0guard on lines 454/457 won't help becauseapp.tsis evaluated beforeindex.tsregisters its own handlers.Additionally,
gracefulShutdowndoesn't actually close database pools — it just logs and exits. Consider consolidating shutdown logic inindex.tswhere theServerinstance is available, and have it also close pools.Dockerfile (1)
41-42:⚠️ Potential issue | 🟡 Minor
wgetis not available innode:22-slimand will cause the healthcheck to fail.The
node:22-slimimage (Debian bookworm-slim) does not includewgetorcurlby default. The healthcheck command on line 42 useswgetbut it is never installed, causing the healthcheck to always fail.Either install
wgetviaapt-get, or use a Node.js-based check:Alternative: use a Node.js one-liner for the healthcheck
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3000/healthz || exit 1 + CMD node -e "fetch('http://localhost:3000/healthz').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"
🤖 Fix all issues with AI agents
In `@Dockerfile.production`:
- Line 7: The build stage currently runs "RUN npm ci --only=production" which
omits devDependencies (like TypeScript and build tools) so the following "npm
run build" will fail; change the build stage to install all deps (use "npm ci"
without omitting dev deps) so tools required by the build scripts are present,
then in the final production stage install only runtime deps using the new npm
10+ flag (use "npm ci --omit=dev" or equivalent) to avoid bringing
devDependencies into the production image; update any references to
"--only=production" to "--omit=dev" to be compatible with npm 10+.
In `@package.json`:
- Line 35: The package.json dependency entry for "dotenv" uses a non-existent
version "^17.2.4"; update the version specifier for the "dotenv" dependency to a
valid published release (e.g., "^17.2.3") so npm/yarn can install it; locate the
"dotenv" line in package.json and change the version string accordingly and run
install to verify.
- Line 43: The project added "joi" v18 in package.json which requires Node.js
>=20 and changes TypeScript generics for arrays; update package.json (the "joi"
dependency) to ensure the Node engine requirement is declared (add or bump the
"engines" field to ">=20" and/or update CI/Dockerfiles to use Node 20+), and
audit TypeScript usages of Joi array generics—replace occurrences of
Joi.array<T>().items(...) with the new pattern Joi.array().items<T>(...) and
update any related imports/usages referencing Joi array typing to match v18
typings.
In `@services/prisma.ts`:
- Around line 4-8: The PrismaPg adapter is being constructed at module scope
using process.env.POSTGRES_URI which may be undefined due to import ordering;
change prisma.ts to lazily initialize the adapter and client (implement a
singleton getter like getPrisma() that constructs new PrismaPg and new
PrismaClient only on first call) or add an explicit guard at the top of
prisma.ts that throws or reads process.env.POSTGRES_URI before creating
PrismaPg; reference the existing symbols PrismaPg, PrismaClient and the exported
prisma instance (replace direct export with a getter or factory) so callers use
getPrisma()/prisma() after POSTGRES_URI is validated in app.ts.
🧹 Nitpick comments (14)
docker-compose.yml (1)
17-18: Consider usingdepends_onwith a health condition for reliable startup ordering.Currently, the
apiservice starts as soon as thepostgrescontainer is created, not when Postgres is actually ready to accept connections. Since you already have a healthcheck on thepostgresservice (viaoptions), you could usedepends_onwith a condition to avoid race conditions during startup.♻️ Suggested improvement
api: build: . depends_on: - - postgres + postgres: + condition: service_healthyNote: this requires adding a
healthcheckblock to thepostgresservice definition (the healthcheck inoptionsis only for CI). For example:postgres: ... healthcheck: test: ["CMD-SHELL", "pg_isready -U disasters"] interval: 10s timeout: 5s retries: 5nodemon.json (1)
5-5: Consider droppingnpxfor faster restarts.If
tsxis already indevDependencies, nodemon can resolve it directly fromnode_modules/.bin. Usingnpxadds a small resolution overhead on every restart during development.♻️ Suggested change
- "exec": "npx tsx index.ts", + "exec": "tsx index.ts",routes/disasters.protobuf.test.ts (2)
3-6: CJS interop approach is correct.Using
createRequireto load the protobuf CJS module from an ESM/TS context is the standard Node.js pattern.One minor note: the
import typeon line 7 is interleaved after theconstdeclarations on lines 5-6. While this is harmless (type imports are erased at compile time), grouping allimportstatements together is conventional and avoids potential linter warnings.♻️ Suggested reordering
import request from 'supertest'; import { createApp } from '../app'; import { createRequire } from 'module'; import path from 'path'; +import type { Server } from 'http'; + const requireCjs = createRequire(path.resolve(process.cwd(), 'package.json')); const disastersPb = requireCjs('./proto/disaster_pb.js'); -import type { Server } from 'http';
37-44: Test silently passes on non-200 responses due to loose assertion.If the endpoint returns a non-200 status (e.g., 500 due to a DB issue), the
elsebranch logs the error but then assertsres.type === 'application/x-protobuf', which will fail with a confusing message about content type rather than surfacing the actual status code issue. This is a pre-existing pattern, but worth noting since this test file was touched..gitignore (1)
38-40: The broad*.jsignore requires explicit un-ignoring for every tracked JS file.This is a known TypeScript-project pattern, but it can silently ignore new JS config files (e.g.,
jest.config.js,commitlint.config.js, etc.) if someone forgets to add an!entry. Just flagging for awareness — no action needed if the team is familiar with this convention.graphql/resolvers.ts (1)
63-67: Consider logging the original error before wrapping it.The bare
catchdiscards the original error, making debugging harder. The same pattern appears in other resolvers whereerris captured but only used for theinstanceofcheck — the original message/stack is lost in theINTERNAL_ERRORwrap.A lightweight improvement would be to log the original error (e.g., via your Winston logger) before re-throwing the sanitized
GraphQLError, at least for theINTERNAL_ERRORpath. This applies to all catch blocks in this file.graphql/resolvers.coverage.test.ts (2)
6-10: Casting toRecord<string, (...args: any[]) => any>loses all type safety.This is pragmatic for direct resolver invocation but means the test won't catch signature drift. An alternative is to type the resolver maps more narrowly using the generated
IResolverstype, though the current approach is common in resolver unit tests.
24-26: Tests don't assert the specificextensions.codevalues.The tests only verify that a
GraphQLErroris thrown, but don't check whether the code isBAD_USER_INPUT,NOT_FOUND, orINTERNAL_ERROR. Adding assertions onextensions.codewould ensure the correct error classification reaches clients.For example:
try { await Query.disaster({}, { id: undefined }, {}); } catch (e) { expect(e).toBeInstanceOf(GraphQLError); expect((e as GraphQLError).extensions?.code).toBe('BAD_USER_INPUT'); }routes/disasters.ts (1)
24-28: Useimport.meta.urlinstead ofprocess.cwd()for more robust CJS interop.The current approach using
process.cwd()breaks when the process starts from a different working directory (e.g., test runners, monorepo setups, Docker with non-root WORKDIR). Useimport.meta.urlwhich resolves relative to the file itself:♻️ Suggested fix
-import { createRequire } from 'module'; -import path from 'path'; -const requireCjs = createRequire(path.resolve(process.cwd(), 'package.json')); -const disastersPb = requireCjs('./proto/disaster_pb.js'); +import { createRequire } from 'module'; +const requireCjs = createRequire(import.meta.url); +const disastersPb = requireCjs('../proto/disaster_pb.js');Note: The same pattern appears in
routes/disasters.protobuf.test.tsand should also be updated.index.ts (1)
5-6: Remove debug console.log statements.These top-level log lines (
Entry point reached,Starting Disaster API entrypoint) look like development/debugging artifacts. Consider removing them or routing through the Winston logger for consistency.Dockerfile (1)
14-15: Redundantnpm install --save-dev protobufjs-cli.
protobufjs-cliis already listed indevDependencies(package.json line 74), sonpm installon line 9 already installs it. This extra install step modifies the in-containerpackage.jsonandpackage-lock.jsonunnecessarily.Remove redundant install
-# Install protobufjs CLI for code generation -RUN npm install --save-dev protobufjs-cli - # Copy proto files for code generation COPY proto/ ./proto/app.ts (3)
96-114: Apollo Server initialization reworked correctly for Express 5 integration.The
initApollofunction now:
- Accepts the Express
appto mount middleware directly- Calls
apolloServer.start()before mounting (required by Apollo Server 4+)- Mounts via
expressMiddlewarewith its ownexpress.json()scoped to/graphqlOne minor note: the
formatErrorcallback logs to bothconsole.error,logger.error, andprocess.stderr.write(lines 104-107). This triple-logging is noisy — consider keeping only the structuredlogger.errorcall.
414-439: Remove commented-out dead code.Lines 414-429, 431-439, and 462 contain large blocks of commented-out code (old error handler, old mongoSanitize middleware, old 404 registration). These are superseded by the code now inside
createApp()and should be removed to reduce clutter.Also applies to: 461-462
155-157: Variableclientshadows theprom-clientimport.Lines 156 and 336 declare
const client = await pool.connect(), which shadows the module-levelimport client from 'prom-client'(line 16) within their respective function scopes. This won't cause a bug today but is a maintenance hazard — someone adding Prometheus logic insidecreateApporupdateDisasterMetricscould accidentally reference thepgclient instead.Consider renaming the pg client variables (e.g.,
pgClientordbClient).Also applies to: 335-337
| FROM node:22-alpine AS build | ||
| WORKDIR /app | ||
| COPY package*.json ./ | ||
| RUN npm ci --only=production && npm cache clean --force |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
fd Dockerfile.productionRepository: goodthingsdev/disasters-api
Length of output: 92
🌐 Web query:
npm ci --only=production deprecated npm 10.x modern alternative --omit=dev
💡 Result:
npm ci --only=production is deprecated in npm 10+ because the only=production / production=true config flags are deprecated in favor of the newer omit/include mechanism. The modern equivalent is:
npm ci --omit=devThis omits devDependencies from what’s installed on disk (and is also what npm defaults to when NODE_ENV=production). [1] [2]
Related:
- Old
--productionflag is also deprecated → use--omit=dev. [1] - If you need to force dev deps to be installed (even with
NODE_ENV=production), use:(Works as the replacement for oldernpm ci --include=dev
--also=dev, which is deprecated.) [1] [3]
Sources
- npm CLI docs (config:
only/productiondeprecated; use--omit=dev, andonly=productionis an alias for--omit=dev) [1] - npm CLI docs (
npm ci:omitoption and default behavior) [2] - Notes on
--alsodeprecation and--include=devusage (npm 10.x) [3]
🏁 Script executed:
cat -n Dockerfile.productionRepository: goodthingsdev/disasters-api
Length of output: 732
Build stage installs only production dependencies but then runs npm run build.
npm ci --only=production skips devDependencies such as TypeScript, build tools, and type definitions. The subsequent npm run build on line 9 will fail because the compiler isn't installed. Install all dependencies in the build stage and restrict to production-only in the production stage.
Additionally, --only=production is deprecated in npm 10.x (shipped with Node 22). Update to --omit=dev.
🐛 Proposed fixes
Line 7 (build stage):
-RUN npm ci --only=production && npm cache clean --force
+RUN npm ci && npm cache clean --forceLine 18 (production stage):
-RUN npm ci --only=production && npm cache clean --force
+RUN npm ci --omit=dev && npm cache clean --force🤖 Prompt for AI Agents
In `@Dockerfile.production` at line 7, The build stage currently runs "RUN npm ci
--only=production" which omits devDependencies (like TypeScript and build tools)
so the following "npm run build" will fail; change the build stage to install
all deps (use "npm ci" without omitting dev deps) so tools required by the build
scripts are present, then in the final production stage install only runtime
deps using the new npm 10+ flag (use "npm ci --omit=dev" or equivalent) to avoid
bringing devDependencies into the production image; update any references to
"--only=production" to "--omit=dev" to be compatible with npm 10+.
| "@prisma/client": "^7.3.0", | ||
| "cors": "^2.8.5", | ||
| "dotenv": "^16.5.0", | ||
| "dotenv": "^17.2.4", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
dotenv npm package latest version 2026
💡 Result:
As of February 7, 2026, the latest published version of the dotenv npm package is 17.2.3. [1][2]
Version 17.2.4 of dotenv does not exist — update to 17.2.3 (the latest published version).
As of February 2026, the latest published version of dotenv is 17.2.3. The ^17.2.4 specified here will fail to install. Change to ^17.2.3 or allow the caret to resolve to the latest patch within 17.2.
🤖 Prompt for AI Agents
In `@package.json` at line 35, The package.json dependency entry for "dotenv" uses
a non-existent version "^17.2.4"; update the version specifier for the "dotenv"
dependency to a valid published release (e.g., "^17.2.3") so npm/yarn can
install it; locate the "dotenv" line in package.json and change the version
string accordingly and run install to verify.
| "helmet": "^8.1.0", | ||
| "hpp": "^0.2.3", | ||
| "joi": "^17.13.3", | ||
| "joi": "^18.0.2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
joi npm package version 18.0.2 changelog
💡 Result:
For joi@18.0.2 there doesn’t appear to be a substantive changelog entry—the v18.0.2 tag (Nov 19, 2025) points to a commit whose only change is bumping package.json from 18.0.1 to 18.0.2. [1][2]
Also, the official joi.dev changelog currently lists 18.0.1 and 18.0.0 but does not include an 18.0.2 section. [3]
Sources: GitHub tags / commit diff for v18.0.2, and joi.dev changelog. [1][2][3]
🌐 Web query:
joi v18.0.0 breaking changes changelog
💡 Result:
Joi v18.0.0 — breaking changes / migration notes (changelog)
- Dropped Node.js < 20 support (v18 targets Node >= 20). [1][2]
- Dependency / module upgrades (breaking-change release): updated the internal
@hapi/*modules (e.g.,addressv4→v5 and moved to@hapinamespace;hoekv10→v11;topov5→v6;tldsadded, etc.). [1] string().ip()cidroption validation change: invalidcidrvalues now error withoptions.cidr must be one of required, optional, forbidden(instead ofoptions.cidr must be a string). [1]- TypeScript typing change for arrays: if you used
Joi.array<T>().items(...), you’ll likely need to switch toJoi.array().items<T>(...). [1]
For context, the v18.0.0 changelog entries also include: isAsync() helper, UUID wrapper option, and Standard Schema support. [3][4]
Sources:
[1] v18.0.0 release notes issue (migration checklist + breaking changes) (github.com)
[2] v18.0.0 package.json (Node engines >= 20) (app.unpkg.com)
[3] joi.dev changelog (18.0.0 entries) (joi.dev)
[4] PR for Standard Schema support (#3080) (github.com)
Citations:
- 1: 18.0.0 Release Notes hapijs/joi#2926
- 2: https://app.unpkg.com/joi%4018.0.0/files/package.json?utm_source=openai
- 3: https://joi.dev/resources/changelog
- 4: feat: implement standard schema spec hapijs/joi#3080
🏁 Script executed:
find . -name "app.ts" -o -name "app.js" | head -20Repository: goodthingsdev/disasters-api
Length of output: 79
🏁 Script executed:
cat -n ./app.ts | head -100Repository: goodthingsdev/disasters-api
Length of output: 4462
🏁 Script executed:
rg "\.uri\(" ./app.tsRepository: goodthingsdev/disasters-api
Length of output: 117
Review joi v18 breaking changes—not specifically .uri() but Node.js and TypeScript requirements.
joi 18.0.2 exists (released Nov 2025). However, the changelog shows no breaking changes to .uri() validation. The actual breaking changes in v18 are:
- Node.js >= 20 required (dropped < 20 support)
- TypeScript array typing:
Joi.array<T>().items(...)→Joi.array().items<T>(...) - Dependency upgrades:
@hapimodules updated (e.g.,addressv4→v5,hoekv10→v11)
Verify that the project's Node.js version meets the >= 20 requirement and update any TypeScript array patterns if used.
🤖 Prompt for AI Agents
In `@package.json` at line 43, The project added "joi" v18 in package.json which
requires Node.js >=20 and changes TypeScript generics for arrays; update
package.json (the "joi" dependency) to ensure the Node engine requirement is
declared (add or bump the "engines" field to ">=20" and/or update CI/Dockerfiles
to use Node 20+), and audit TypeScript usages of Joi array generics—replace
occurrences of Joi.array<T>().items(...) with the new pattern
Joi.array().items<T>(...) and update any related imports/usages referencing Joi
array typing to match v18 typings.
| const adapter = new PrismaPg({ | ||
| connectionString: process.env.POSTGRES_URI, | ||
| }); | ||
|
|
||
| export const prisma = new PrismaClient({ adapter }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check where services/prisma.ts is imported
rg -n "from.*services/prisma|from.*\./prisma|from.*services" --type=ts | grep -i prisma | head -20Repository: goodthingsdev/disasters-api
Length of output: 136
🏁 Script executed:
# Find and examine app.ts to see the guard at lines 38-40
fd -e ts -e js | xargs grep -l "app\|main" | grep -E "(app|main|index)" | head -5Repository: goodthingsdev/disasters-api
Length of output: 157
🏁 Script executed:
# Get more context on imports in the project
git ls-files | grep -E "(app|main|index|prisma)" | grep -E "\.(ts|js)$"Repository: goodthingsdev/disasters-api
Length of output: 216
🏁 Script executed:
# Read app.ts, especially around lines 38-40 to see the guard
cat -n app.ts | head -50Repository: goodthingsdev/disasters-api
Length of output: 2326
🏁 Script executed:
# Read index.ts to see the entry point and import order
cat -n index.tsRepository: goodthingsdev/disasters-api
Length of output: 1803
🏁 Script executed:
# Read services/disaster.service.ts to see how prisma is used
head -20 services/disaster.service.tsRepository: goodthingsdev/disasters-api
Length of output: 903
🏁 Script executed:
# Search for imports of disaster.service
rg -n "disaster.service|from.*services" --type=ts | grep -v node_modulesRepository: goodthingsdev/disasters-api
Length of output: 380
🏁 Script executed:
# Read routes/disasters.js to see if it imports from services
cat routes/disasters.js | head -30Repository: goodthingsdev/disasters-api
Length of output: 122
🏁 Script executed:
# Read graphql/resolvers.js to see if it imports from services
cat graphql/resolvers.js | head -30Repository: goodthingsdev/disasters-api
Length of output: 123
🏁 Script executed:
# Read routes/disasters.ts to see the import chain
head -25 routes/disasters.tsRepository: goodthingsdev/disasters-api
Length of output: 898
🏁 Script executed:
# Read graphql/resolvers.ts to see the import chain
head -25 graphql/resolvers.tsRepository: goodthingsdev/disasters-api
Length of output: 733
🏁 Script executed:
# Check where routes/disasters.ts is imported in app.ts
rg -n "routes/disasters|from.*routes" app.tsRepository: goodthingsdev/disasters-api
Length of output: 121
🏁 Script executed:
# Verify the exact import order by checking where app.ts module code runs
cat -n app.ts | sed -n '1,45p'Repository: goodthingsdev/disasters-api
Length of output: 2087
🏁 Script executed:
# Double-check services/prisma.ts to confirm it executes at module scope
cat -n services/prisma.tsRepository: goodthingsdev/disasters-api
Length of output: 356
process.env.POSTGRES_URI will be undefined when the adapter is initialized.
This module instantiates PrismaPg and PrismaClient at module scope (lines 4-8). Due to ES module import hoisting, this code executes during the import phase—before the POSTGRES_URI guard in app.ts (lines 38-40) runs. The import chain is: index.ts → app.ts → routes/disasters.ts → disaster.service.ts → prisma.ts. The guard executes only after all imports complete, so the adapter receives undefined as connectionString.
Either lazily initialize the client or add an explicit guard here:
Lazy singleton pattern
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
-const adapter = new PrismaPg({
- connectionString: process.env.POSTGRES_URI,
-});
-
-export const prisma = new PrismaClient({ adapter });
+let _prisma: PrismaClient | undefined;
+
+export function getPrisma(): PrismaClient {
+ if (!_prisma) {
+ if (!process.env.POSTGRES_URI) {
+ throw new Error('POSTGRES_URI must be set before initializing Prisma');
+ }
+ const adapter = new PrismaPg({
+ connectionString: process.env.POSTGRES_URI,
+ });
+ _prisma = new PrismaClient({ adapter });
+ }
+ return _prisma;
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const adapter = new PrismaPg({ | |
| connectionString: process.env.POSTGRES_URI, | |
| }); | |
| export const prisma = new PrismaClient({ adapter }); | |
| import { PrismaClient } from '@prisma/client'; | |
| import { PrismaPg } from '@prisma/adapter-pg'; | |
| let _prisma: PrismaClient | undefined; | |
| export function getPrisma(): PrismaClient { | |
| if (!_prisma) { | |
| if (!process.env.POSTGRES_URI) { | |
| throw new Error('POSTGRES_URI must be set before initializing Prisma'); | |
| } | |
| const adapter = new PrismaPg({ | |
| connectionString: process.env.POSTGRES_URI, | |
| }); | |
| _prisma = new PrismaClient({ adapter }); | |
| } | |
| return _prisma; | |
| } |
🤖 Prompt for AI Agents
In `@services/prisma.ts` around lines 4 - 8, The PrismaPg adapter is being
constructed at module scope using process.env.POSTGRES_URI which may be
undefined due to import ordering; change prisma.ts to lazily initialize the
adapter and client (implement a singleton getter like getPrisma() that
constructs new PrismaPg and new PrismaClient only on first call) or add an
explicit guard at the top of prisma.ts that throws or reads
process.env.POSTGRES_URI before creating PrismaPg; reference the existing
symbols PrismaPg, PrismaClient and the exported prisma instance (replace direct
export with a getter or factory) so callers use getPrisma()/prisma() after
POSTGRES_URI is validated in app.ts.
Summary by CodeRabbit
New Features
Improvements
Documentation