Hi all, I'm learning roc by building an app. I just wrote our this code to get something working
# https://github.com/roc-lang/basic-webserver/tree/main/examples
# https://roc-lang.github.io/basic-webserver/SQLite3/
module [init, Config]
import pf.Task
import pf.SQLite3
import KeyValueStore
import Logger exposing [Logger]
Config : {
sqlitePath : Str,
logger : Logger,
}
get : Config -> (Str -> Task.Task Str [NotFound, Errored Str])
get = \config -> \key ->
executed <-
SQLite3.execute {
path: config.sqlitePath,
query: "SELECT key, value FROM key_value WHERE key = :key;",
bindings: [{ name: ":key", value: key }],
}
|> Task.attempt
when executed is
Err err -> Task.err (Errored (SQLite3.errToStr err))
Ok rows ->
firstRow = List.first rows
when firstRow is
Err _ -> Task.err NotFound
Ok row ->
firstColumn = List.first row
when firstColumn is
Err _ -> Task.err (Errored "No value")
Ok columnValue ->
when columnValue is
String columnValueStr -> Task.ok columnValueStr
_ -> Task.err (Errored "No value")
I'm now going to refactor this code to make it flat. I'm curious how any you guys would refactor this code? Is there a single right way or multiple right ways?
Maybe something like this... I haven't tested it as it's only a module.
get : Config -> (Str -> Task.Task Str [NotFound, Errored Str])
get = \config -> \key ->
rows =
SQLite3.execute {
path: config.sqlitePath,
query: "SELECT key, value FROM key_value WHERE key = :key;",
bindings: [{ name: ":key", value: key }],
}
|> Task.result!
when rows is
Ok [] -> Task.err NotFound
Ok [[String _, String value]] -> Task.ok value
Ok _ -> Task.err (Errored "unexpected values returned, expected key and value")
Err (SQLError _ msg) -> Task.err (Errored msg)
edits: some fixes
It's very similar to the text editor example I just posted
Also, note the API for Sqlite will hopefully change soon with some awesome new improvements Brendan has made. If you're interested to see what that looks like here is the upgrade for the roc-htmx-tailwindcss-demo app I've been using to test that upgrade.
It uses the new record builders to decode each row into a record, so you can can easily get a List User
or whatever you're expecting.
That refactor is clean. I'm gonna use that. Thanks! The upcoming sqlite API looks very clean too. For the demo app, what are you guys using for db migrations? I've been using https://github.com/amacneil/dbmate.
One other change. Why select key
when it isn't being used
Just drop it from the sql query
And in the new API will just be queryOne
Last updated: Jul 06 2025 at 12:14 UTC