tileserver-rs speaks the OGC API Features standard end-to-end. Any PostgreSQL table with a PostGIS geometry column automatically becomes an OGC collection — usable directly from QGIS, ArcGIS, FME, or any OGC client.
What you get
| Capability | Feature | Endpoint |
|---|---|---|
| Core (OGC Part 1) | List / get features as GeoJSON | GET /ogc/collections/{id}/items |
| CRS (OGC Part 2) | Reprojection on demand (PostGIS ST_Transform) | ?crs=…&bbox-crs=… |
| Filter (OGC Part 3) | CQL2 text + JSON, safe from SQL injection | ?filter=…&filter-lang=… |
| Transactions (OGC Part 4) | Create / replace / update / delete | POST, PUT, PATCH, DELETE /items[/{id}] |
| Schemas (OGC Part 5) | JSON Schema metadata | /queryables, /sortables, /schema |
Quick Start
Point tileserver-rs at a PostGIS database in config.toml:
[postgres]
connection_string = "postgresql://user:pass@localhost:5432/gis"
[[postgres.tables]]
id = "cities"
schema = "public"
table = "cities"
geometry_column = "geom"
# Opt-in: enable write operations (POST/PUT/PATCH/DELETE) for this table
writable = true
Start the server, then browse to:
http://localhost:8080/ogc # landing page
http://localhost:8080/ogc/conformance # conformance classes
http://localhost:8080/ogc/collections # collection list
Core read operations
Every PostGIS table becomes a collection with a TileJSON 3.0-ish metadata document:
curl http://localhost:8080/ogc/collections/cities/items?limit=3
Returns a GeoJSON FeatureCollection with numberMatched, numberReturned, and next/prev pagination links.
Query parameters:
bbox=minx,miny,maxx,maxy— spatial filter (4 or 6 values, per spec)limit=N— max features (default 10)offset=N— pagination offset
Coordinate reference systems
Request output in any CRS your PostGIS install supports:
# Web Mercator output
curl 'http://localhost:8080/ogc/collections/cities/items?crs=http://www.opengis.net/def/crs/EPSG/0/3857'
# British National Grid
curl 'http://localhost:8080/ogc/collections/cities/items?crs=http://www.opengis.net/def/crs/EPSG/0/27700'
Responses include a Content-Crs header so clients know what they got:
Content-Crs: <http://www.opengis.net/def/crs/EPSG/0/3857>
Collection metadata advertises supported CRSs and the storage CRS:
{
"crs": [
"http://www.opengis.net/def/crs/OGC/1.3/CRS84",
"http://www.opengis.net/def/crs/EPSG/0/4326"
],
"storageCrs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
}
Identity transforms are elided — if you request data in the storage CRS, no ST_Transform runs.
CQL2 filter expressions
Filter features with the OGC Common Query Language 2 (CQL2), in either text or JSON form.
# Text: simple equality
curl "http://localhost:8080/ogc/collections/cities/items?filter=country='Brazil'"
# Text: numeric range + boolean
curl "http://localhost:8080/ogc/collections/cities/items?filter=population%20%3E%2010000000%20AND%20is_capital%3Dtrue"
# JSON form
curl "http://localhost:8080/ogc/collections/cities/items?filter-lang=cql2-json&filter=%7B%22op%22%3A%22%3D%22%2C%22args%22%3A%5B%7B%22property%22%3A%22country%22%7D%2C%22Brazil%22%5D%7D"
Supported operators: =, <>, <, <=, >, >=, LIKE, BETWEEN … AND …, IN (…), IS NULL, AND, OR, NOT, spatial ops (S_INTERSECTS, S_WITHIN, S_CONTAINS, …).
SQL injection defence
CQL2 translates to raw PostgreSQL SQL, so tileserver-rs applies two defences:
- Property allow-list — every
Expr::Propertyin the parsed AST is checked against the table's actual column list frominformation_schema.columns. References topg_catalog.pg_shadow,information_schema.*, or any non-existing column return HTTP 400. - Round-trip canonicalisation gate — parsed CQL2 is re-serialised and compared to the input (normalising parens/whitespace/case). Any trailing garbage (
; DROP TABLE …,--comments) causes a mismatch and HTTP 400.
Write operations (CRUD)
Opt-in per table. Add writable = true to the [[postgres.tables]] entry:
# Create a feature (expects application/geo+json body)
curl -X POST http://localhost:8080/ogc/collections/cities/items \
-H 'Content-Type: application/geo+json' \
-d '{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.63,-23.55]},"properties":{"name":"São Paulo","country":"Brazil","population":12330000,"is_capital":false,"elevation_m":760}}'
# → 201 Created, Location: /ogc/collections/cities/items/18
# Full replace (PUT)
curl -X PUT http://localhost:8080/ogc/collections/cities/items/18 \
-H 'Content-Type: application/geo+json' -d '{…}'
# → 204 No Content
# Partial merge (PATCH, RFC 7396 merge-patch)
curl -X PATCH http://localhost:8080/ogc/collections/cities/items/18 \
-H 'Content-Type: application/merge-patch+json' \
-d '{"properties":{"population":99999999}}'
# → 204 No Content
# Delete
curl -X DELETE http://localhost:8080/ogc/collections/cities/items/18
# → 204 No Content
All values are bound as parameterised queries ($N placeholders) — no string interpolation of user data. Geometries go through ST_GeomFromGeoJSON.
Tables with writable = false (the default) return HTTP 405 Method Not Allowed on any mutation verb.
Schema introspection
Introspect collection structure without reading data:
# Filterable columns
curl http://localhost:8080/ogc/collections/cities/queryables
# Sortable columns (excludes jsonb + array columns that pg can't ORDER BY)
curl http://localhost:8080/ogc/collections/cities/sortables
# Full GeoJSON Feature schema ($ref to geojson.org for the geometry shape)
curl http://localhost:8080/ogc/collections/cities/schema
Returns JSON Schema 2020-12 documents with per-column types derived from information_schema.columns:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://localhost:8080/ogc/collections/cities/queryables",
"type": "object",
"properties": {
"name": { "type": "string", "title": "name" },
"country": { "type": "string", "title": "country" },
"population": { "type": ["integer", "null"], "title": "population" },
"is_capital": { "type": "boolean", "title": "is_capital" },
"founded_year":{ "type": ["integer", "null"], "title": "founded_year" },
"elevation_m": { "type": ["number", "null"], "title": "elevation_m" }
}
}
Using with QGIS / ArcGIS / FME
Any OGC Features-compliant client can connect to /ogc:
- QGIS: Browser panel → WFS / OGC API - Features → add
http://localhost:8080/ogc - ArcGIS Pro: Catalog → Connections → OGC API Features →
http://localhost:8080/ogc - FME:
OGCAPIFeaturesConnectorreader, base URLhttp://localhost:8080/ogc
QGIS and ArcGIS natively round-trip the feature editing workflow when writable = true — users draw/edit features in the GIS client and tileserver-rs applies the changes via PATCH/DELETE.
Interactive API reference
Scalar docs render all OGC endpoints with try-it-now interactivity at:
http://localhost:8080/_openapi
Specifications
| Spec | Link |
|---|---|
| Part 1 – Core | OGC 17-069r4 |
| Part 2 – CRS | OGC 18-058 |
| Part 3 – Filter | OGC 19-079r2 |
| Part 4 – Create-Replace-Delete | OGC 20-002 |
| Part 5 – Schemas | OGC 23-058 |
| CQL2 | OGC 21-065 |