Skip to content
PtahPtah

HCL schema

Use HCL schema files when you already maintain schema files in Atlas-compatible HCL syntax and want Ptah to read the supported subset. Unsupported constructs fail explicitly instead of being silently guessed.

Create schema.hcl:

schema "public" {}
table "accounts" {
schema = schema.public
column "id" {
type = int
}
column "email" {
type = varchar(255)
null = false
}
column "created_at" {
type = timestamptz
}
index "accounts_email_key" {
unique = true
columns = [column.email]
}
unique "accounts_email_covering" {
columns = [column.email]
include = [column.created_at]
}
}
schema "extensions" {}
extension "pgcrypto" {
schema = schema.extensions
if_not_exists = true
}
Terminal window
ptah schema render --schema-file schema.hcl --dialect postgres

Expected output includes:

CREATE SCHEMA IF NOT EXISTS "public";
CREATE SCHEMA IF NOT EXISTS "extensions";
CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions";
CREATE TABLE "public"."accounts" (
"id" int NOT NULL,
"email" varchar(255) NOT NULL,
"created_at" timestamptz NOT NULL,
CONSTRAINT "accounts_email_covering" UNIQUE ("email") INCLUDE ("created_at")
);
CREATE UNIQUE INDEX IF NOT EXISTS "accounts_email_key" ON "public"."accounts" ("email");

--schema-file is accepted wherever Ptah needs a desired schema: ptah schema render, ptah schema compare, ptah schema drift, the migration commands (ptah migrations plan / ptah migrations generate), and every target of ptah schema export except hcl, whose source is --root-dir. That includes the two documentation targets, so a Markdown or HTML reference can be generated from this file.

Path confinement is shared by every --schema-file source; see Schema file paths.

To replace Go annotations with an HCL source, use the review-aware one-time export workflow in Go annotations.

Ptah HCL extends the Atlas-compatible table and column blocks with contract metadata. Tables accept api_name, openapi_name, graphql_name, and proto_name. Columns accept those four attributes plus api_type and api_expose:

table "billing_invoices" {
api_name = "invoices"
openapi_name = "invoice_documents"
graphql_name = "invoice_records"
proto_name = "invoice_records"
column "billing_amount_minor" {
type = integer
api_name = "amount"
openapi_name = "amount_value"
graphql_name = "amountMinor"
proto_name = "amount_minor"
api_type = "TEXT"
api_expose = "read"
}
}

The target-specific name wins over api_name, which wins over the database name. Table GraphQL and Protobuf values are type/message stems; column GraphQL and Protobuf values are exact field names. api_type changes only the generated contract type, and api_expose accepts read, write, read-write, or none. Canonical HCL export and OCI schema artifacts preserve all six column attributes and all four table attributes. Unknown or non-string attributes fail before output. See API schema export for target naming rules and collision behavior.

These attributes are Ptah extensions, not Atlas CE schema syntax. Native Ptah and the default compatibility profile preserve them; strict Atlas CE mode refuses a schema that contains API export metadata instead of discarding it.

Everything a desired schema is for — comparing, gating on drift, generating migrations, applying directly, composing sources, validating across dialects — is the same for every source and lives on Work with a desired schema. For HCL the flag is --schema-file:

Terminal window
ptah migrations plan \
--schema-file schema.hcl \
--db-url "$DATABASE_URL"

Ptah reads schema HCL as desired-schema input. An atlas.hcl project configuration is a different file type: it provides database URLs, migration directories, and environment selection, while a schema HCL file provides desired schema objects. See Configuration and the Atlas project config subset.