Skip to main content
RapidDev - Software Development Agency
replit-tutorial

How to use a database with Replit

Replit offers two built-in database options: PostgreSQL (SQL Database) for relational data and Replit DB for simple key-value storage. PostgreSQL is the recommended choice for full-stack apps, providing up to 10 GiB of storage with automatic DATABASE_URL configuration. Access it through the SQL runner, Drizzle Studio visual manager, or your application code. Replit DB is simpler but limited to 50 MiB and 5,000 keys. Both are accessible from the Tools dock without any external service setup.

What you'll learn

  • Set up and connect to Replit's built-in PostgreSQL database
  • Use the SQL runner and Drizzle Studio to manage data visually
  • Implement CRUD operations in Node.js and Python with the DATABASE_URL
  • Understand the key-value Replit DB and when to use it instead of PostgreSQL
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate9 min read20 minutesAll Replit plans (Starter: free 10 GB dev database, Core/Pro: production databases available)March 2026RapidDev Engineering Team
TL;DR

Replit offers two built-in database options: PostgreSQL (SQL Database) for relational data and Replit DB for simple key-value storage. PostgreSQL is the recommended choice for full-stack apps, providing up to 10 GiB of storage with automatic DATABASE_URL configuration. Access it through the SQL runner, Drizzle Studio visual manager, or your application code. Replit DB is simpler but limited to 50 MiB and 5,000 keys. Both are accessible from the Tools dock without any external service setup.

How to Use a Database with Replit for Full-Stack Applications

Every full-stack application needs a database. Replit provides built-in PostgreSQL and a key-value store so you can add data persistence without configuring external services. This tutorial covers both options: when to use each, how to set them up, how to query data, and how to avoid the most common pitfalls like missing deployment secrets and accidentally modifying production data.

Prerequisites

  • A Replit account (Starter plan includes a free development database)
  • A web project (Node.js or Python) in Replit
  • Basic understanding of SQL (SELECT, INSERT, UPDATE, DELETE)
  • Familiarity with environment variables and the Secrets tool

Step-by-step guide

1

Add a PostgreSQL database to your Repl

Open your Replit workspace and look for the Tools dock on the left sidebar. Search for Database or SQL Database and click to open it. Replit automatically provisions a PostgreSQL 16 database and creates environment variables including DATABASE_URL, PGHOST, PGUSER, PGPASSWORD, PGDATABASE, and PGPORT. These are available immediately in your code via process.env in Node.js or os.getenv() in Python. The development database is free with 10 GB storage.

Expected result: A PostgreSQL database is provisioned, and DATABASE_URL appears automatically in your environment variables.

2

Create tables using the SQL runner

In the Database tool, find the SQL runner panel where you can type and execute SQL queries directly. Create your tables with standard PostgreSQL syntax. Use CREATE TABLE IF NOT EXISTS so the query is safe to run multiple times. Define columns with appropriate types: TEXT for strings, INTEGER for whole numbers, NUMERIC for decimals, TIMESTAMP for dates, BOOLEAN for true/false, and SERIAL for auto-incrementing IDs.

typescript
1CREATE TABLE IF NOT EXISTS users (
2 id SERIAL PRIMARY KEY,
3 email TEXT UNIQUE NOT NULL,
4 name TEXT NOT NULL,
5 created_at TIMESTAMP DEFAULT NOW()
6);
7
8CREATE TABLE IF NOT EXISTS posts (
9 id SERIAL PRIMARY KEY,
10 user_id INTEGER REFERENCES users(id),
11 title TEXT NOT NULL,
12 content TEXT,
13 published BOOLEAN DEFAULT FALSE,
14 created_at TIMESTAMP DEFAULT NOW()
15);
16
17-- Verify tables were created
18SELECT table_name FROM information_schema.tables
19WHERE table_schema = 'public';

Expected result: Tables are created and visible in both the SQL runner results and Drizzle Studio.

3

Connect to PostgreSQL from Node.js

Install a PostgreSQL client library and connect using the auto-configured DATABASE_URL. The pg package is the most common choice for Node.js. Create a database module that initializes a connection pool and exports query functions. Always use parameterized queries (with $1, $2 placeholders) to prevent SQL injection. Close the pool gracefully on process exit.

typescript
1// Install in Shell: npm install pg
2
3// src/db.js
4const { Pool } = require('pg');
5
6const pool = new Pool({
7 connectionString: process.env.DATABASE_URL,
8 ssl: false // Not needed for Replit's internal connection
9});
10
11// Test connection on startup
12pool.query('SELECT NOW()', (err, res) => {
13 if (err) console.error('Database connection failed:', err.message);
14 else console.log('Database connected at:', res.rows[0].now);
15});
16
17// Export query helper
18module.exports = {
19 query: (text, params) => pool.query(text, params),
20 pool
21};

Expected result: Your Node.js app connects to PostgreSQL on startup and logs the connection timestamp.

4

Implement CRUD operations

Build API routes that create, read, update, and delete records using your database module. Each operation maps to a SQL command: INSERT for create, SELECT for read, UPDATE for update, and DELETE for delete. Use RETURNING * on INSERT and UPDATE to get the new or modified row back without a separate query. Handle errors gracefully and return appropriate HTTP status codes.

typescript
1// src/routes/users.js
2const express = require('express');
3const db = require('../db');
4const router = express.Router();
5
6// Create a user
7router.post('/', async (req, res) => {
8 try {
9 const { email, name } = req.body;
10 const result = await db.query(
11 'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *',
12 [email, name]
13 );
14 res.status(201).json(result.rows[0]);
15 } catch (err) {
16 if (err.code === '23505') {
17 res.status(409).json({ error: 'Email already exists' });
18 } else {
19 res.status(500).json({ error: err.message });
20 }
21 }
22});
23
24// Get all users
25router.get('/', async (req, res) => {
26 const result = await db.query(
27 'SELECT id, email, name, created_at FROM users ORDER BY created_at DESC'
28 );
29 res.json(result.rows);
30});
31
32module.exports = router;

Expected result: Your API routes create, read, update, and delete records in PostgreSQL, returning proper JSON responses.

5

Use Replit DB for simple key-value storage

For simpler data needs like configuration, caching, or session storage, Replit DB is a zero-configuration key-value store. It supports strings, numbers, objects, and arrays as values. The limits are 50 MiB total, 5,000 keys, and 5 MiB per value. In Node.js, install @replit/database. In Python, use from replit import db. Note that in deployed apps, Replit DB reads its URL from /tmp/replitdb instead of the environment variable.

typescript
1// Node.js: npm install @replit/database
2const Database = require('@replit/database');
3const db = new Database();
4
5// Set a value
6await db.set('settings', { theme: 'dark', language: 'en' });
7
8// Get a value
9const settings = await db.get('settings');
10console.log(settings); // { theme: 'dark', language: 'en' }
11
12// List all keys
13const keys = await db.list();
14console.log(keys);
15
16// Delete a key
17await db.delete('settings');

Expected result: Values are stored and retrieved from Replit DB without any database configuration or connection strings.

6

Configure the database for production deployment

Development and production databases are separate in Replit. When you deploy, the production database starts empty. You need to run your table creation scripts against the production database after deploying. The DATABASE_URL is automatically configured for the deployment environment, but you must add any other database-related secrets in the Deployments pane. Production databases incur costs (~$0.16/hour compute, ~$1.50/GB/month storage). For complex database architectures spanning dev and production, RapidDev can help plan migration strategies and ensure data integrity.

typescript
1// Initialize tables on startup (works in both dev and prod)
2const db = require('./db');
3
4async function initDatabase() {
5 await db.query(`
6 CREATE TABLE IF NOT EXISTS users (
7 id SERIAL PRIMARY KEY,
8 email TEXT UNIQUE NOT NULL,
9 name TEXT NOT NULL,
10 created_at TIMESTAMP DEFAULT NOW()
11 )
12 `);
13 console.log('Database tables initialized');
14}
15
16initDatabase().catch(console.error);

Expected result: Your deployed app connects to the production database and creates tables on first startup.

Complete working example

src/db.js
1// src/db.js — PostgreSQL database module for Replit
2// Connects using the auto-configured DATABASE_URL
3
4const { Pool } = require('pg');
5
6if (!process.env.DATABASE_URL) {
7 console.error(
8 'DATABASE_URL is not set. Open the Database tool in Replit '
9 + 'to provision a PostgreSQL database.'
10 );
11 process.exit(1);
12}
13
14const pool = new Pool({
15 connectionString: process.env.DATABASE_URL,
16 max: 10,
17 idleTimeoutMillis: 30000,
18 connectionTimeoutMillis: 5000,
19});
20
21pool.on('error', (err) => {
22 console.error('Unexpected database pool error:', err.message);
23});
24
25// Initialize tables on first connection
26async function initDatabase() {
27 const client = await pool.connect();
28 try {
29 await client.query(`
30 CREATE TABLE IF NOT EXISTS users (
31 id SERIAL PRIMARY KEY,
32 email TEXT UNIQUE NOT NULL,
33 name TEXT NOT NULL,
34 created_at TIMESTAMP DEFAULT NOW()
35 )
36 `);
37 await client.query(`
38 CREATE TABLE IF NOT EXISTS posts (
39 id SERIAL PRIMARY KEY,
40 user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
41 title TEXT NOT NULL,
42 content TEXT,
43 published BOOLEAN DEFAULT FALSE,
44 created_at TIMESTAMP DEFAULT NOW()
45 )
46 `);
47 console.log('Database tables initialized successfully');
48 } finally {
49 client.release();
50 }
51}
52
53module.exports = {
54 query: (text, params) => pool.query(text, params),
55 pool,
56 initDatabase,
57};

Common mistakes when using a database with Replit

Why it's a problem: Expecting workspace DATABASE_URL to work in deployments without checking the deployment secrets

How to avoid: DATABASE_URL is auto-configured for deployments when using Replit's built-in PostgreSQL, but verify it exists in the Deployments pane

Why it's a problem: Using string concatenation for SQL queries, creating SQL injection vulnerabilities

How to avoid: Always use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId])

Why it's a problem: Using Replit DB as the primary database for a full-stack app and hitting the 50 MiB or 5,000 key limit

How to avoid: Use PostgreSQL for application data. Reserve Replit DB for configuration, caching, or simple prototypes.

Why it's a problem: Forgetting to create tables in the production database, causing 'relation does not exist' errors on deploy

How to avoid: Run CREATE TABLE IF NOT EXISTS in your app's initialization code so tables are created on first startup

Why it's a problem: Opening a new database connection for every query instead of using a connection pool

How to avoid: Initialize a Pool once and reuse it for all queries. The pool manages connections automatically.

Best practices

  • Use PostgreSQL for relational data and Replit DB only for simple key-value storage like settings or caches
  • Always use parameterized queries ($1, $2) to prevent SQL injection — never concatenate user input into SQL
  • Use CREATE TABLE IF NOT EXISTS so your initialization script is safe to run on every startup
  • Configure connection pool limits (max, idleTimeoutMillis) to avoid exhausting database connections
  • Separate development and production database concerns — production databases start empty after deployment
  • Handle PostgreSQL error codes specifically (23505 for duplicates, 23503 for foreign key violations)
  • Add RETURNING * to INSERT and UPDATE queries to get the result without a separate SELECT
  • Use Drizzle Studio in the Database tool for visual data management instead of writing queries for every task

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I added a PostgreSQL database in Replit but my Node.js app says DATABASE_URL is undefined. I can see the database in the SQL runner and it works there. What could be wrong and how do I fix it?

Replit Prompt

Set up a PostgreSQL database for my Replit project. Create a users table and a posts table with a foreign key relationship. Build Express API routes for creating, reading, updating, and deleting users. Use the pg library with parameterized queries and connection pooling.

Frequently asked questions

Replit provides two built-in options: PostgreSQL 16 (SQL Database) for relational data with up to 10 GiB storage, and Replit DB for simple key-value storage limited to 50 MiB and 5,000 keys. PostgreSQL is the recommended choice for full-stack applications.

Development databases are free with 10 GB storage. Production databases cost approximately $0.16/hour for compute and $1.50/GB/month for storage, with a 10 GiB maximum and 33 MB minimum footprint.

No. The DATABASE_URL is scoped to your Repl only and cannot be used from external tools or services. If you need external access, use a third-party database provider like Supabase or Neon.

Development and production databases are separate. Your production database starts empty when you first deploy. Run table creation scripts in your app's initialization code to set up the schema automatically.

Drizzle Studio is a visual database manager integrated into Replit's Database tool. It lets you browse tables, view and edit rows, and inspect relationships without writing SQL queries.

No. After a major incident in July 2025, Replit added a safety restriction preventing Agent from modifying production databases. You must run production schema changes and data modifications manually.

PostgreSQL is a full relational database supporting SQL, joins, indexes, and up to 10 GiB. Replit DB is a simple key-value store limited to 50 MiB and 5,000 keys. Use PostgreSQL for application data and Replit DB only for configuration or caching.

Replit's PostgreSQL supports time-travel rollback to Agent checkpoints. Pro plans include 28-day database restore. For manual backups, use pg_dump from Shell or export data through your application code.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.