Current section
Files
Jump to
Current section
Files
src/example/queries.gleam
import gleam/dynamic/decode
import gleam/result
import gloo/error.{type GlooError}
import gloo/query
import gloo/repo.{type Repo}
import gloo/sql
import example/schema
// ── query builder usage (I.query) ─────────────────────────────────────────
pub fn find_user_by_email(
r: Repo,
email: String,
) -> Result(schema.User, GlooError) {
query.from(schema.users())
|> query.where(query.Eq("email", sql.string(email)))
|> repo.query_one(r, _)
}
pub fn find_posts_for_user(
r: Repo,
user_id: Int,
) -> Result(List(schema.Post), GlooError) {
query.from(schema.posts())
|> query.where(query.Eq("user_id", sql.int(user_id)))
|> query.order_by("inserted_at", query.Desc)
|> repo.query_all(r, _)
}
pub fn create_user(
r: Repo,
email: String,
name: String,
) -> Result(schema.User, GlooError) {
query.insert(query.from(schema.users()), schema.users(), [
#("email", sql.string(email)),
#("name", sql.string(name)),
])
|> query.returning_columns(["id", "email", "name"])
|> query.returning(schema.users().decoder)
|> repo.query_one(r, _)
}
pub fn create_post(
r: Repo,
user_id: Int,
body: String,
) -> Result(schema.Post, GlooError) {
query.insert(query.from(schema.posts()), schema.posts(), [
#("user_id", sql.int(user_id)),
#("body", sql.string(body)),
])
|> query.returning_columns(["id", "user_id", "body"])
|> query.returning(schema.posts().decoder)
|> repo.query_one(r, _)
}
pub fn follow_user(
r: Repo,
follower_id: Int,
followee_id: Int,
) -> Result(Int, GlooError) {
query.insert(query.from(schema.follows()), schema.follows(), [
#("follower_id", sql.int(follower_id)),
#("followee_id", sql.int(followee_id)),
])
|> repo.query_execute(r, _)
}
// ── raw sql module usage (I.sql) ───────────────────────────────────────────
pub type FeedPost {
FeedPost(post_id: Int, author_name: String, body: String)
}
pub fn feed_for_user(
r: Repo,
user_id: Int,
limit: Int,
) -> Result(List(FeedPost), GlooError) {
let decoder = {
use post_id <- decode.field(0, decode.int)
use author_name <- decode.field(1, decode.string)
use body <- decode.field(2, decode.string)
decode.success(FeedPost(post_id:, author_name:, body:))
}
sql.query(
"SELECT p.id, u.name, p.body
FROM posts p
JOIN users u ON u.id = p.user_id
WHERE p.user_id IN (
SELECT followee_id FROM follows WHERE follower_id = $1
)
ORDER BY p.inserted_at DESC
LIMIT $2",
)
|> sql.param(sql.int(user_id))
|> sql.param(sql.int(limit))
|> sql.returns(decoder)
|> repo.sql_all(r, _)
}
pub fn delete_user_posts(
r: Repo,
user_id: Int,
) -> Result(Int, GlooError) {
sql.query("DELETE FROM posts WHERE user_id = $1")
|> sql.param(sql.int(user_id))
|> repo.sql_execute(r, _)
}
// ── transaction usage (V5, V6) ─────────────────────────────────────────────
pub fn delete_user_and_posts(
r: Repo,
user_id: Int,
) -> Result(Nil, GlooError) {
repo.transaction(r, fn(tx) {
use _ <- result.try(delete_user_posts(tx, user_id))
use _ <- result.try(
query.from(schema.follows())
|> query.delete
|> query.where(
query.Or([
query.Eq("follower_id", sql.int(user_id)),
query.Eq("followee_id", sql.int(user_id)),
]),
)
|> repo.query_execute(tx, _),
)
use _ <- result.try(
repo.execute(tx, "DELETE FROM users WHERE id = $1", [sql.int(user_id)]),
)
Ok(Nil)
})
}