Skip to content
On this page

Queries

Hull provides a functional, composable query API inspired by Ecto.

Building Queries

Queries are built by composing functions:

typescript
import { from, whereEq, orderBy, limit } from "hull"

const query = limit(
  orderBy(
    whereEq(from(User), "active", true),
    "created_at", "desc"
  ),
  10
)

Query Functions

from

Start a query from a schema:

typescript
const query = from(User)

where / whereEq

Filter results:

typescript
whereEq(query, "email", "[email protected]")  // email = ?
where(query, "age", ">", 18)                   // age > ?
where(query, "status", "!=", "deleted")        // status != ?

whereIn / whereNull

typescript
whereIn(query, "status", ["active", "pending"])
whereNull(query, "deleted_at")
whereNotNull(query, "verified_at")

orderBy

typescript
orderBy(query, "created_at", "desc")
orderBy(query, "name", "asc")  // asc is default

limit / offset

typescript
limit(query, 10)
offset(query, 20)

select

Select specific fields:

typescript
select(query, ["id", "email", "name"])

distinct

typescript
distinct(query)

join / leftJoin

typescript
join(query, "posts", ["users.id", "posts.user_id"])
leftJoin(query, "profiles", ["users.id", "profiles.user_id"])

Executing Queries

all

Get all matching rows:

typescript
const users = await all(repo, query)

one

Get first matching row or null:

typescript
const user = await one(repo, whereEq(from(User), "id", userId))

count

Count matching rows:

typescript
const total = await count(repo, whereEq(from(User), "active", true))

exists

Check if any rows match:

typescript
const hasUsers = await exists(repo, from(User))

Raw Queries

For complex queries, use raw SQL:

typescript
import { raw } from "hull"

const results = await raw(repo, `
  SELECT u.*, COUNT(p.id) as post_count
  FROM users u
  LEFT JOIN posts p ON p.user_id = u.id
  GROUP BY u.id
`)

With parameters:

typescript
const results = await raw<UserRow>(repo,
  `SELECT * FROM users WHERE email = $1`,
  [email]
)

Released under the MIT License.