d1_gleamflare

Hex Package Hex Docs License Gleam Target

A modern, type-safe Gleam library to integrate with the Cloudflare D1 REST API. Specifically built to target the Erlang VM (BEAM) using gleam_httpc.

Installation

Add d1_gleamflare to your gleam.toml dependencies:

[dependencies]
d1_gleamflare = ">= 1.0.0"

Quick Start

1. Initialize the Client

To interact with the Cloudflare API, you need your Cloudflare Account ID and a Cloudflare API Token with permissions to access D1.

import d1_gleamflare

let client = d1_gleamflare.new("your-account-id", "your-api-token")

2. Manage Databases (Account-Level)

Create, list, or retrieve metadata for your databases:

// List all databases
let assert Ok(databases) = d1_gleamflare.list_databases(client)

// Create a new database
let assert Ok(db_info) = d1_gleamflare.create_database(client, "my-production-db")
io.println("Created database: " <> db_info.uuid)

// Get database metadata by UUID
let assert Ok(db_info) = d1_gleamflare.get_database(client, "some-database-uuid")

3. Querying a Database

To query a database, instantiate a Database record using d1_gleamflare.database, then use the fluent, database-centric functions.

import d1_gleamflare/database
import gleam/json
import gleam/dynamic

let db = d1_gleamflare.database(client, "database-uuid", "database-name")

Run Parameterized Queries with Custom Decoders

It is highly recommended to decode query results directly into typed structures:

type User {
  User(id: Int, name: String, email: String)
}

fn user_decoder() {
  dynamic.decode3(
    User,
    dynamic.field("id", dynamic.int),
    dynamic.field("name", dynamic.string),
    dynamic.field("email", dynamic.string),
  )
}

// Execute SELECT query
let sql = "SELECT id, name, email FROM users WHERE active = ? AND age >= ?"
let params = [json.bool(True), json.int(21)]

let assert Ok(query_result) = 
  db
  |> database.query(sql, params, user_decoder())

// query_result.results has type List(User)
let users = query_result.results

Run Dynamic Queries

If you don’t want to use a specific decoder, you can retrieve results as raw Dynamic values:

let assert Ok(query_result) = 
  db
  |> database.query_dynamic("SELECT * FROM settings", [])

// query_result.results has type List(dynamic.Dynamic)

Run Raw (Performance-Optimized) Queries

The /raw endpoint is optimized for performance by returning arrays of values rather than objects with keys:

let assert Ok(query_result) = 
  db
  |> database.raw_query("SELECT id, name FROM users", [])

// Each row is returned as a List(Dynamic)
// query_result.results has type List(List(dynamic.Dynamic))

Run Batch Queries

You can execute multiple SQL queries in a single round-trip:

import d1_gleamflare/types.{Query}

let batch_queries = [
  Query(
    "INSERT INTO logs (message, level) VALUES (?, ?)", 
    [json.string("Server started"), json.string("INFO")]
  ),
  Query(
    "UPDATE stats SET value = value + 1 WHERE key = ?", 
    [json.string("connections")]
  ),
]

let assert Ok(batch_results) = 
  db
  |> database.batch(batch_queries)

// batch_results has type List(QueryResult(dynamic.Dynamic))

Delete a Database

let assert Ok(Nil) = database.delete(db)

Error Handling

All actions return a Result(t, types.Error). The library maps connection, HTTP, JSON decoding, and Cloudflare API-level errors cleanly:

import d1_gleamflare/types

case d1_gleamflare.list_databases(client) {
  Ok(databases) -> // Use databases...
  Error(types.ApiError(errors)) -> // Cloudflare API-level errors (e.g. code: 10001, message: "Authentication failed")
  Error(types.HttpError(status, body)) -> // Non-2xx HTTP responses without standard envelope
  Error(types.JsonError(reason)) -> // Malformed JSON or type mismatch during decoding
  Error(types.NetworkError(reason)) -> // TCP or TLS connection issues
}
Search Document