With Next.js 13 introducing the new /app directory structure, you can now perform server-side operations more seamlessly. In this blog post, I will walk you through the process of setting up a PostgreSQL client and making server-side database calls in your Next.js project using TypeScript.
Thank me by sharing on Twitter 🙏
Setting Up the PostgreSQL Client
To start, we need to install the pg library, which will allow us to interact with our PostgreSQL database.
Step 1: Install the pg Library
Run the following command in your project directory to install the pg library:
npm install pgStep 2: Configure Environment Variables
Next, we need to set up our environment variables to store our database connection details. Create a .env.local file in the root of your project if it doesn’t already exist and add your PostgreSQL connection details:
DATABASE_URL=postgresql://user:password@host:port/databaseStep 3: Create a PostgreSQL Client Instance
We will create a new file lib/db.ts to initialize the PostgreSQL client. This file will be responsible for setting up the connection to our database.
Highwings 8K 10K 4K HDMI Cable 48Gbps 6.6FT/2M, Certified Ultra High Speed HDMI Cable Braided Cord-4K@120Hz 8K@60Hz, DTS:X, HDCP 2.2 & 2.3, HDR 10 Compatible with Roku TV/PS5/HDTV/Blu-ray
$8.49 (as of November 19, 2025 01:28 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Syntech USB C to USB Adapter Pack of 2, USB 3.0 Female to Thunderbolt 4/3 Adapter Compatible with MacBook Pro Air 2024, Surface, iPad, iPhone, Galaxy Notebook, XPS and More Type C Devices, Space Grey
$9.99 (as of November 19, 2025 01:28 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Car Carplay Cable for iPhone 17 15 16 /Air/ Pro Max, USB A to USB C for Carplay for iPhone USB Cord, Car Charger for iPhone 17 Air Charging Cable, iPad Pro, iPad Air 5th, Mini 6th Gen Car Charger Cord
$7.99 (as of November 19, 2025 01:28 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)// lib/db.ts
import { Client } from 'pg';
const client = new Client({
connectionString: process.env.DATABASE_URL,
});
client.connect();
export default client;Fetching Data on the Server Side
Now that we have our PostgreSQL client set up, we can move on to fetching data on the server side within our Next.js page component.
Step 4: Create the Page Component
We will create a file app/page.tsx where we will fetch data from PostgreSQL on the server side.
// app/page.tsx
import client from '../lib/db';
interface User {
id: number;
name: string;
}
export default async function Home() {
let users: User[] = [];
try {
const result = await client.query('SELECT * FROM users');
users = result.rows;
} catch (error) {
console.error('Failed to fetch users:', error);
}
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}In this example:
- The
Homecomponent is an async function, allowing us to fetch data on the server side. - We define a
Userinterface to type our user data. - The PostgreSQL query is executed directly within the
Homecomponent. - The fetched data is stored in the
usersarray and rendered on the server side.
Handling Errors Gracefully
Fetching data from a database can sometimes result in errors, such as connectivity issues or malformed queries. It’s important to handle these errors gracefully to ensure a smooth user experience.
Step 5: Implement Error Handling
We already added a basic error handling mechanism in our Home component, but let’s expand on that to make it more robust.
// app/page.tsx
import client from '../lib/db';
interface User {
id: number;
name: string;
}
export default async function Home() {
let users: User[] = [];
try {
const result = await client.query('SELECT * FROM users');
users = result.rows;
} catch (error) {
console.error('Failed to fetch users:', error);
// Optionally, you could set an error state to display an error message to the user
users = [{ id: -1, name: 'Error fetching users' }];
}
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}In this enhanced version, if there’s an error fetching the users, we set a default error message in the users array. This ensures that our application remains functional and provides feedback to the user.
Conclusion
Integrating PostgreSQL into your Next.js application and performing server-side database operations can greatly enhance your ability to build dynamic and responsive web applications. By following the steps outlined in this post, you can set up a PostgreSQL client, fetch data server-side, and handle errors gracefully, all while leveraging TypeScript for better type safety and developer experience.
Building data-driven applications has never been easier with Next.js 13 and its powerful new features. Dive in, explore more, and see how you can enhance your projects with server-side data fetching and database integration.


