For AI agents: the complete documentation index is available at /hclapi/llms.txt, the full documentation bundle is available at /hclapi/llms-full.txt, and this page is available as Markdown at /hclapi/docs/steps/sql.md.

sql

Executes a parameterized SQL query or mutation against a database connection pool. Parameters are bound through prepared statements using @name placeholders.

Declaration

sql "find_user" {
  connection = connection.postgres.main
  query      = <<-SQL
    SELECT id, name, email FROM users WHERE id = @id
  SQL
  args = { id = ctx.request.path.id }
}

Attributes

AttributeTypeRequiredDescription
labelstringyesStep identifier; outputs are written to steps.<name>
connectionconnectionyesDatabase connection pool reference
querystringyesQuery or mutation; parameters use @name
argsmapnoContext values bound to query parameters
catch "<code>"blocknoHandles specific database error codes

Exported outputs

FieldTypeDescription
steps.<name>.rowslist(map)All returned rows; [] if no rows match
steps.<name>.rowmap or nullFirst returned row
steps.<name>.rows_affectedintRows returned, inserted, updated, or deleted

Fetch a single record

endpoint "GET /api/v1/users/{id}" {
  pipeline {
    sql "find_user" {
      connection = connection.postgres.main
      query      = "SELECT id, name, email FROM users WHERE id = @id"
      args       = { id = ctx.request.path.id }
    }

    respond {
      condition = steps.find_user.rows_affected == 0
      status    = 404
      body      = { error = "User not found" }
    }

    respond {
      status = 200
      body   = steps.find_user.row
    }
  }
}

Fetch a list

endpoint "GET /api/v1/users" {
  pipeline {
    sql "list_users" {
      connection = connection.postgres.main
      query      = "SELECT id, name, email FROM users ORDER BY id LIMIT 50"
    }

    respond {
      status = 200
      body   = steps.list_users.rows
    }
  }
}

Catch database constraint errors

sql "insert_user" {
  connection = connection.postgres.main
  query      = <<-SQL
    INSERT INTO users (email, full_name)
    VALUES (@email, @full_name)
    RETURNING id, email, full_name, created_at
  SQL
  args = {
    email     = ctx.request.body.email
    full_name = ctx.request.body.full_name
  }

  catch "23505" {
    status = 409
    body = {
      error = "A user with this email address already exists."
    }
  }
}

Common database error codes

ErrorPostgreSQL / CockroachDBSQLiteMySQL / MariaDBSQL Server
Unique violation235052067 / 1910622627 / 2601
Foreign key violation23503787 / 191452547
Not null violation235021299 / 191048515
Check constraint failure23514275 / 193819547
Deadlock / serialization40001 / 40P01512131205

See the official PostgreSQL, SQLite, MySQL, and SQL Server error references.