Managing Database Package Dependencies in a Workspace

Dan Lynch

Dan Lynch

Nov 14, 2025

lesson header image

Previously: In Testing Your Database Module, we added tests to the pets module. Now let's explore multi-module dependencies.

The real power of pgpm workspaces emerges when you build multiple modules that depend on each other. Instead of manually tracking deployment order, pgpm resolves the entire dependency graph automatically. In this lesson, you'll create modules that depend on other modules, use cross-module references, and leverage recursive deployment.

Prerequisites

See Prerequisites. Requires: Complete Creating Your First Postgres Database Module.

Understanding Module Dependencies

Modules declare dependencies in their .control file. Dependencies can be:

  • Native Postgres extensions (uuid-ossp, pgcrypto, postgis)
  • Other database modules in your workspace

When you deploy a module, pgpm automatically deploys all dependencies first.

Step 1: Create a Dependent Module

Let's create an adoptions module that depends on the pets module:

cd my-database-project
pgpm init

Enter module details:

? Enter the module name: adoptions
? Which extensions? pets, uuid-ossp

Notice we selected pets as a dependency. pgpm discovered it by scanning the workspace for .control files.

The generated adoptions.control file:

# adoptions.control
comment = 'Pet adoption tracking module'
default_version = '0.0.1'
requires = 'pets,uuid-ossp'

Step 2: Add Schema and Table Changes

Navigate to the adoptions module:

cd packages/adoptions

Add a schema:

pgpm add schemas/adoptions

Edit deploy/schemas/adoptions.sql:

-- Deploy schemas/adoptions

CREATE SCHEMA adoptions;

Add an adoptions table:

pgpm add schemas/adoptions/tables/adoptions

Edit deploy/schemas/adoptions/tables/adoptions.sql:

-- Deploy schemas/adoptions/tables/adoptions

CREATE TABLE adoptions.adoptions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pet_id UUID NOT NULL REFERENCES pets.pets(id),
  adopter_name TEXT NOT NULL,
  adoption_date DATE DEFAULT CURRENT_DATE,
  created_at TIMESTAMPTZ DEFAULT now()
);

This table references pets.pets(id) from the pets module. pgpm ensures the pets module deploys first.

Step 3: Deploy with Recursive Resolution

Deploy the adoptions module:

pgpm deploy --database pets_dev --package adoptions --yes

pgpm automatically:

  1. Detects that adoptions depends on pets
  2. Checks if pets is already deployed
  3. Deploys pets if needed (skipped if already deployed)
  4. Deploys adoptions

This is recursive deployment—pgpm resolves the entire dependency tree automatically.

Step 4: Cross-Module Change Dependencies

You can declare dependencies on specific changes from other modules. Add a change that depends on a specific change in the pets module:

pgpm add schemas/adoptions/tables/adoption_history --requires pets:schemas/pets/tables/pets

Edit deploy/schemas/adoptions/tables/adoption_history.sql:

-- Deploy schemas/adoptions/tables/adoption_history
-- requires: pets:schemas/pets/tables/pets

CREATE TABLE adoptions.adoption_history (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pet_id UUID NOT NULL REFERENCES pets.pets(id),
  event_type TEXT NOT NULL,
  event_date TIMESTAMPTZ DEFAULT now()
);

The -- requires: pets:schemas/pets/tables/pets comment tells pgpm this change depends on the schemas/pets/tables/pets change from the pets module.

Step 5: Understanding Dependency Resolution

pgpm resolves dependencies by:

  1. Scanning the workspace for all .control files
  2. Building a dependency graph from requires fields
  3. Resolving topological order (which modules deploy first)
  4. Deploying in sequence with transactional safety

Step 6: Managing Extension Dependencies

The pgpm extension command helps manage module dependencies interactively:

cd packages/adoptions
pgpm extension

pgpm shows available modules and extensions:

? Which extensions? 
  ● pets
  ● uuid-ossp
  ◯ plpgsql
  ◯ pgcrypto

Tip: Use spacebar to select/deselect extensions, arrow keys to navigate, and Enter to confirm. pgpm updates the .control file automatically.

Note: The pgpm install command is for installing npm-based database packages (e.g., @pgpm/base32) into your module's extensions/ directory. That's a different workflow covered in advanced lessons.

Key Takeaways

  • Modules declare dependencies in .control files
  • pgpm automatically resolves dependency graphs
  • Recursive deployment deploys all dependencies automatically
  • Cross-module references use module:change syntax
  • pgpm extension manages dependencies interactively
  • pgpm detects circular dependencies
  • Shortest path wins for naming collisions

Multi-module dependencies make pgpm workspaces powerful. Let's explore testing and packaging next.