Imports & extend
imports lets you consume schema shipped by an installed package instead of
copying its YAML into your own schema/ directory. The installed dependency
version controls the schema — bump the dependency to get schema changes; no
copy, no drift. extend: then lets each app add its own columns, indexes, and
policies to a shared table without owning its definition.
imports — load schema from packages
Section titled “imports — load schema from packages”List package imports in your config file:
imports: - '@smplcty/auth' - '@smplcty/schema-std'
default: pgSchema: publicFor each entry, schema-flow loads that package’s
node_modules/<pkg>/schema/{tables,enums,functions,views,roles,mixins,pre,post}
as additional sources, merged with your local ./schema.
- Precedence: imported sources load first (in listed order), then the local schema. So a local definition can build on imported ones.
- Cross-source references work: a local table can have a foreign key to an
imported table, a local table can list an imported mixin in
mixins:, and imported functions are available to local triggers. - Apply ordering respects cross-source dependencies — schema-flow creates
every table first, then adds foreign keys, so an imported
usersis in place before a local table that references it.
Version pinning and pnpm dlx
Section titled “Version pinning and pnpm dlx”Imports are resolved by walking up from your schema directory to find
node_modules/<pkg>/schema. Because your schema directory lives inside your
project, this always resolves from your project’s node_modules — even
when you run pnpm dlx @smplcty/schema-flow run, the imported schema comes from
your installed, version-pinned dependency, not the dlx sandbox where
schema-flow itself was fetched.
A missing package, or a package that ships no schema/ directory, fails with a
clear error naming the package.
Parameterized imports
Section titled “Parameterized imports”An import entry can be an object with params to supply values for a package’s
parameterized mixins — e.g.
the FK target an audit mixin uses, or the GUC its trigger reads. Defaults
declared on the mixin cover the common case, so params is only needed to
override them:
imports: - package: '@smplcty/schema-std' params: { user_table: accounts, user_pk: account_id, actor_guc: app.actor_id } - '@smplcty/auth' # bare string when no overrides are neededSupplying a param the package doesn’t declare is an error.
Drift and lint
Section titled “Drift and lint”Imported objects are part of your desired state. drift and lint compare the
live database against the merged (imports + local) schema, so imported
tables are never reported as unmanaged or as drift.
Conflicts
Section titled “Conflicts”Declaring a table locally that an import already declares (without using
extend:) is an error that names both sources:
Table "users" is declared in two sources: @smplcty/auth:tables/users.yaml andtables/users.yaml. To add columns to an imported table, use an "extend:" fileinstead of re-declaring it.extend: — add to a table without redeclaring it
Section titled “extend: — add to a table without redeclaring it”An extend: file augments an existing table — imported or local — without
owning its full definition. This is what lets each app add its own columns to a
shared users/sessions:
extend: userscolumns: - { name: display_name, type: text }mixins: [soft_delete]indexes: - { columns: [display_name] }extend: merges columns, indexes, checks, triggers, policies,
grants, mixins, seeds, rls, and force_rls into the named table.
- Multiple
extend:files for one table are allowed — they merge in source order (imports first, then local). - Re-declaring an existing column is an error. Column type changes go
through a pre-script, not
extend:. - Targeting a table that doesn’t exist in any source is an error.
Example: sharing a canonical identity schema
Section titled “Example: sharing a canonical identity schema”@smplcty/auth ships the canonical users/sessions tables. Two apps consume
it by reference and each adds its own columns:
imports: - '@smplcty/auth'# schema/tables/users.ext.yaml — this app's extra columnsextend: userscolumns: - { name: marketing_opt_in, type: boolean, nullable: false, default: 'false' }# schema/tables/orders.yaml — this app's own table, FK to the imported userstable: orderscolumns: - name: id type: bigint primary_key: true - name: user_id type: bigint references: { table: users, column: user_id }Bumping the @smplcty/auth dependency picks up any changes to the canonical
users/sessions schema — no copy-paste, no drift.