# Koop turns any spatial API into an ArcGIS feature service

> Koop is a JavaScript toolkit and Node web server that translates geospatial data between formats on the fly, so a dataset kept in its native form can be served as GeoServices for ArcGIS, vector tiles, WMS or GeoJSON through a plugin architecture. It suits a GIS team publishing existing sources to ArcGIS clients, and it needs a provider plugin for every data source.

**koopjs/koop** — Transform, query, and download geospatial data on the web.

- Repository: https://github.com/koopjs/koop
- Website: http://koopjs.github.io
- Stars: 713 · Forks: 135
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/koopjs-koop

## The idea is a translation layer, not a data store

Koop is a JavaScript toolkit for making requests to spatial APIs, and it exposes a Node.js web server that facilitates on-the-fly transformations of geospatial data from one format to another and delivers it to clients by HTTP. The phrasing in the README is the clearest summary of the design. Koop allows you to keep your data in its native format, while making it accessible in any format required. That is the whole value proposition. A team with data in a departmental system, a REST service or a set of files can expose it to ArcGIS clients without building a data pipeline, duplicating the dataset, or writing a translation layer by hand. Out of the box, Koop translates data into the GeoServices specification supported by ArcGIS products, and the plugin architecture supports output in other formats including vector-tile, WMS, and plain old GeoJSON. The output is an HTTP API that behaves like a spatial service, and the data never leaves the system that already holds it.

## Providers read, outputs write, and GeoJSON is the pivot

The plugin architecture has two kinds of plugin and a common intermediate format. Provider plugins connect to novel data formats and translate to a common format, which is GeoJSON, while output plugins then transform that GeoJSON into other specifications. The repository links a list of plugins already authored and maintained, and the development section of the docs covers how to write new ones if you have a novel datasource or require an as-yet unsupported output format. The two-stage design is what makes the toolkit extensible rather than merely configurable. A new data source only has to produce GeoJSON, and every output plugin you have installed then works against it for free. It also means the conversion cost sits in one place, which is where you look when a result is wrong. The demo in the repository shows the pairing, using the file-geojson data provider with the GeoServices output plugin, which is the smallest possible illustration of the whole model.

## The demo is a working API with ArcGIS style routes

The repository ships with a demo that shows Koop's support for GeoServices, and the console output when it starts lists the routes it exposes, which tells you what a Koop instance actually looks like from the outside:

```bash
2023-03-17T19:18:29.417Z info: ROUTE | [GET, POST] | /file-geojson/rest/services/:id/FeatureServer
```

The service routes follow the ArcGIS REST layout, with an /info endpoint, a /tokens pair for authentication, a layers listing, and a per layer path, plus a catch-all FeatureServer route. The queries in the README show what the client can ask for. A plain request returns the demo data as GeoJSON, a request with a where clause filters features by a property value, and a request with outSR=3857 returns the same data in a different coordinate system. That last one is the point of the whole exercise, since reprojecting on demand is exactly the kind of thing that is tedious to bolt onto an existing service. The demo runs with three commands, a clone, a cd and npm run demo, and then listens on port 8080.

The demo is started with three commands:

```bash
git clone https://github.com/koopjs/koop
cd koop
npm run demo
```

## A monorepo where the core package defines the version

This repository is home of the Koop monorepo, which contains the collection of packages shipped by default with every Koop instance. The parent package is koop-core, and it is used to generate a default configuration of Koop, and references to the Koop version refer to the version of this package. The other packages in the monorepo are dependencies of core and include the Geoservices output plugin and its dependencies, the default in-memory data cache, and a logger, while all other plugins, providers and outputs live in separate repositories. The release tags reflect that split, with the most recent releases being @koopjs/output-geoservices@8.1.25, @koopjs/koop-core@10.4.19 and @koopjs/featureserver@9.3.0, all published on the same day, which is a useful reminder that Koop is several packages rather than one. The featureserver package is worth singling out, because it is the component that implements the ArcGIS REST behaviour, with the rest of the platform built around it.

## Testing is split between unit and integration, and coverage is published

The repository treats coverage as a published artefact rather than a private metric. The README shows coverage badges per package and a table separating integration plus unit coverage from unit only, with a note that the winnow package coverage includes integration tests as opposed to true unit tests, and that the goal is to have complete unit test coverage and break out integration and e2e tests separately. The package.json scripts show the machinery behind that. There is a plain test script that runs the workspace tests, a test:e2e script that sets SUPPRESS_NO_CONFIG_WARNING and a DATA_DIR before invoking jest, and separate coverage targets for all and unit only, each running a coverage analysis script, merging coverage changes and then producing a report through nyc. There is also a coverage-on-branch-changes target, so a pull request can be judged on whether its diff is covered rather than on a repository-wide number. The tooling around that includes a .nycrc for nyc, jest.config.js, a .coverage_json directory and husky with lint-staged, so a commit is linted and tested before it lands.

## Running it in a container is a demo-sized setup

The container story in the repository is minimal, and it is worth reading with that in mind. The Dockerfile uses the official Node.js image as the base, currently node:16.20.2, sets the working directory, copies package.json and package-lock.json, runs npm install, copies the demo directory, exposes port 8080 and starts the demo entry point with node demo/index.js. The compose file builds from that Dockerfile, maps 8080 to 8080 and mounts the current directory into /usr/src/app. So what the container gives you is a working Koop instance serving the bundled demo data, not a production deployment recipe. For a real deployment the interesting configuration decisions are elsewhere, in the provider plugins and in how the host is set up. The release tooling around the monorepo is a changesets setup, with a version script that runs changeset version, a release:npm script that runs changeset publish, a release:github script that runs a local Node file, and a changeset-to-changelog.js helper, plus a lerna.json and a commitlint configuration. The licence is Apache 2.0, and the repository links the Koop documentation, the ArcGIS REST API documentation and the ArcGIS for Developers site as further reading.

## Conclusion

Adopt Koop when you have spatial data that already lives in some other system and need to publish it to ArcGIS clients, tiles or plain GeoJSON without duplicating the data into a new database. Do not adopt it if your data is already GeoJSON served by a static file, since that is what the demo does with a file provider. Verify first that a provider plugin exists for your source, that the output format you need is available, and that the versions of the koop-core and featureserver packages you pull in are compatible with each other.

## FAQ

### What does Koop actually do?

It is a JavaScript toolkit and Node.js web server that makes requests to spatial APIs and transforms geospatial data on the fly between formats, delivering it over HTTP. You keep your data in its native format and expose it in whatever format clients need, with GeoServices for ArcGIS out of the box and vector-tile, WMS and GeoJSON as plugin options.

### How do Koop plugins fit together?

Provider plugins connect to a data source and translate it into a common format, GeoJSON, and output plugins then transform that GeoJSON into another specification such as GeoServices, vector tiles or WMS. The two-stage design means a new data source only has to produce GeoJSON for every installed output plugin to work against it.

### How do I run the Koop demo?

Clone the repository, cd into koop, and run npm run demo. Koop then listens on port 8080 and logs the exposed routes, and you can query the demo data through the ArcGIS style paths such as /file-geojson/rest/services/line/FeatureServer/0/query, with a where clause for filtering and an outSR parameter to reproject on demand.

### Which packages make up the Koop monorepo?

The parent package is koop-core, which generates the default Koop configuration and whose version is the Koop version, and the other packages in the monorepo are its dependencies, including the Geoservices output plugin and its dependencies, the default in-memory data cache, a logger, the featureserver package and winnow. All other provider and output plugins live in separate repositories.

## Sources

- [Issues](https://github.com/koopjs/koop/issues)
- [koopjs/koop on GitHub](https://github.com/koopjs/koop)
- [Project website](http://koopjs.github.io)
- [README](https://github.com/koopjs/koop/blob/master/README.md)
- [Releases](https://github.com/koopjs/koop/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/koopjs-koop
