Skip to main content

Overview

The Inmobiliaria API uses PostgreSQL as its database and Drizzle ORM for type-safe database access and migrations.

Requirements

  • PostgreSQL 12 or higher
  • Node.js 18 or higher
  • Database with public schema access

Quick Setup

1

Install PostgreSQL

Install PostgreSQL on your system:
brew install postgresql@15
brew services start postgresql@15
2

Create Database

Create a database for the application:
CREATE DATABASE inmobiliaria;
Or using psql:
psql -U postgres -c "CREATE DATABASE inmobiliaria;"
3

Set DATABASE_URL

Add the connection string to your .env file:
.env
DATABASE_URL=postgresql://postgres:password@localhost:5432/inmobiliaria
Format: postgresql://[user]:[password]@[host]:[port]/[database]
4

Run Migrations

Apply the database schema:
npm run db:push
Or use migration files:
npm run db:migrate:run
5

Seed Data (Optional)

Populate with sample data for development:
npm run db:seed

Drizzle Configuration

The database is configured in drizzle.config.ts:
drizzle.config.ts
import { defineConfig } from "drizzle-kit";

// Ensure search_path is set for migrations
let dbUrl = process.env.DATABASE_URL!;
if (dbUrl && !dbUrl.includes("search_path")) {
  const separator = dbUrl.includes("?") ? "&" : "?";
  dbUrl += `${separator}schema=public`;
}

export default defineConfig({
  out: "./src/db/migrations",
  schema: "./src/db/schema/*",
  dialect: "postgresql",
  dbCredentials: {
    url: dbUrl,
  },
  schemaFilter: ["public"],
  verbose: true,
  strict: true,
});

Configuration Options

OptionValueDescription
out./src/db/migrationsDirectory for migration files
schema./src/db/schema/*Drizzle schema definition files
dialectpostgresqlDatabase type
schemaFilter["public"]Use PostgreSQL public schema
verbosetrueShow detailed migration logs
stricttrueEnable strict mode for schema validation

Database Schema

The application uses a comprehensive schema organized into several domains.

Authentication Tables

Core user accounts table (Better Auth compatible).Schema Definition: src/db/schema/users.ts
ColumnTypeDescription
idtextPrimary key (UUID)
nametextUser’s display name
emailtextUnique email address
email_verifiedbooleanEmail verification status
imagetextProfile image URL
roleuser_roleuser or admin
created_attimestampAccount creation time
updated_attimestampLast update time
Active user sessions.
ColumnTypeDescription
idtextPrimary key
user_idtextReferences users.id
tokentextUnique session token
expires_attimestampSession expiration
ip_addresstextClient IP
user_agenttextBrowser/device info
OAuth provider accounts linked to users.
ColumnTypeDescription
idtextPrimary key
user_idtextReferences users.id
provider_idtextOAuth provider (google, etc.)
account_idtextProvider’s user ID
access_tokentextOAuth access token
refresh_tokentextOAuth refresh token
Email verification tokens.
ColumnTypeDescription
idtextPrimary key
identifiertextEmail or user identifier
valuetextVerification token
expires_attimestampToken expiration

Property Management Tables

Main properties listing table.Schema Definition: src/db/schema/properties.tsKey Columns:
  • Basic info: title, description, url_slug
  • Classification: property_type_id, property_subtype_id, operation_type_id
  • Pricing: price, original_price, currency_id, expenses
  • Size: surface_covered_m2, surface_uncovered_m2, lot_size_m2
  • Rooms: rooms, bedrooms, bathrooms
  • Features: has_balcony, has_terrace, has_garden, garage_spaces
  • Status: status_id, published_at, views_count
  • Ownership: user_id
Relationships:
  • References multiple metadata tables
  • One-to-one with property_locations
  • One-to-many with property_images
Property address and geolocation.
ColumnTypeDescription
property_idintegerReferences properties.id (unique)
streetvarcharStreet name
street_numbervarcharStreet number
neighborhoodvarcharNeighborhood/barrio
cityvarcharCity name
provincevarcharProvince/state
latitudedecimal(10,7)GPS latitude
longitudedecimal(10,7)GPS longitude
show_exact_addressbooleanPrivacy control
Property photos with variants.
ColumnTypeDescription
property_idintegerReferences properties.id
image_keytextStorage key (main variant)
image_urltextPublic URL
order_indexintegerDisplay order
is_mainbooleanFeatured image flag
rotation_degreesinteger0, 90, 180, or 270
Images are stored with variants: -lg.webp (large) and -thumb.webp (thumbnail).

Metadata Tables

Property categories (Casa, Departamento, etc.).Pre-populated with: 14 types including Casa, Departamento, PH, Terreno, Local comercial, etc.
ColumnTypeDescription
idserialPrimary key
namevarcharDisplay name
slugvarcharURL-friendly identifier
Subcategories within property types.Examples: Casa en country, Monoambiente, PH con jardín
ColumnTypeDescription
idserialPrimary key
type_idintegerReferences property_types.id
namevarcharDisplay name
slugvarcharURL-friendly identifier
Transaction types: Venta, Alquiler, Alquiler temporal, Permuta.
Status: Activo, Pendiente, Vendido, Alquilado, Pausado.
Condition: A estrenar, Bueno, Muy bueno, Excelente, Reciclado, Para refaccionar.
Supported currencies: ARS (Peso Argentino), USD (Dólar), EUR (Euro).

Property Characteristics

Unified characteristics table supporting both predefined and custom features.
ColumnTypeDescription
iduuidPrimary key
property_idintegerReferences properties.id
labelvarchar(150)Characteristic name
created_attimestampWhen added
Constraints:
  • Unique per property (case-insensitive, trimmed)
  • Label length: 1-150 characters
Examples: Parrilla, Pileta, Aire acondicionado, SUM, Gimnasio, Seguridad 24hs

Contact & Engagement

Property valuation and contact form submissions.Schema Definition: src/db/schema/contact.ts
ColumnTypeDescription
iduuidPrimary key
namevarcharContact name
emailvarcharContact email
phonevarcharContact phone
available_hoursvarcharPreferred contact time
operation_typevarcharVenta/Alquiler
property_typevarcharType of property
locationtextProperty location
descriptiontextDetails
statusenumpending/contacted/completed
user_idtextOptional linked user
is_deletedbooleanSoft delete flag
User saved properties.
ColumnTypeDescription
idserialPrimary key
user_idtextReferences users.id
property_idintegerReferences properties.id
created_attimestampWhen favorited
Constraint: Unique per user-property pair

Database Scripts

Available npm scripts for database management:
package.json
{
  "scripts": {
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "db:push": "drizzle-kit push",
    "db:studio": "drizzle-kit studio",
    "db:seed": "tsx src/db/seed.ts",
    "db:migrate:run": "tsx src/db/migrate.ts",
    "db:setup": "node scripts/setup-database.js"
  }
}

Command Reference

# Generate SQL migration files from schema changes
npm run db:generate

# Output: src/db/migrations/*.sql

Connection Configuration

The database connection is configured in src/db/index.ts:
src/db/index.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  // SSL configuration for production
  ssl: process.env.DATABASE_URL?.includes("sslmode=require")
    ? { rejectUnauthorized: false }
    : false,
});

export const db = drizzle(pool, { schema });

Connection Pooling

The application uses pg connection pooling with defaults:
  • Max connections: 10 (pg default)
  • Idle timeout: 10 seconds
  • Connection timeout: 0 (no timeout)
To customize:
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20, // max connections
  idleTimeoutMillis: 30000, // 30 seconds
  connectionTimeoutMillis: 2000, // 2 seconds
});

Health Checks

The API includes database health check endpoints:
GET /

Response:
{
  "status": "ok",
  "message": "Inmobiliaria API Server",
  "timestamp": "2026-03-03T10:30:00.000Z",
  "database": {
    "status": "healthy",
    "latency": "5ms"
  }
}

Production Considerations

Enable SSL for production databases:
DATABASE_URL=postgresql://user:pass@host:5432/db?sslmode=require
The application automatically detects sslmode=require and configures SSL.
  • Monitor active connections
  • Configure max pool size based on load
  • Use connection pooling services (PgBouncer) for high traffic
  • Most cloud providers (Heroku, Render) include connection pooling
Development:
npm run db:push  # Quick schema updates
Production:
npm run db:generate  # Generate migration SQL
npm run db:migrate:run  # Apply migrations
  • Always use migrations in production
  • Review generated SQL before applying
  • Backup database before major migrations
Regular backup strategies:
# Full backup
pg_dump -h host -U user -d inmobiliaria > backup.sql

# Schema only
pg_dump -h host -U user -d inmobiliaria --schema-only > schema.sql

# Data only
pg_dump -h host -U user -d inmobiliaria --data-only > data.sql
Automated: Use your hosting provider’s backup features
The schema includes performance indexes:
  • idx_sessions_token - Fast session lookups
  • idx_sessions_user_id - User’s sessions
  • idx_properties_type - Filter by property type
  • idx_properties_operation - Filter by operation
  • idx_locations_city - Location-based search
  • idx_properties_search - Full-text search (GIN index)
Monitor slow queries and add indexes as needed.

Troubleshooting

Error: ECONNREFUSED or “Connection refused”Solutions:
  • Verify PostgreSQL is running: pg_isready
  • Check host/port in DATABASE_URL
  • Verify firewall allows connections
  • For Docker: check container is running
Error: “password authentication failed”Solutions:
  • Verify username and password in DATABASE_URL
  • Check pg_hba.conf authentication method
  • Reset password: ALTER USER postgres PASSWORD 'newpassword';
Error: ‘database “inmobiliaria” does not exist’Solution:
psql -U postgres -c "CREATE DATABASE inmobiliaria;"
Error: ‘relation “users” does not exist’Solutions:
  • Run migrations: npm run db:push or npm run db:migrate:run
  • Verify schemaFilter: ["public"] in drizzle.config.ts
  • Check search_path: SHOW search_path;
Error: “sorry, too many clients already”Solutions:
  • Reduce pool max connections
  • Close idle connections
  • Increase PostgreSQL max_connections
  • Use connection pooling service (PgBouncer)

See Also

Build docs developers (and LLMs) love