Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,19 @@ Stac-server is a production-ready implementation of the [STAC API specification]
Get started with Docker Compose for local development:

```bash
# Clone the repository
# Clone and install
git clone https://github.com/stac-utils/stac-server.git
cd stac-server
npm install

# Start services
# Start OpenSearch and LocalStack
docker compose up -d

# Ingest sample data
npm run ingest:example
# Start the STAC API on http://localhost:3000 (leave this running)
npm run serve

# Test the API
# In a separate terminal: ingest sample data, then query the API
npm run ingest:example
curl http://localhost:3000/
```

Expand Down
66 changes: 66 additions & 0 deletions bin/ingest-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env node
// Ingest the example collection and item into a locally running stac-server
// via the Transaction extension. Requires the API to be running (npm run serve)
// with ENABLE_TRANSACTIONS_EXTENSION=true (which `npm run serve` sets).

import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'node:path'

const API = (process.env.STAC_API_URL || 'http://localhost:3000').replace(/\/$/, '')
const examplesDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'examples')

const loadJson = async (file) =>
JSON.parse(await readFile(path.join(examplesDir, file), 'utf8'))

const post = async (url, body, label, { tolerate404 = false } = {}) => {
let res
try {
res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
} catch {
console.error(`\nCould not reach the STAC API at ${API}.`)
console.error('Make sure the services and API are running:')
console.error(' docker compose up -d # OpenSearch + LocalStack')
console.error(' npm run serve # STAC API on :3000 (in a separate terminal)\n')
process.exit(1)
}
// 409 = already exists, fine for a re-runnable example. 404 may be transient
// while a newly created collection becomes searchable; the caller retries.
if (res.status === 404 && tolerate404) return res.status
if (!res.ok && res.status !== 409) {
console.error(`Failed to ${label}: ${res.status} ${res.statusText}`)
console.error(await res.text())
process.exit(1)
}
const note = res.status === 409 ? ' (already exists)' : ''
console.log(` ${label}: ${res.status}${note}`)
return res.status
}

const sleep = (ms) => new Promise((resolve) => { setTimeout(resolve, ms) })

const collection = await loadJson('sample-collection.json')
const item = await loadJson('sample-item.json')

console.log(`Ingesting example data into ${API}`)
await post(`${API}/collections`, collection, `create collection '${collection.id}'`)

// A newly created collection isn't immediately searchable (OpenSearch refreshes
// ~1s after a write), and the item endpoint validates the collection exists, so
// retry a few times on 404 before giving up.
const itemsUrl = `${API}/collections/${collection.id}/items`
const label = `create item '${item.id}'`
/* eslint-disable no-await-in-loop -- sequential retry is intentional */
for (let attempt = 1; ; attempt += 1) {
const status = await post(itemsUrl, item, label, { tolerate404: attempt < 10 })
if (status !== 404) break
await sleep(1000)
}
/* eslint-enable no-await-in-loop */

console.log('\nDone. Try:')
console.log(` curl '${API}/search?collections=${collection.id}'`)
25 changes: 17 additions & 8 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,28 @@ Get STAC Server running with sample data in 5 minutes.
## Step 1: Start the Services

```bash
# Clone the repository
# Clone and install
git clone https://github.com/stac-utils/stac-server.git
cd stac-server
npm install

# Start services
# Start OpenSearch and LocalStack
docker compose up -d

# Wait for services to be ready (about 30 seconds)
sleep 30

# Start the STAC API (leave this running in its own terminal)
npm run serve
```

This starts OpenSearch and the STAC API on `http://localhost:3000`.
`docker compose` starts OpenSearch and LocalStack; `npm run serve` runs the STAC API
on `http://localhost:3000` with the Transaction extension enabled (so the steps below
can create collections and items).
Comment on lines +29 to +31

> **Tip:** Once the API is running, you can load a ready-made sample collection and item
> in one step with `npm run ingest:example`, then skip to [Step 5](#step-5-search-for-items).
> The steps below walk through doing it manually.

## Step 2: Verify the API

Expand Down Expand Up @@ -177,11 +187,10 @@ Wait for "Node started" message.

### Transaction extension errors

The Transaction extension is enabled by default in Docker Compose. To disable:

```bash
ENABLE_TRANSACTIONS_EXTENSION=false docker compose up
```
The Transaction extension (required for the `POST` steps and `npm run ingest:example`
above) is enabled by `npm run serve`. If you run the API another way and those
requests return 404, make sure `ENABLE_TRANSACTIONS_EXTENSION=true` is set for the
server process.

## Next Steps

Expand Down
16 changes: 16 additions & 0 deletions examples/sample-collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "sample-collection",
"type": "Collection",
"stac_version": "1.0.0",
"description": "A sample collection",
"license": "proprietary",
"extent": {
"spatial": {
"bbox": [[-180, -90, 180, 90]]
},
"temporal": {
"interval": [["2020-01-01T00:00:00Z", null]]
}
},
"links": []
}
23 changes: 23 additions & 0 deletions examples/sample-item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"type": "Feature",
"stac_version": "1.0.0",
"id": "sample-item",
"collection": "sample-collection",
"geometry": {
"type": "Point",
"coordinates": [-122.4, 37.8]
},
"bbox": [-122.4, 37.8, -122.4, 37.8],
"properties": {
"datetime": "2024-01-15T10:30:00Z",
"title": "Sample Item"
},
"assets": {
"thumbnail": {
"href": "https://example.com/thumbnail.jpg",
"type": "image/jpeg",
"title": "Thumbnail"
}
},
"links": []
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"package": "sls package",
"serve": "REQUEST_LOGGING_FORMAT=dev LOG_LEVEL=debug STAC_API_URL=http://localhost:3000 ENABLE_TRANSACTIONS_EXTENSION=true nodemon --exec node --import=tsx ./src/lambdas/api/local.ts",
"build-api-docs": "npx @redocly/cli build-docs src/lambdas/api/openapi.yaml -o ./docs/api-spec.html",
"prepare": "husky"
"prepare": "husky",
"ingest:example": "node bin/ingest-example.js"
},
"ava": {
"verbose": true,
Expand Down