Skip to content

Commit 5804dc0

Browse files
committed
Example of using PG ranges
Signed-off-by: itowlson <[email protected]>
1 parent 978a1b3 commit 5804dc0

File tree

8 files changed

+136
-0
lines changed

8 files changed

+136
-0
lines changed

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ members = [
5959
"examples/mysql",
6060
"examples/postgres",
6161
"examples/postgres-v3",
62+
"examples/postgres-v4",
6263
"examples/redis-outbound",
6364
"examples/mqtt-outbound",
6465
"examples/variables",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
target = "wasm32-wasip1"

examples/postgres-v4/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "rust-outbound-pg-v4"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
crate-type = ["cdylib"]
8+
9+
[dependencies]
10+
# Useful crate to handle errors.
11+
anyhow = "1"
12+
# General-purpose crate with common HTTP types.
13+
http = "1.0.0"
14+
# The Spin SDK.
15+
spin-sdk = { path = "../.." }

examples/postgres-v4/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Spin Outbound PostgreSQL example
2+
3+
This example shows how to access a PostgreSQL database from a Spin component.
4+
It shows the new PostgreSQL range support in the v4 interface.
5+
6+
## Prerequisite: Postgres
7+
8+
This example assumes postgres is running and accessible locally via its standard 5432 port.
9+
10+
We suggest running the `postgres` Docker container which has the necessary postgres user permissions
11+
already configured. For example:
12+
13+
```
14+
docker run --rm -h 127.0.0.1 -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres
15+
```
16+
17+
## Spin up
18+
19+
Then, run the following from the root of this example:
20+
21+
```
22+
createdb -h localhost -U postgres spin_dev
23+
psql -h localhost -U postgres -d spin_dev -f db/testdata.sql
24+
spin build --up
25+
```
26+
27+
Curl with a year between 2005 and today as the path:
28+
29+
```
30+
$ curl -i localhost:3000/2016
31+
HTTP/1.1 200 OK
32+
transfer-encoding: chunked
33+
date: Mon, 18 Aug 2025 05:02:29 GMT
34+
35+
Splodge and Fang and Kiki and Slats
36+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CREATE TABLE cats (
2+
name text not null,
3+
reign int4range not null
4+
);
5+
6+
INSERT INTO cats (name, reign) VALUES
7+
('Smoke', '[2005, 2013]'::int4range),
8+
('Splodge', '[2005, 2019]'::int4range),
9+
('Fang', '[2005, 2016]'::int4range),
10+
('Kiki', '[2005, 2020]'::int4range),
11+
('Slats', '[2005, 2021]'::int4range),
12+
('Rosie', '[2021,)'::int4range),
13+
('Hobbes', '[2021,)'::int4range)
14+
;

examples/postgres-v4/spin.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
spin_manifest_version = 2
2+
3+
[application]
4+
authors = ["Fermyon Engineering <[email protected]>"]
5+
name = "rust-outbound-pg-v4-example"
6+
version = "0.1.0"
7+
8+
[[trigger.http]]
9+
route = "/:year"
10+
component = "outbound-pg"
11+
12+
[component.outbound-pg]
13+
environment = { DB_URL = "host=localhost user=postgres dbname=spin_dev" }
14+
source = "../../target/wasm32-wasip1/release/rust_outbound_pg_v4.wasm"
15+
allowed_outbound_hosts = ["postgres://localhost"]
16+
[component.outbound-pg.build]
17+
command = "cargo build --target wasm32-wasip1 --release"

examples/postgres-v4/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![allow(dead_code)]
2+
use anyhow::Result;
3+
use http::{Request, Response};
4+
use spin_sdk::{http_component, pg3, pg3::Decode};
5+
6+
// The environment variable set in `spin.toml` that points to the
7+
// address of the Pg server that the component will write to
8+
const DB_URL_ENV: &str = "DB_URL";
9+
10+
#[http_component]
11+
fn process(req: Request<()>) -> Result<Response<String>> {
12+
let address = std::env::var(DB_URL_ENV)?;
13+
let conn = pg3::Connection::open(&address)?;
14+
15+
let year_header = req
16+
.headers()
17+
.get("spin-path-match-year")
18+
.map(|hv| hv.to_str())
19+
.transpose()?
20+
.unwrap_or("2025");
21+
let year: i32 = year_header.parse()?;
22+
23+
// Due to an ambiguity in the PostgreSQL `<@` operator syntax, we MUST qualify
24+
// the year as an int4 rather than an int4range in the query.
25+
let rulers = conn.query(
26+
"SELECT name FROM cats WHERE $1::int4 <@ reign",
27+
&[year.into()],
28+
)?;
29+
30+
let response = if rulers.rows.is_empty() {
31+
"it was anarchy".to_owned()
32+
} else {
33+
let ruler_names = rulers
34+
.rows
35+
.into_iter()
36+
.map(|r| Decode::decode(&r[0]))
37+
.collect::<Result<Vec<String>, _>>()?;
38+
ruler_names.join(" and ")
39+
};
40+
41+
Ok(http::Response::builder().body(format!("{response}\n"))?)
42+
}

0 commit comments

Comments
 (0)