Next.js PostgreSQL : How to Connect PostgreSQL to Next.js: Step-by-Step Tutorial — SEO INSIGHTS
How to connect PostgreSQL database to Next.js Website
Learn how to connect PostgreSQL to Next.js step by step. Set up DATABASE_URL, install pg, create a connection pool, query data, and deploy securely.
Highlights
- Connect a PostgreSQL database to a Next.js application using the pg package.
- Store database credentials safely with DATABASE_URL.
- Create a reusable PostgreSQL connection pool.
- Query PostgreSQL from a Next.js Server Component.
- Understand why database code should remain on the server.
- Prepare your application for production deployment and connection limits.
How to Connect PostgreSQL to a Next.js Website
Imagine that you have just created a Next.js website.
The pages work. The navigation looks good. Your application is running locally, and you are ready to build something more useful.
Then you reach the point where your application needs real data.
Perhaps you want to store users, blog posts, products, orders, comments or analytics records.
This is where PostgreSQL enters the picture.
PostgreSQL is a relational database that can store structured application data, while Next.js provides the application layer that can retrieve and display that information.
The good news is that connecting the two does not require a complicated architecture.
In this guide, we will build the connection step by step using the PostgreSQL pg driver. We will create a database connection string, store it in an environment variable, create a reusable connection pool and retrieve data from PostgreSQL inside a Next.js application.
The examples use the Next.js App Router, which is the current approach documented in Next.js learning materials. ( Next.js)
What You Need Before Connecting PostgreSQL to Next.js
Before starting, make sure you have:
- Node.js installed
- A Next.js project
- A PostgreSQL database
- Basic JavaScript or TypeScript knowledge
- A terminal or command prompt
- A code editor
Your PostgreSQL database can be hosted locally or by a cloud provider.
You will also need the PostgreSQL connection details supplied by your database provider, usually including:
A PostgreSQL connection string commonly looks like this:
postgresql://username:password@hostname:5432/database_name
The actual value will be different for your database.
Never publish your real database password in your source code.
Step 1: Create or Open Your Next.js Project
If you are starting a new application, create a Next.js project:
npx create-next-app@latest my-nextjs-app
Move into the project:
cd my-nextjs-app
Then start the development server:
npm run dev
Open:
If the default Next.js page appears, your application is ready.
If you already have a Next.js project, you can simply use your existing application.
Step 2: Install the PostgreSQL Package
For a direct PostgreSQL connection, install the pg package:
npm install pg
If your project uses TypeScript, also install its type definitions:
npm install -D @types/pg
The node-postgres project, commonly used through the pg package, supports connection strings and PostgreSQL connection pooling. ( Node Postgres)
At this point, your Next.js application has the library it needs to communicate with PostgreSQL.
Step 3: Add Your PostgreSQL Connection String
Now comes an important part.
You need to tell Next.js where your PostgreSQL database is located.
Create a file named:
.env.local
in the root of your project.
Add:
DATABASE_URL=”postgresql://username:password@hostname:5432/database_name”
Replace the example values with your actual PostgreSQL connection string.
For example:
DATABASE_URL=”postgresql://myuser:mypassword@localhost:5432/mydatabase”
Why use an environment variable?
You should not write this directly into your application:
const password = “mypassword”;
That can expose sensitive credentials in source control.
Instead, your application reads:
process.env.DATABASE_URL
Next.js documentation similarly emphasizes keeping database credentials in environment files and ensuring environment files containing secrets are excluded from Git. ( Next.js)
Check your .gitignore
Make sure your environment file isn’t accidentally committed.
For example:
.env*
or an appropriate project-specific rule should be present in .gitignore.
Step 4: Create a PostgreSQL Connection Pool
Now we can create the database connection.
A clean approach is to create a dedicated database utility.
For example:
src/
lib/
db.ts
Create:
src/lib/db.ts
Add:
import { Pool } from “pg”;
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
That’s enough to create a reusable PostgreSQL pool.
Why use a pool?
Imagine your website receives many requests.
If every request creates a completely new database connection, your application can consume connections unnecessarily.
A pool maintains a controlled collection of database connections that can be reused.
node-postgres includes built-in pooling through pg-pool and recommends generally keeping the number of pools limited-usually one pool for an application process rather than creating an unbounded number of pools. ( Node Postgres)
For a simple Next.js application, keeping your pool in one database module is a straightforward pattern.
Step 5: Create a PostgreSQL Table
Before asking Next.js to retrieve information, you need some data.
For this example, let’s create a simple posts table.
Run this SQL in your PostgreSQL client:
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Now insert a couple of records:
INSERT INTO posts (title, content)
VALUES
(‘Learning Next.js’, ‘Next.js makes full-stack web development easier.’),
(‘Learning PostgreSQL’, ‘PostgreSQL is a powerful relational database.’);
You now have something for Next.js to retrieve.
Step 6: Query PostgreSQL From Next.js
Now let’s connect the two pieces.
Suppose your App Router contains:
src/
app/
page.tsx
Open page.tsx.
You can write:
import { pool } from “@/lib/db”;
export default async function Home() {
const result = await pool.query(
“SELECT id, title, content FROM posts ORDER BY id DESC”
);
return (
<main>
<h1>Latest Posts</h1>
{result.rows.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</main>
);
}
Now your Next.js page is reading directly from PostgreSQL.
This is an important concept with the App Router: database access can happen on the server, rather than exposing database credentials or database operations to the browser.
Step 7: Understand What Just Happened
The flow is actually quite simple:
Visitor
↓
Next.js Server
↓
PostgreSQL Connection Pool
↓
PostgreSQL Database
↓
Query Result
↓
Next.js Page
↓
Visitor
When the page is rendered on the server, Next.js executes:
SELECT id, title, content
FROM posts
ORDER BY id DESC
PostgreSQL returns the rows.
Next.js then uses those rows to generate the page.
This separation is important because the browser does not need to know your PostgreSQL password.
Step 8: Use Parameterized Queries
Suppose you want to find a particular post.
A beginner might be tempted to build SQL like this:
const id = 10;
await pool.query(
`SELECT * FROM posts WHERE id = ${id}`
);
A safer pattern is to use a parameterized query:
const id = 10;
const result = await pool.query(
“SELECT * FROM posts WHERE id = $1”,
[id]
);
Here $1 represents the supplied parameter.
This becomes particularly important when values come from users, URLs, forms or other external input.
For example:
const result = await pool.query(
“SELECT * FROM posts WHERE id = $1”,
[postId]
);
Avoid constructing SQL statements by directly concatenating untrusted user input.
Step 9: Create a Dynamic Post Page
Now let’s make the example more useful.
Imagine you want URLs like:
/posts/1
/posts/2
/posts/3
You could create:
src/app/posts/[id]/page.tsx
Then:
import { pool } from “@/lib/db”;
type PageProps = {
params: Promise<{ id: string }>;
};
export default async function PostPage({ params }: PageProps) {
const { id } = await params;
const result = await pool.query(
“SELECT id, title, content FROM posts WHERE id = $1”,
[id]
);
const post = result.rows[0];
if (!post) {
return <h1>Post not found</h1>;
}
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
);
}
Now each post can be retrieved from PostgreSQL using its ID.
Step 10: Handle Database Errors
Real applications need error handling.
A database can become temporarily unavailable. A connection string can be incorrect. A table may not exist yet.
Instead of allowing an unexpected error to produce a poor user experience, handle errors appropriately.
For example:
Get SEO Insights’s stories in your inbox
Join Medium for free to get updates from this writer.
import { pool } from “@/lib/db”;
export default async function Home() {
try {
const result = await pool.query(
“SELECT id, title, content FROM posts ORDER BY id DESC”
);
return (
<main>
<h1>Latest Posts</h1>
{result.rows.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</main>
);
} catch (error) {
console.error(“Database error:”, error);
return <p>Unable to load posts.</p>;
}
}
In production, you should also consider how much error information is shown to visitors.
Detailed database errors are useful for server logs but should generally not be displayed publicly.
Step 11: Keep Database Code on the Server
One of the most important rules for beginners is this:
Do not send your PostgreSQL credentials to the browser.
Database operations should be performed in server-side code.
With Next.js App Router, this can include:
- Server Components
- Route Handlers
- Server Actions
- Other server-side application code
For example, your database module:
import { Pool } from “pg”;
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
should not be imported into a browser Client Component.
A Client Component is designed to execute in the browser.
Your PostgreSQL connection belongs on the server.
Step 12: Using PostgreSQL Through a Route Handler
Sometimes you don’t want a page to query PostgreSQL directly.
You may instead want an application endpoint.
For example:
src/app/api/posts/route.ts
You could create:
import { pool } from “@/lib/db”;
export async function GET() {
const result = await pool.query(
“SELECT id, title, content FROM posts ORDER BY id DESC”
);
return Response.json(result.rows);
}
Your application now has an endpoint:
/api/posts
A request to that endpoint can retrieve the PostgreSQL data.
This approach can be useful when database operations need to sit behind an application API boundary.
However, don’t automatically create an API endpoint for every database query. If a Server Component can safely retrieve the data directly on the server, an additional API layer may not be necessary.
PostgreSQL With Prisma: Do You Need an ORM?
You may have noticed another popular approach when searching for Next.js PostgreSQL:
Prisma.
Prisma is an ORM that provides a higher-level way to work with databases.
Instead of writing SQL for every operation, you can define models and use Prisma’s generated APIs.
Current Prisma documentation provides dedicated Next.js guides covering PostgreSQL, migrations, querying and deployment. Prisma’s current documentation also has separate material for its newer ORM versions, so commands should be checked against the version you install rather than copied from older tutorials. ( Prisma)
A simplified Prisma-based application might conceptually look like:
Next.js
↓
Prisma ORM
↓
PostgreSQL
Whereas our direct approach is:
Next.js
↓
pg
↓
PostgreSQL
When should a beginner consider Prisma?
Prisma can be useful when your project has:
- Multiple related tables
- A growing database schema
- Frequent schema changes
- Complex application models
- A preference for typed database access
- A need for ORM features
For a small application where you want to understand SQL and PostgreSQL directly, the pg approach can be easier to understand initially.
The choice is architectural rather than an SEO requirement. Both approaches can be used with Next.js.
Step 13: Consider Connection Pooling for Production
A local application might work perfectly with a simple connection pool and a handful of requests.
Production is different.
Serverless and horizontally scaled applications can create multiple application instances. Each instance may open database connections.
If your database has a connection limit, uncontrolled connections can eventually become a problem.
This is why connection pooling and, depending on your hosting/database architecture, a managed pooling solution can matter.
Vercel’s current Next.js database tutorial discusses connecting a Postgres provider through its marketplace integrations, while its Prisma documentation notes that Prisma Postgres provides connection pooling for its managed setup. ( Next.js)
The exact production configuration depends on your PostgreSQL provider and hosting platform.
Step 14: Deploy Your Next.js PostgreSQL Application
Once your application works locally, you can deploy it.
The important point is that your production environment needs the same database connection information.
For example:
DATABASE_URL=your-production-postgresql-connection-string
Do not put your production database password directly into source files.
Instead, configure DATABASE_URL as a server-side environment variable in your hosting platform.
If you use Vercel, its current Next.js learning material demonstrates connecting a Postgres database through the project’s storage/database integrations and copying environment variables into the application environment. ( Next.js)
After deployment, verify:
- The application builds successfully.
- DATABASE_URL exists in the production environment.
- The application can reach PostgreSQL.
- Required database tables exist.
- Queries return expected data.
- Database errors are logged appropriately.
Common PostgreSQL and Next.js Connection Errors
When something goes wrong, the error message usually gives you a clue.
DATABASE_URL is undefined
Check:
.env.local
and make sure you have:
DATABASE_URL=”…”
Then restart your development server.
Environment variables loaded by the development process may not become available to an already-running process after you change the file.
password authentication failed
This usually means that the PostgreSQL username or password doesn’t match the database credentials.
Check the connection string carefully.
database does not exist
Check the database name in:
DATABASE_URL
For example:
postgresql://user:password@host:5432/mydatabase
The final portion identifies the database.
relation “posts” does not exist
PostgreSQL cannot find the table you’re querying.
Check whether you created:
CREATE TABLE posts (…);
in the correct database.
Too many database connections
If the application works locally but experiences connection problems after deployment, investigate connection pooling and the connection limits of your PostgreSQL provider.
Do not solve this simply by creating more and more pools. node-postgres specifically warns that creating an unbounded number of pools defeats the purpose of pooling. ( Node Postgres)
A Simple Project Structure
By now, your application might look something like this:
my-nextjs-app/
├── src/
│ ├── app/
│ │ ├── api/
│ │ │ └── posts/
│ │ │ └── route.ts
│ │ ├── posts/
│ │ │ └── [id]/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ │
│ └── lib/
│ └── db.ts
│
├── .env.local
├── .gitignore
├── package.json
└── next.config.ts
The important relationship is:
page.tsx
↓
lib/db.ts
↓
PostgreSQL
This keeps the database connection logic separate from your page presentation.
Practical Tips for Beginners to connect PostgreSQL to Next.js
Don’t begin with ten tables and complicated relationships.
Create something simple such as:
posts
Get your first SELECT query working.
Then expand.
Treat your PostgreSQL connection string like a password.
Never publish:
DATABASE_URL
with its real credentials in GitHub.
Prefer:
pool.query(
“SELECT * FROM posts WHERE id = $1”,
[id]
);
over building SQL with string concatenation.
Don’t expose your database connection to Client Components.
Make sure the application can:
before moving to production.
A database connection strategy that works for one local process may need additional consideration when the application scales.
Key Takeaways
- Next.js PostgreSQL applications can connect directly to PostgreSQL using the pg package.
- Store the PostgreSQL connection string in DATABASE_URL rather than hard-coding credentials.
- A reusable connection pool is preferable to repeatedly creating uncontrolled database connections.
- Use parameterized SQL queries when working with dynamic values.
- Keep PostgreSQL access in server-side Next.js code.
- Prisma is another option when you want an ORM and additional database-development features.
- Production deployments require careful handling of environment variables, database connectivity and connection limits.
Frequently Asked Questions
1. Can I use PostgreSQL with Next.js?
Yes. Next.js applications can work with PostgreSQL through database drivers such as pg, or through database tools and ORMs such as Prisma. The current Next.js learning materials include PostgreSQL-backed application examples. ( Next.js)
2. How do I connect PostgreSQL to Next.js?
Install the pg package, place your PostgreSQL connection string in DATABASE_URL, create a PostgreSQL Pool, and execute queries from server-side Next.js code.
A basic connection looks like:
import { Pool } from “pg”;
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
3. Where should I store my PostgreSQL password in Next.js?
Store it in a server-side environment variable such as:
DATABASE_URL=”postgresql://…”
Do not hard-code the password into your source code or expose it through client-side JavaScript.
4. Should I use pg or Prisma with Next.js?
It depends on your project.
pg gives you direct access to PostgreSQL and SQL. Prisma provides an ORM-based approach and can be useful for applications with larger schemas and more complex data models.
If you’re learning PostgreSQL itself, starting with pg can help you understand what is happening between Next.js and the database.
5. Can I deploy a Next.js PostgreSQL application to Vercel?
Yes. Next.js applications can be deployed to Vercel and connected to PostgreSQL providers. Vercel’s current documentation demonstrates connecting Postgres databases through its integrations and configuring the required environment variables. ( Next.js)
Conclusion
Connecting PostgreSQL to Next.js becomes much less intimidating when you break it into small steps.
First, create your PostgreSQL database. Then obtain its connection string and store it securely in DATABASE_URL. Install pg, create a reusable connection pool and perform your first query from server-side Next.js code.
From there, you can build more advanced features-user accounts, dashboards, blog posts, products, orders and other data-driven functionality.
The most important lesson is not simply getting the first query to work. It is understanding the boundary between your Next.js application and PostgreSQL database.
Keep database credentials private, use parameterized queries, reuse connections sensibly and choose an ORM such as Prisma when its features make sense for your application’s complexity.
Once that foundation is in place, PostgreSQL can become the reliable data layer behind a powerful Next.js website.
Originally published at https://seoinsights.co.in on September 27, 2026.








