@postgres Directive

The @postgres directive resolves a field by executing a SQL operation against a PostgreSQL table. It requires a database connection linked via @link(type: Postgres, src: "postgres://...").

Fields

FieldTypeDefaultDescription
dbStringnullThe @link(type: Postgres) id to use. Optional when only one Postgres link is defined.
tableStringRequiredTarget table name (optionally schema-qualified, e.g. "public.users").
operationPostgresOperationSELECTThe CRUD operation to perform. See below.
filterJSONnullA JSON object describing the WHERE clause. Supports Mustache templates.
inputStringnullInput data source for INSERT/UPDATE. Typically "{{.args.input}}".
batchKey[String][]Columns used for DataLoader batch keys (N+1 prevention).
dedupeBooleanfalseDeduplicate identical in-flight database calls.
limitStringnullMustache template for the LIMIT clause, e.g. "{{.args.limit}}".
offsetStringnullMustache template for the OFFSET clause, e.g. "{{.args.offset}}".
orderByStringnullMustache template for the ORDER BY clause, e.g. "{{.args.orderBy}}".

PostgresOperation

ValueDescription
SELECTSelect multiple rows. Returns a list.
SELECT_ONESelect a single row by primary key or unique constraint.
INSERTInsert a new row and return the created record.
UPDATEUpdate an existing row and return the updated record.
DELETEDelete a row.

Examples

Fetching a single record by ID

type Query {
  userById(id: Int!): User
  @postgres(
    table: "users"
    operation: SELECT_ONE
    filter: { id: "{{.args.id}}" }
  )
}

Inserting a record

type Mutation {
  createUser(input: CreateUserInput!): User
  @postgres(
    table: "users"
    operation: INSERT
    input: "{{.args.input}}"
  )
}

Paginated list with ordering

type Query {
  usersList(limit: Int, offset: Int, orderBy: String): [User!]!
  @postgres(
    table: "users"
    operation: SELECT
    limit: "{{.args.limit}}"
    offset: "{{.args.offset}}"
    orderBy: "{{.args.orderBy}}"
  )
}

Batched relationship (N+1 prevention)

type Post {
  author: User
  @postgres(
    table: "users"
    operation: SELECT_ONE
    filter: { id: "{{.value.userId}}" }
    batchKey: ["id"]
  )
}

Multiple databases

When multiple @link(type: Postgres) are defined, use the db field to specify which connection to query:

schema
@server(port: 8000)
@link(id: "main", type: Postgres, src: "postgres://localhost:5432/main_db")
@link(id: "analytics", type: Postgres, src: "postgres://localhost:5432/analytics_db") {
  query: Query
}

type Query {
  userById(id: Int!): User @postgres(db: "main", table: "users", operation: SELECT_ONE, filter: { id: "{{.args.id}}" })

  pageViews(limit: Int): [PageView!]!
  @postgres(db: "analytics", table: "page_views", operation: SELECT, limit: "{{.args.limit}}")
}

When only one @link(type: Postgres) is defined, the db field can be omitted.

Security

All dynamic values referenced by Mustache templates in filter, input, limit, offset, and orderBy are passed as parameterised query arguments — they are never interpolated into SQL text. Table and column names are escaped using quote_ident to prevent SQL injection.