<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tech with Jay]]></title><description><![CDATA[Tech with Jay]]></description><link>https://learn.unseenjs.xyz</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1768581390610/9485b7d6-c2af-44f2-b0ca-aa16cbe1d4db.png</url><title>Tech with Jay</title><link>https://learn.unseenjs.xyz</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 13:07:24 GMT</lastBuildDate><atom:link href="https://learn.unseenjs.xyz/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Express.js + Prisma v7 Setup (TypeScript + pnpm)]]></title><description><![CDATA[Express.js + Prisma v7 Setup (TypeScript + pnpm)
Preface
Prisma has introduced a major update with Prisma v7, bringing notable structural changes to how Prisma Client is generated and managed.
Key changes include:

A new prisma.config.ts file

A sepa...]]></description><link>https://learn.unseenjs.xyz/expressjs-prisma-v7-setup-typescript-pnpm</link><guid isPermaLink="true">https://learn.unseenjs.xyz/expressjs-prisma-v7-setup-typescript-pnpm</guid><category><![CDATA[prisma]]></category><category><![CDATA[Express]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[APIs]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[pnpm]]></category><category><![CDATA[npm]]></category><category><![CDATA[full stack]]></category><category><![CDATA[software development]]></category><category><![CDATA[orm]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Jay Shende]]></dc:creator><pubDate>Fri, 16 Jan 2026 16:50:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768580566823/dee0c8cd-510c-4484-9e7c-68029c3399ba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-expressjs-prisma-v7-setup-typescript-pnpm">Express.js + Prisma v7 Setup (TypeScript + pnpm)</h2>
<h2 id="heading-preface">Preface</h2>
<p>Prisma has introduced a major update with <strong>Prisma v7</strong>, bringing notable structural changes to how Prisma Client is generated and managed.</p>
<p>Key changes include:</p>
<ul>
<li><p>A new <code>prisma.config.ts</code> file</p>
</li>
<li><p>A <strong>separate output directory</strong> for generated clients</p>
</li>
<li><p>The <strong>Prisma Client and generated types are no longer stored inside</strong> <code>node_modules</code></p>
</li>
</ul>
<p>Instead, when running <code>prisma generate</code>, Prisma now <strong>requires</strong> an explicit <code>output</code> path to be defined in your project’s <code>schema.prisma</code>. For example:</p>
<pre><code class="lang-plaintext">output = "../src/generated/prisma"
</code></pre>
<p>This guide walks through a <strong>clean and modern setup</strong> for building an <strong>Express.js + Prisma v7</strong> application using <strong>TypeScript</strong> and the <strong>pnpm</strong> package manager.</p>
<hr />
<h2 id="heading-project-setup">Project Setup</h2>
<h3 id="heading-initialize-the-nodejs-project">Initialize the Node.js project</h3>
<pre><code class="lang-bash">pnpm init
</code></pre>
<p>This command creates a <code>package.json</code> file and initializes your Node.js project.</p>
<p>Ensure that <strong>pnpm</strong> is installed globally before continuing.</p>
<hr />
<h3 id="heading-initialize-typescript">Initialize TypeScript</h3>
<pre><code class="lang-bash">tsc --init
</code></pre>
<p>This creates a <code>tsconfig.json</code> file with default compiler settings, which we will customize later.</p>
<hr />
<h2 id="heading-update-packagejson">Update <code>package.json</code></h2>
<p>Add the following scripts:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc -b"</span>,
  <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node dist/src/index.js"</span>,
  <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"pnpm run build &amp;&amp; pnpm run start"</span>
}
</code></pre>
<p>Also add:</p>
<pre><code class="lang-json"><span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>
</code></pre>
<h3 id="heading-why-type-module-is-required">Why <code>"type": "module"</code> is required</h3>
<p>By default, Node.js treats <code>.js</code> files as <strong>CommonJS</strong> modules (<code>require()</code> syntax).</p>
<p>However, modern Prisma versions and recent tooling use <strong>ES Modules (ESM)</strong> with <code>import/export</code>.</p>
<p>Adding <code>"type": "module"</code> tells Node.js:</p>
<blockquote>
<p>“Treat all .js files in this project as ES Modules.”</p>
</blockquote>
<p>Without this, Node.js will attempt to execute your compiled files as CommonJS and fail when encountering ESM syntax or Prisma’s generated ESM client.</p>
<hr />
<h2 id="heading-configure-tsconfigjson">Configure <code>tsconfig.json</code></h2>
<p>Replace the contents of <code>tsconfig.json</code> with the following:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES2022"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>
  },
  <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"src/**/*"</span>]
}
</code></pre>
<h3 id="heading-understanding-the-key-compiler-options">Understanding the Key Compiler Options</h3>
<p>Think of <code>tsconfig.json</code> as instructions for the TypeScript compiler:</p>
<p><em>What language to output, how strict to be, and where files should go.</em></p>
<hr />
<h3 id="heading-1-runtime-target-target">1. Runtime Target (<code>target</code>)</h3>
<ul>
<li><p><strong>Setting:</strong> <code>ES2022</code></p>
</li>
<li><p><strong>Purpose:</strong> Defines the JavaScript version emitted by TypeScript</p>
</li>
<li><p><strong>Why it matters:</strong></p>
<p>  Modern Node.js versions support ES2022 natively. This avoids unnecessary polyfills and keeps the compiled output clean and performant.</p>
</li>
</ul>
<hr />
<h3 id="heading-2-module-system-module-amp-moduleresolution">2. Module System (<code>module</code> &amp; <code>moduleResolution</code>)</h3>
<ul>
<li><p><strong>Setting:</strong> <code>NodeNext</code></p>
</li>
<li><p><strong>Purpose:</strong> Enables native ES Module behavior</p>
</li>
</ul>
<p>Important note:</p>
<pre><code class="lang-tsx">import { x } from "./file.js";
</code></pre>
<p>When using <code>NodeNext</code>, <strong>file extensions are mandatory</strong> in imports. This is a common source of confusion, but it aligns with how modern Node.js resolves modules.</p>
<hr />
<h3 id="heading-3-type-safety-amp-build-stability">3. Type Safety &amp; Build Stability</h3>
<ul>
<li><p><code>strict: true</code></p>
<p>  Enables comprehensive type safety checks—essential for production-grade code.</p>
</li>
<li><p><code>skipLibCheck: true</code></p>
<p>  Prevents build failures caused by type errors in third-party libraries.</p>
</li>
</ul>
<hr />
<h3 id="heading-4-output-structure">4. Output Structure</h3>
<ul>
<li><p><code>include</code> limits compilation to the <code>src</code> directory</p>
</li>
<li><p><code>outDir</code> ensures compiled files are emitted to <code>dist</code></p>
</li>
</ul>
<p>This keeps source and build artifacts clearly separated.</p>
<hr />
<p>At this point, the <strong>base application setup is complete</strong>.</p>
<hr />
<h2 id="heading-prisma-setup">Prisma Setup</h2>
<h3 id="heading-install-dependencies">Install Dependencies</h3>
<pre><code class="lang-bash">pnpm add prisma @types/node @types/pg --save-dev
pnpm add @prisma/client @prisma/adapter-pg pg dotenv
</code></pre>
<h3 id="heading-package-breakdown">Package Breakdown</h3>
<ul>
<li><p><code>prisma</code> – Prisma CLI for migrations and generation</p>
</li>
<li><p><code>@prisma/client</code> – Type-safe database client</p>
</li>
<li><p><code>@prisma/adapter-pg</code> – PostgreSQL driver adapter</p>
</li>
<li><p><code>pg</code> – PostgreSQL driver</p>
</li>
<li><p><code>@types/pg</code> – Type definitions for <code>pg</code></p>
</li>
<li><p><code>dotenv</code> – Loads environment variables from <code>.env</code></p>
</li>
</ul>
<hr />
<h3 id="heading-initialize-prisma">Initialize Prisma</h3>
<pre><code class="lang-bash">pnpm prisma init
</code></pre>
<p>This creates:</p>
<ul>
<li><p><code>prisma.config.ts</code></p>
</li>
<li><p><code>prisma/schema.prisma</code></p>
</li>
</ul>
<hr />
<h3 id="heading-configure-the-prisma-client-generator">Configure the Prisma Client Generator</h3>
<p>Update <code>schema.prisma</code>:</p>
<pre><code class="lang-plaintext">generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

datasource db {
  provider = "postgresql"
}
</code></pre>
<p>This ensures the Prisma Client is generated <strong>outside</strong> <code>node_modules</code>.</p>
<p>📌 Add the following to <code>.gitignore</code>:</p>
<pre><code class="lang-plaintext">/generated/prisma
dist
</code></pre>
<hr />
<h3 id="heading-configure-database-url">Configure Database URL</h3>
<p>Update the <code>DATABASE_URL</code> in your <code>.env</code> file.</p>
<pre><code class="lang-plaintext">DATABASE_URL="postgresql://username:password@localhost:5432/mydatabase"
</code></pre>
<p>You may use a local PostgreSQL instance or a hosted provider such as Neon.</p>
<hr />
<h2 id="heading-define-the-data-model">Define the Data Model</h2>
<p>Edit <code>prisma/schema.prisma</code>:</p>
<pre><code class="lang-plaintext">model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
}
</code></pre>
<hr />
<h2 id="heading-run-migrations">Run Migrations</h2>
<pre><code class="lang-bash">pnpm prisma migrate dev --name init
</code></pre>
<p>This creates database tables based on your schema.</p>
<hr />
<h3 id="heading-generate-prisma-client">Generate Prisma Client</h3>
<pre><code class="lang-bash">pnpm dlx prisma generate
</code></pre>
<p>A new <code>generated/prisma</code> directory will be created with the client and types.</p>
<hr />
<h2 id="heading-instantiate-prisma-client">Instantiate Prisma Client</h2>
<p>Create <code>src/lib/prisma.ts</code>:</p>
<pre><code class="lang-tsx">import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../generated/prisma/client.js";

const connectionString = process.env.DATABASE_URL!;
const adapter = new PrismaPg({ connectionString });

const prisma = new PrismaClient({ adapter });

export { prisma };
</code></pre>
<blockquote>
<p>💡 In Development, consider implementing a singleton Prisma Client to avoid connection exhaustion.</p>
</blockquote>
<hr />
<h2 id="heading-express-setup">Express Setup</h2>
<h3 id="heading-install-express">Install Express</h3>
<pre><code class="lang-bash">pnpm add express @types/express
</code></pre>
<hr />
<h3 id="heading-create-the-application-entry-point">Create the Application Entry Point</h3>
<p>Create <code>src/index.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> { prisma } <span class="hljs-keyword">from</span> <span class="hljs-string">"./lib/prisma.js"</span>;

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">3009</span>;

app.get(<span class="hljs-string">"/test"</span>, <span class="hljs-keyword">async</span> (_req, res) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.create({
    data: {
      name: <span class="hljs-string">"Alice"</span>,
      email: <span class="hljs-string">"alice@prisma.io"</span>,
      posts: {
        create: {
          title: <span class="hljs-string">"Hello World"</span>,
          content: <span class="hljs-string">"This is my first post!"</span>,
          published: <span class="hljs-literal">true</span>,
        },
      },
    },
    include: { posts: <span class="hljs-literal">true</span> },
  });

  <span class="hljs-keyword">const</span> allUsers = <span class="hljs-keyword">await</span> prisma.user.findMany({
    include: { posts: <span class="hljs-literal">true</span> },
  });

  res.json({ users: allUsers });
});

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>⚠️ This approach is <strong>not recommended for production</strong>, but it is sufficient for demonstration and verification purposes.</p>
<h3 id="heading-test-the-endpoint">Test The Endpoint</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768577512268/be7d07cf-86c0-4df3-8ead-c21f26dd0edb.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>By following this guide step by step, you will have a fully working <strong>Express.js + Prisma v7</strong> application using:</p>
<ul>
<li><p>TypeScript</p>
</li>
<li><p>pnpm</p>
</li>
<li><p>PostgreSQL</p>
</li>
<li><p>Prisma Client outside <code>node_modules</code></p>
</li>
<li><p>Native ES Modules</p>
</li>
</ul>
<p>This setup aligns with <strong>modern Node.js and Prisma best practices</strong> and serves as a solid foundation for scalable backend applications.<strong>d types are no longer stored inside</strong> <code>node_modules</code></p>
<hr />
<p>If this guide helped you understand how to set up <strong>Express.js with Prisma v7</strong> using <strong>TypeScript and pnpm</strong>, consider giving it a <strong>❤️ like</strong> and <strong>sharing it with other developers</strong> who might be struggling with Prisma’s new client generation changes.</p>
<p>Have questions, edge cases, or improvements in mind?<br />Drop them in the <strong>comments</strong>—I actively read and respond.</p>
<p>Follow me on <strong>Hashnode</strong> for more <strong>deep-dive backend guides, Prisma internals, and modern Node.js workflows</strong> 🚀</p>
]]></content:encoded></item></channel></rss>