~/techpuraistart learning
~/tutorials/build-a-rest-api/04-connecting-a-database
intermediate·part 4 of 6·1 min read

Connecting a database

Updated Aug 6, 2026Node.js · PostgreSQL

Every route so far has returned hardcoded data. This part connects a real Postgres database using a connection pool, and rewrites the user route from part two to query it.

Why a pool, not a single connection

Opening a new database connection per request is slow and will exhaust your database's connection limit under load. A pool keeps a small set of connections open and hands them out as requests need them.

js
const { Pool } = require('pg');

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

Set DATABASE_URL in a .env file locally — never commit it. Most free Postgres hosts (including the one you'll deploy to in part six) give you this connection string directly.

Querying from a route

Replace the hardcoded user route with a real query, wrapped in the asyncHandler from part three so failures reach your error middleware:

js
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const { rows } = await pool.query(
    'SELECT id, name, email FROM users WHERE id = $1',
    [req.params.id]
  );

  if (rows.length === 0) {
    return res.status(404).json({ error: 'User not found' });
  }

  res.json(rows[0]);
}));
Security note

Always pass values as query parameters ($1, $2, ...) instead of building SQL strings with template literals. String concatenation here is exactly how SQL injection vulnerabilities happen.

Try it yourself

Create a users table with id, name, and email columns, insert a couple of rows, and confirm the route above returns real data. Next, you'll protect routes like this one behind authentication.

← previous3. Error handling in Express, the way you'll actually use itnext →5. Auth middleware