Code Examples are created to display example queries for multiple languages. The language list is in codeGenerators.config.js. If you add or remove a language, update that file as well as the LANGUAGES array in tests/integration/run-tests.sh and the matrix.language list in .github/workflows/codegen-integration.yml.
- Create a generator class in
src/codegen/, for exampleLanguageGenerator.js. - Make it extend
CodeGeneratorand implement:get language()get outputFile()get template()- if the language doesn't use
#-style comments:get commentChars() - if dependencies are needed for running the code:
get installDependencies() - if JSON should be indented:
get indent() - if you need extra template variables:
getVariables(filters, cqlSerialized) - if you have separate templates for native/HTTP:
getTemplate(filters, cqlSerialized)
- Add the language to the
programminggroup of the locals, using the value ofget language()as the key. - Add the new generator import and class to the array in codeGenerators.config.js.
- Add template file(s) in
src/codegen/templates/— one per language, or a native + HTTP pair if the STAC client library doesn't cover all scenarios. - Validate with integration tests:
npm run test:integration
- Add an integration runtime test in
tests/integration/(for example Dockerfile anddocker-compose.yml), add the language name to theLANGUAGESarray in tests/integration/run-tests.sh and to thematrix.languagelist in .github/workflows/codegen-integration.yml.
Integration tests for code generators are run with Docker so each language snippet executes in an isolated, reproducible runtime. The test script first generates fresh snippets into tests/integration/generated/, then builds the images/services defined in tests/integration/docker-compose.yml, and finally runs each language service against a real STAC API endpoint. This verifies both that snippet generation succeeds and that the generated code actually runs in its target language environment.
If a generator implements get installDependencies(), the same command is shown in the Example Code UI and also executed by the integration test harness before running the snippet. This keeps user-facing install instructions and CI/runtime setup in sync.
Templates use __KEY__ placeholders (double-underscore delimited) that are replaced at generation time. The following built-in variables are available in every template:
| Variable | Description |
|---|---|
__CATALOG_URL__ |
The URL of the landing page of the API |
__SEARCH_URL__ |
The URL of the search endpoint |
__SEARCH_METHOD__ |
The HTTP method used for the endpoint (GET/POST) |
__RESULT_ARRAY_KEY__ |
Response array key for results (features for item search, collections for collection search) |
__FILTERS__ |
The formatted filters as JSON (without CQL wrapper objects) |
__REQUEST_URL__ |
The fully-built request URL (with query parameters for GET), constructed by Utils.addFiltersToLink() |
__REQUEST_BODY__ |
The serialized JSON body (for POST), constructed by Utils.addFiltersToLink() |
__IS_GET__ |
Boolean — true when search method is GET |
__IS_POST__ |
Boolean — true when search method is POST |
Generators can add language-specific variables by overriding getVariables(filters, cqlSerialized).
Templates support conditional blocks using comment syntax, so templates remain valid in their language. The comment characters are configured via the commentChars getter (default ## for #-comment languages, /// for //-comment languages):
## if IS_POST ##
# POST-specific code
## else ##
# GET-specific code
## endif ##/// if IS_POST ///
// POST-specific code
/// else ///
// GET-specific code
/// endif ///A block with no else is omitted entirely when the variable is falsy. The if, else, and endif tags should each be on their own line — the tag line itself is consumed and does not produce blank lines in the output.
Code examples are generated for three practical UI scenarios:
- Global item search (API Item Search)
- Collection search (API Collection Search)
- Item search scoped by selected collections
Generators should emit valid code for both item and collection endpoints. Use __RESULT_ARRAY_KEY__ in templates instead of hard-coding features.
CQL mode is determined automatically from API conformance via the Cql class's serialize() method:
- If the API supports CQL2-JSON, generated filters use
filter-lang: cql2-jsonand JSON filter bodies. - If the API supports CQL2-TEXT only, generated filters use
filter-lang: cql2-textand text expressions. - For GET requests, text format is preferred when available; JSON is used as fallback.
- For POST requests, JSON format is preferred when available; text is used as fallback.
Request preparation (query parameter encoding for GET, body construction for POST, CQL serialization) is handled by Utils.addFiltersToLink() — the same method STAC Browser uses for its own requests.
Most languages have one template file in src/codegen/templates/, using comment-based conditionals to handle GET/POST differences. This replaces the earlier convention of separate get/post/item templates per language.
- Languages that use raw HTTP (JavaScript, C#, Java) use
if IS_POSTconditionals to switch between POST and GET code paths. - Languages with a dedicated STAC client library (R/rstac, Rust/stac-rs) use separate template files for the native client path and the raw HTTP fallback (e.g.
r.r+r-http.r,rust.rs+rust-http.rs). The generator selects the appropriate template at generation time based on search context (collection search, CQL filters, etc.). - Python uses pystac-client in a single template since the library covers all search scenarios.
When a language needs context-dependent template selection, the generator can:
- Override
get template()if the decision depends only on properties available on the generator instance (e.g.this.isCollectionSearch). SeeRGeneratorfor an example. - Override
getTemplate(filters, cqlSerialized)if the decision also depends on the filter/CQL context passed during generation. SeeRustGeneratorfor an example.
The base getTemplate() method returns this.template by default.
Generated snippets should be minimal and concrete.
- Create a template in
src/codegen/templates/for your language. Use comment-based conditionals (CC if IS_POST CCwhereCCis yourcommentChars) to differentiate GET and POST code paths. - Override
get commentChars()to return the appropriate comment prefix for your language ('##'for#-based comments,'///'for//-based comments). - Use
__REQUEST_URL__in the GET branch and__REQUEST_BODY__in the POST branch — the base class handles query-parameter encoding and body construction viaUtils.addFiltersToLink(). - If a STAC client library exists for your language but doesn't cover all scenarios (e.g. collection search, CQL), create separate template files — one for the native client and one for raw HTTP — and override
get template()orgetTemplate(filters, cqlSerialized)to select the right one. - Prefer an established STAC client library for that language when one is available or generic HTTP requests when no practical STAC client library exists.
- Remove the generator from codeGenerators.config.js.
- Remove any language-specific integration files under
tests/integration/. - Validate with integration tests:
npm run test:integration
- The order in codeGenerators.config.js controls tab order in the UI.
- Snippets shown in the app are generated at runtime from the active generator.
- tests/integration/generate-snippets.js is a test helper used by
npm run test:integrationand does not need per-language edits.