Server-Side PostgreSQL Integration in Next.js with TypeScript

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:

ShellScript
npm install pg

Step 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:

Plaintext
DATABASE_URL=postgresql://user:password@host:port/database

Step 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.

TypeScript
// 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.

TSX
// 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 Home component is an async function, allowing us to fetch data on the server side.
  • We define a User interface to type our user data.
  • The PostgreSQL query is executed directly within the Home component.
  • The fetched data is stored in the users array 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.

TSX
// 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.

Share this:

Leave a Reply