Skip to content

--template nest scaffolds NestJS 11 (CJS-style imports, no @nestjs/cli, tsdown build) and never closes the Prisma 8 pool on shutdown; should target NestJS 12 ESM #108

Description

@ankur-arch

Summary

--template nest still scaffolds NestJS 11, does not install the Nest CLI, and never closes the Prisma 8 connection pool on shutdown. NestJS 12 (ESM-only) has been the current major for months and is what @nestjs/cli new produces today, so the template is the one place where "official Prisma + NestJS" and "official NestJS" disagree.

The docs page "How to use Prisma ORM with NestJS" (docs.prisma.io/docs/guides/frameworks/nestjs) sends every NestJS user to this template, so it is effectively the Prisma 8 NestJS guide.

Generated today with create-prisma@0.13.1:

npm create prisma@latest -- nest-ref --template nest --provider postgres --yes --skills none

What the template produces

Aspect Template today Current NestJS (npx @nestjs/cli@12 new)
@nestjs/core / common / platform-express ^11.1.17 (resolves to 11.2.5) 12.0.3
@nestjs/cli, @nestjs/schematics, @nestjs/testing not installed installed; nest build, nest g resource, nest start --watch all work
Module system "type": "module" but every relative import is extensionless (from "./app.module"), i.e. CommonJS spelling ESM with .js extensions (from './app.module.js'), module: nodenext
Build tsdown bundling to dist/server.mjs, with a custom externals list for optional Nest peer deps nest build (tsc), dist/main.js
Dev loop tsx watch src/main.ts nest start --watch
Tests none vitest unit + e2e scaffolded
TypeScript ^5.9.3 ^6.0.2
Pool teardown none (db.close() never called, no enableShutdownHooks) n/a
Extra deps @prisma/composer, @prisma/composer-prisma-cloud, alchemy (Cloudflare runtime), @prisma/dev none

Notes on the rows that matter most:

  1. NestJS 11 vs 12. The generated package.json pins ^11.1.17, so a user who scaffolds today and later runs nest g from a globally installed Nest 12 CLI gets schematics that emit .js-suffixed ESM imports into a project whose existing files are extensionless. npx --no-install nest build fails in the generated project (could not determine executable to run) because @nestjs/cli is not a dependency.

  2. Extensionless imports only work because of tsdown. With "type": "module" in package.json, import { AppModule } from "./app.module" is not valid for Node; it works here only because tsdown resolves and bundles it. The moment someone swaps the build for tsc/nest build (the standard Nest workflow), every import breaks. NestJS 12 avoids this by spelling .js everywhere; the template should too.

  3. No db.close() on shutdown. src/prisma/db.ts exports a long-lived postgres<Contract>(...) client and PrismaService just wraps it (readonly db = db). Nothing implements OnApplicationShutdown, and main.ts never calls app.enableShutdownHooks(), so a SIGTERM under a process manager or in a container leaves the pg.Pool to be killed rather than drained. The Prisma 8 runtime reference (references/runtime.md) is explicit that servers must close exactly once, on shutdown. This is the one NestJS-specific thing a template has to get right, since Prisma 7 users are used to enableShutdownHooks / $disconnect and will look for the equivalent.

  4. Composer/Alchemy weight. The nest template pulls in @prisma/composer, @prisma/composer-prisma-cloud and alchemy@2.0.0-beta.74 (which brings the Cloudflare runtime) into a plain Node/Express server. That is fine as an opt-in, but it makes the scaffold a poor starting point for the majority of Nest users who deploy to their own infrastructure, and it is why install takes minutes on a cold cache. Related: Scaffolds pin the ORM exactly but install prisma@latest, so every ORM bump opens a mismatch window #101 (ORM pin vs prisma@latest mismatch window) shows how sensitive the template's dependency block is; fewer moving parts would help there too.

What I expected

A NestJS template that is a superset of npx @nestjs/cli@latest new, so the Nest CLI, schematics, and build all keep working:

  • @nestjs/* at ^12, plus @nestjs/cli, @nestjs/schematics, @nestjs/testing in devDependencies.

  • tsconfig.json with module: "nodenext" / moduleResolution: "nodenext" (Nest 12 default) and relative imports spelled with .js. This currently requires db.ts to import ./contract.js rather than ./contract.d; that is tracked in prisma orm init silently rewrites an existing tsconfig.json (nodenextpreserve/bundler); generated db.ts imports ./contract.d, which does not resolve under nodenext orm#30358, and the template can just use the working spelling.

  • "build": "nest build", "prebuild": "prisma contract emit", "start:dev": "nest start --watch", "start:prod": "node dist/main". Keep tsdown only if a single-file bundle is needed for Composer deploys, as a separate build:composer script.

  • A PrismaModule that owns the lifecycle. Either of these is fine; I verified the first one end to end (build, unit + e2e tests, CRUD routes, db.transaction, clean exit on SIGTERM) on Nest 12.0.3 + @prisma/orm-postgres@8.0.0-rc.11:

    // src/prisma/prisma.module.ts
    import { Global, Module, type OnApplicationShutdown } from '@nestjs/common';
    import { db } from './db.js';
    
    export const DB = Symbol('PRISMA_DB');
    export type Db = typeof db;
    
    @Global()
    @Module({ providers: [{ provide: DB, useValue: db }], exports: [DB] })
    export class PrismaModule implements OnApplicationShutdown {
      async onApplicationShutdown() {
        await db.close();
      }
    }
    // src/main.ts
    const app = await NestFactory.create(AppModule);
    app.enableShutdownHooks();
    await app.listen(process.env.PORT ?? 3000);

    or keep the current PrismaService { readonly db = db } class and add implements OnApplicationShutdown { onApplicationShutdown() { return db.close(); } } plus enableShutdownHooks().

  • Composer/Alchemy deps behind a flag (--deploy already exists) rather than in the base template.

Reproduction

npm create prisma@latest -- nest-ref --template nest --provider postgres --yes --skills none
cd nest-ref
npm ls @nestjs/core            # 11.2.5
npm view @nestjs/core version  # 12.0.3
npx --no-install nest build    # npm error could not determine executable to run
grep -n 'from "./' src/*.ts    # extensionless imports in a "type": "module" package
grep -rn 'close\|enableShutdownHooks\|OnApplicationShutdown' src/ || echo "no teardown"

Environment: create-prisma@0.13.1, Node 24.11.1, npm 11.6.2, macOS 15.6. Generated package.json resolves to @nestjs/core@11.2.5, @prisma/orm-postgres@8.0.0-rc.11, prisma@8.0.0-rc.15, typescript@5.9.3, tsdown@0.22.14.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions