Docs/Tools & reference

Ecosystem packages

Install the packages that connect a Valk program to databases, caches and file formats.

On this page

Packages are installed per project with vman and imported by the name they were installed under. The standard library covers HTTP, files, JSON, crypto and the rest; these packages cover what talks to the world outside your program: databases, caches, tokens and file formats.

Terminal
vman install github.com/ctxcode/valk-redis

That writes the dependency into valk.json, copies it into vendor/, and the program then uses it by name:

Valk
use redis

Browse vpkg.dev for the full index, where each package lists its versions and the lowest compiler version each one needs. See Projects & packages for how dependencies, versions and valk.json work.

Databases#

Package What it is
valk-sql One API over all three: query builder, migrations, pooling, rows read into your own classes
valk-sqlite SQLite, bound to the C library, so the database is a file inside your program
valk-postgres PostgreSQL, written in Valk, with prepared and cached statements
valk-mysql MySQL, written in Valk

Each driver works on its own, and each one also plugs into valk-sql, which is where the parts every program writes again otherwise already live. Install the driver you use together with it:

Terminal
vman install github.com/ctxcode/valk-sql
vman install github.com/ctxcode/valk-sqlite
Valk
use sql
use sqlite

let db = sqlite.database(sqlite.open("app.db") ! panic("%{E.message}"))
defer db.close()

// Statements are written with `?` whatever the database is; Postgres gets $1, $2 on the way out
db.exec("INSERT INTO users (name, age) VALUES (?, ?)", .{ sql.Value.of("Ada"), sql.Value.of_int(36) }) ! panic("%{E.message}")

let rows = sql.Query.select()
    .on(db)
    .from("users")
    .where("age > ?", .{ sql.Value.of_int(18) })
    .all() ! panic("%{E.message}")

Moving to another database is then a matter of the line that opens it. What differs between them — placeholders, RETURNING, LIMIT/OFFSET, the id of an insert — is what valk-sql smooths over.

Caches and queues#

valk-redis speaks RESP to Redis and Valkey, with pipelines, transactions, scripts, pub/sub, streams, TLS, pools and Sentinel:

Valk
use redis

let con = redis.connect("127.0.0.1", 6379) ! panic("%{E.message}")
defer con.close()

con.set("greeting", "hello", 60) ! panic("%{E.message}")    // with a 60 second lifetime
let visits = con.incr("visits") ! panic("%{E.message}")
let job = con.blpop("jobs", 5) ! panic("%{E.message}")      // waits up to five seconds

Web services#

Package What it is
valk-jwt JSON Web Tokens: HS256, HS384 and HS512, with decoding that verifies before it believes
valk-uuid UUIDs: random (v4), time-ordered (v7) and name based (v5 and v3)
Valk
use jwt
use uuid

let id = uuid.v7()                  // time-ordered, so it sorts by when it was made
let token = jwt.encode_of(Session { sub: id.to_string() }, secret, jwt.Algorithm.hs256, 3600)
let session = jwt.decode_to[Session](token, secret) ! panic("%{E.message}")

The algorithm is taken from the caller, never from the token, so a token that claims alg: none is refused rather than believed.

File formats#

Package What it is
valk-toml TOML 1.0, decoded into your own classes or a json.Value, and encoded back
valk-image PNG, JPEG, WebP and BMP: reading, writing, resizing, cropping, flipping and rotating
Valk
use toml
use valk.fs

let settings = toml.decode_to[Settings](fs.read("config.toml") !!) ! panic("%{E.message} at line %{E.line}")

Tooling#

valk-cli turns Array[String] into commands, options and arguments, writes the help text, and prints the completion script for bash, zsh and fish:

Valk
use cli

fn main(args: Array[String]) {
    let app = cli.App.new("greet", "Greet someone", "1.0.0")
    app.root.flag("loud", "l", "Greet in capitals")
    app.root.argument("name", "Who to greet", true)
    app.root.on_run(fn(ctx: cli.Context) i32 !cli.Error {
        let line = "Hello " + ctx.arg("name") + "!"
        println(ctx.flag("loud") ? line.upper() : line)
        return 0
    })
    exit(app.run(args))
}

vman itself is written with it, which is where its vman completion bash comes from.

A program that uses them together#

valk-starter-api is a small JSON API built out of these parts: valk.http for the server, valk-sql with valk-sqlite for storage and migrations, valk-redis for rate limiting, and valk-jwt with valk-uuid for sessions. It is a working project to read rather than a package to install, and its README explains the few decisions that are easy to get wrong the first time, such as opening connections per thread.