Send a push notification (Gleam)
Send a push notification via thinwrap_notifications's send_push/2 in Gleam — one call shape for FCM, APNs, Expo and every other push provider.
1. Install
gleam add thinwrap_notifications gleam_erlang envoy2. Send the call
import envoy
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode
import gleam/erlang/atom
import gleam/io
import gleam/list
@external(erlang, "thinwrap_notifications", "send_push")
fn send_push(config: Dynamic, input: Dynamic) -> Result(Dynamic, Dynamic)
pub fn send(device_token: String) -> Nil {
let assert Ok(project_id) = envoy.get("FCM_PROJECT_ID")
let assert Ok(client_email) = envoy.get("FCM_CLIENT_EMAIL")
let assert Ok(private_key) = envoy.get("FCM_PRIVATE_KEY")
let config =
obj([
#("provider", atom("fcm")),
#("project_id", dynamic.string(project_id)),
#("client_email", dynamic.string(client_email)),
#("private_key", dynamic.string(private_key)),
])
let input =
obj([
#("to", dynamic.string(device_token)),
#("title", dynamic.string("Hello")),
#("body", dynamic.string("Hello from Thinwrap")),
#(
"data",
dynamic.properties([
#(dynamic.string("deepLink"), dynamic.string("/orders/42")),
]),
),
])
case send_push(config, input) {
Ok(res) -> {
let sent = {
use status <- decode.field(key("status"), atom.decoder())
use id <- decode.field(key("provider_message_id"), decode.string)
decode.success(atom.to_string(status) <> " " <> id)
}
let assert Ok(line) = decode.run(res, sent)
io.println(line)
}
Error(err) -> io.println(describe(err))
}
}
// The library speaks atom-keyed Erlang maps; these helpers build and read them.
fn key(name: String) -> atom.Atom {
atom.create(name)
}
fn atom(name: String) -> Dynamic {
atom.to_dynamic(atom.create(name))
}
fn obj(fields: List(#(String, Dynamic))) -> Dynamic {
dynamic.properties(list.map(fields, fn(f) { #(atom(f.0), f.1) }))
}
fn describe(err: Dynamic) -> String {
let error = {
use code <- decode.field(key("code"), atom.decoder())
use message <- decode.field(key("message"), decode.string)
decode.success(atom.to_string(code) <> ": " <> message)
}
let assert Ok(line) = decode.run(err, error)
line
}
Same code works for all supported providers — just change the provider ID.