SPARQL Protocol (HTTP Endpoint)

pg_ripple v0.15.0 ships with a companion HTTP service (pg_ripple_http) that implements the W3C SPARQL 1.1 Protocol. Any standard SPARQL client — YASGUI, Protégé, SPARQLWrapper, Jena, or plain curl — can query pg_ripple without driver-specific configuration.


Architecture

pg_ripple_http is a standalone Rust binary built with axum and tokio. It connects to PostgreSQL via deadpool-postgres, translates HTTP requests into calls to pg_ripple.sparql(), sparql_ask(), sparql_construct(), sparql_describe(), and sparql_update(), then formats the results according to the requested content type.

┌────────────┐    HTTP     ┌──────────────────┐    SQL/SPI    ┌────────────┐
│   Client   │ ──────────► │  pg_ripple_http   │ ────────────► │ PostgreSQL │
│  (YASGUI,  │ ◄────────── │  (axum + tokio)   │ ◄──────────── │ + pg_ripple│
│  curl, …)  │   JSON/XML  └──────────────────┘               └────────────┘
└────────────┘

Quick start with Docker Compose

The easiest way to run both PostgreSQL and the HTTP endpoint:

docker compose up

This starts two containers:

ServicePortDescription
postgres5432PostgreSQL 18 with pg_ripple installed
sparql7878HTTP endpoint for SPARQL queries

Once running:

# Health check
curl http://localhost:7878/health

# Run a SPARQL query
curl -G http://localhost:7878/sparql \
  --data-urlencode "query=SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"

Endpoints

GET /sparql

Query via URL parameter. For simple queries and browser-based tools.

GET /sparql?query=SELECT+?s+?p+?o+WHERE+{+?s+?p+?o+}+LIMIT+10

POST /sparql

Query via request body. Supports two content types:

Content-TypeBody format
application/sparql-queryRaw SPARQL query text
application/x-www-form-urlencodedquery=... (URL-encoded)
# Raw SPARQL body
curl -X POST http://localhost:7878/sparql \
  -H "Content-Type: application/sparql-query" \
  -d "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"

# Form-encoded
curl -X POST http://localhost:7878/sparql \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "query=SELECT+?s+?p+?o+WHERE+{+?s+?p+?o+}+LIMIT+10"

POST /sparql (Update)

SPARQL Update operations use Content-Type: application/sparql-update:

curl -X POST http://localhost:7878/sparql \
  -H "Content-Type: application/sparql-update" \
  -d "INSERT DATA { <http://example.org/alice> <http://example.org/name> \"Alice\" }"

GET /health

Returns 200 OK when the service is ready. Use for load balancer health checks.

GET /metrics

Prometheus-compatible metrics:

MetricDescription
pg_ripple_query_countTotal SPARQL queries processed
pg_ripple_error_countTotal query errors
pg_ripple_query_duration_seconds_totalCumulative query execution time
curl http://localhost:7878/metrics

Content negotiation

Set the Accept header to choose the response format:

Accept headerFormatSuitable for
application/sparql-results+jsonSPARQL Results JSONJavaScript apps, YASGUI
application/sparql-results+xmlSPARQL Results XMLJava/Jena clients
text/csvCSVSpreadsheets, pandas
text/tab-separated-valuesTSVCLI pipelines
text/turtleTurtleCONSTRUCT/DESCRIBE results
application/n-triplesN-TriplesStreaming pipelines
application/ld+jsonJSON-LDREST APIs, Linked Data Platform

If no Accept header is set, the default is application/sparql-results+json for SELECT/ASK and text/turtle for CONSTRUCT/DESCRIBE.

# Get results as CSV
curl -G http://localhost:7878/sparql \
  -H "Accept: text/csv" \
  --data-urlencode "query=SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 5"

# Get CONSTRUCT results as JSON-LD
curl -G http://localhost:7878/sparql \
  -H "Accept: application/ld+json" \
  --data-urlencode "query=CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 5"

Authentication

pg_ripple_http supports Bearer token and HTTP Basic authentication. Configure via environment variables:

VariableDescription
PG_RIPPLE_HTTP_AUTH_TOKENIf set, all requests must include Authorization: Bearer <token> or Authorization: Basic <token>

When not set, authentication is disabled.

# Start with bearer token auth
PG_RIPPLE_HTTP_AUTH_TOKEN=my-secret-token ./pg_ripple_http

# Query with auth
curl -G http://localhost:7878/sparql \
  -H "Authorization: Bearer my-secret-token" \
  --data-urlencode "query=SELECT * WHERE { ?s ?p ?o } LIMIT 5"

CORS

Cross-Origin Resource Sharing headers are enabled by default via tower-http, allowing browser-based tools like YASGUI to query the endpoint directly.


Configuration

All configuration is via environment variables:

VariableDefaultDescription
PG_RIPPLE_HTTP_PG_URLpostgresql://localhost/postgresFull PostgreSQL connection URL
PG_RIPPLE_HTTP_PORT7878HTTP listening port
PG_RIPPLE_HTTP_POOL_SIZE16Maximum connections in the pool
PG_RIPPLE_HTTP_AUTH_TOKEN(unset)Bearer/Basic auth token
PG_RIPPLE_HTTP_RATE_LIMIT0Max requests/sec per client IP (0 = disabled)
PG_RIPPLE_HTTP_CORS_ORIGINS*Comma-separated allowed CORS origins

Example:

export PG_RIPPLE_HTTP_PG_URL="postgresql://user:password@db-host:5432/mydb"
export PG_RIPPLE_HTTP_PORT=8080
export PG_RIPPLE_HTTP_POOL_SIZE=32
export PG_RIPPLE_HTTP_AUTH_TOKEN="my-secret-token"
./pg_ripple_http

Client examples

Python (SPARQLWrapper)

from SPARQLWrapper import SPARQLWrapper, JSON

sparql = SPARQLWrapper("http://localhost:7878/sparql")
sparql.setQuery("SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10")
sparql.setReturnFormat(JSON)
results = sparql.query().convert()

for row in results["results"]["bindings"]:
    print(row["s"]["value"], row["p"]["value"], row["o"]["value"])

JavaScript (fetch)

const response = await fetch('http://localhost:7878/sparql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/sparql-query',
    'Accept': 'application/sparql-results+json'
  },
  body: 'SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10'
});
const data = await response.json();

curl

# SELECT
curl -G http://localhost:7878/sparql \
  --data-urlencode "query=SELECT ?s WHERE { ?s a <http://xmlns.com/foaf/0.1/Person> }"

# ASK
curl -G http://localhost:7878/sparql \
  --data-urlencode "query=ASK { <http://example.org/alice> ?p ?o }"

# CONSTRUCT as Turtle
curl -G http://localhost:7878/sparql \
  -H "Accept: text/turtle" \
  --data-urlencode "query=CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 100"

Apache Jena (Java)

QueryExecution qe = QueryExecution.service("http://localhost:7878/sparql")
    .query("SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10")
    .build();
ResultSet rs = qe.execSelect();
ResultSetFormatter.out(rs);