@kaistrum/geonode-node-sdk
v1.0.0
Published
Node.js SDK for GeoNode 4.x — dataset uploads and full WFS-T feature CRUD (create, read, update, delete) with geometry and attributes
Downloads
81
Maintainers
Readme
@kaistrum/geonode-node-sdk
A Node.js SDK for GeoNode 4.x. Create datasets by uploading data, and run full WFS-T feature CRUD (create / read / update / delete) with geometry and attributes — all behind a small, promise-based API with a consistent result envelope.
Built and verified against a live GeoNode 4.x instance (datasets API + GeoServer WFS-T).
Features
- Auth — HTTP Basic Auth against the GeoNode v2 API.
- Datasets — list, read, update metadata, delete (async), and create via upload (GeoJSON object or a file on disk; the import runs asynchronously and is polled for you).
- Features (WFS-T) — insert, read (with CQL / bbox filters), update, and delete, including bulk update/delete by CQL filter.
- Geometry — Point, LineString, Polygon (GeoJSON in, GML out).
- Consistent responses — every call returns
{ success, data?, error? }.
Install
npm install @kaistrum/geonode-node-sdkRequires Node.js >= 16.
Important: GeoNode 4.x specifics
This SDK targets GeoNode 4.x, whose API differs from older guides:
- Datasets, not layers — the API lives at
/api/v2/datasets/(the old/layers/is gone). - No "empty layer" create — you create a dataset by uploading data.
createLayer()therefore takes a GeoJSONgeojsonobject or afilePath, not just metadata. - GeoServer has its own credentials — feature (WFS-T) operations hit GeoServer directly,
which authenticates separately from GeoNode. Set
geoserverUser/geoserverPassword(defaultadmin/geoserver; the real password is yourGEOSERVER_ADMIN_PASSWORD). - Geometry column is
geomby default (the GeoJSON importer's column name). - Fresh layers settle asynchronously — a just-uploaded layer can take a short while before
GeoServer serves it over WFS.
createLayer()waits for readiness before returning, and the SDK deliberately does not hammer WFS on 401 (rapid retries trip GeoServer's brute-force filter and make the block worse).
Quick start
const GeoNodeService = require('@kaistrum/geonode-node-sdk');
const geonode = new GeoNodeService({
baseUrl: 'http://localhost',
username: 'admin',
password: 'admin',
// GeoServer creds (for feature/WFS-T operations)
geoserverUser: 'admin',
geoserverPassword: 'your-geoserver-password',
debug: true,
});
(async () => {
await geonode.authenticate();
// Create a dataset by uploading a GeoJSON FeatureCollection (async import).
const created = await geonode.createLayer({
name: 'parks',
title: 'City Parks',
geojson: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [-74.0, 40.7] },
properties: { name: 'Central Park', area_sqm: 843000, kind: 'park' },
},
],
},
});
const layer = created.data.alternate; // e.g. "geonode:parks"
const pk = created.data.pk;
// Add a feature (WFS-T Insert). Property names must match the uploaded schema.
await geonode.addFeature(layer, {
type: 'Feature',
geometry: { type: 'Point', coordinates: [-73.94, 40.66] },
properties: { name: 'Prospect Park', area_sqm: 526000, kind: 'park' },
});
// Read features (optionally filtered with CQL).
const parks = await geonode.getFeatures(layer, { cqlFilter: "kind='park'", limit: 100 });
console.log(`${parks.data.features.length} parks`);
// Update a feature by its WFS resource id.
const fid = parks.data.features[0].id;
await geonode.updateFeature(layer, fid, { properties: { kind: 'updated_park' } });
// Delete a feature, then the whole dataset.
await geonode.deleteFeature(layer, fid);
await geonode.deleteLayer(pk);
})();Configuration
| Option | Default | Description |
|--------|---------|-------------|
| baseUrl | http://localhost:8000 | GeoNode base URL |
| username / password | admin / admin | GeoNode credentials |
| geoserverUser / geoserverPassword | username / geoserver | GeoServer credentials (WFS-T) |
| workspace | geonode | Default workspace for new datasets |
| geometryColumn | geom | Geometry column name in PostGIS |
| workspaceNamespace | http://www.geonode.org/ | XML namespace bound to the workspace prefix in WFS-T |
| uploadTimeout | 300000 | Max ms to wait for an import to finish |
| pollInterval | 3000 | ms between import status polls |
| wfsReadyTimeout | 300000 | Max ms to wait for a fresh layer to become WFS-accessible |
| wfsReadyInterval | 30000 | ms between readiness checks (kept slow on purpose) |
| wfsRetries / wfsRetryDelay | 4 / 5000 | Retry policy for transient (5xx) GeoServer errors |
| debug | false | Verbose request logging |
API
Every method resolves to { success, data?, error? } (list calls also include total;
delete calls include executionId; bulk feature calls include updated / deleted).
Auth
authenticate(): Promise<boolean>isAuthenticatedCheck(): boolean
Datasets
createLayer({ geojson?, filePath?, name?, title? })— upload data to create a dataset. Returns the dataset detail (data.pk,data.alternate,data.name, ...).getExecutionStatus(execId)— poll an import execution.listLayers({ limit?, offset?, search? })—datais the datasets array,totalthe count.getLayer(pk)/getLayerAttributes(pk)updateLayer(pk, updates)— patch metadata.deleteLayer(pk)— async delete via the resource service.uploadShapefile(shpPath, metadata?)— upload a.shp(with.shx/.dbf/.prjsidecars).
Features (WFS-T) — layerName is "workspace:layer" (use dataset.alternate)
addFeature(layerName, feature)/addFeaturesInBatch(layerName, features)getFeatures(layerName, { cqlFilter?, bbox?, limit? })/getFeature(layerName, featureId)updateFeature(layerName, featureId, { geometry?, properties? })updateFeaturesByFilter(layerName, updates, cqlFilter)— resolves matches, then updates by resource id.deleteFeature(layerName, featureId)deleteFeaturesByFilter(layerName, cqlFilter)
Feature ids are WFS resource ids of the form layer.N (e.g. parks.3) — read them from
getFeatures(...).data.features[i].id.
Geometry
// Point
{ type: 'Point', coordinates: [lon, lat] }
// LineString
{ type: 'LineString', coordinates: [[lon, lat], [lon, lat]] }
// Polygon (closed ring)
{ type: 'Polygon', coordinates: [[[lon, lat], [lon, lat], [lon, lat], [lon, lat]]] }CQL filter examples
"kind='park'"
"area_sqm>100000"
"kind='park' AND area_sqm>100000"
"name LIKE '%Park%'"
"INTERSECTS(geom, POINT(-74 40))"
"DWITHIN(geom, POINT(-74 40), 1, kilometers)"TypeScript
Ships with type definitions (geonode-service.d.ts). Import the default export:
import GeoNodeService, { GeoNodeConfig } from '@kaistrum/geonode-node-sdk';License
MIT © kaistrum
