A fully-typed TypeScript client for the waarneming.nl API. This library provides an easy-to-use interface for interacting with the API, handling both public (unauthenticated) and private (authenticated) endpoints.
This library supports all websites in the Observation International network:
By default, the client connects to the test environment of waarneming.nl
to prevent accidental operations on production data. You can easily switch to another site or to the production environment by providing the platform
and test
options.
import { ObservationClient } from 'observation-js';
// Connect to the Belgian production site
const client = new ObservationClient({
platform: 'be',
test: false, // Explicitly opt-in to production
// other options like clientId, etc.
});
// Connect to the international test environment
const testClient = new ObservationClient({
platform: 'org',
test: true, // This is the default, but can be explicit
// other options
});
You can still use the baseUrl
option to connect to a custom instance, which will take precedence over the platform
setting.
client.species
, client.observations
, etc.).# Using bun
bun add observation-js
# Using npm
npm install observation-js
# Using yarn
yarn add observation-js
Get details for a species in just a few lines of code. The client can be used without any options for accessing public, unauthenticated endpoints.
import { ObservationClient } from 'observation-js';
const client = new ObservationClient();
async function getSpeciesDetails(id: number) {
try {
// Set language for results (optional, defaults to 'en')
client.setLanguage('nl');
const species = await client.species.get(id);
console.log(
`Successfully fetched: ${species.name} (${species.scientific_name})`,
);
console.log(`Group: ${species.group_name}`);
console.log(
`Photos:`,
species.photos.map((p) => p.url),
);
} catch (error) {
console.error('Error fetching species details:', error);
}
}
// Get details for the Little Grebe (Dodaars)
getSpeciesDetails(2);
The client is organized into resources, making the API intuitive to use.
const locations = await client.locations.search({ q: 'Amsterdam' });
console.log('Found locations:', locations.results);
const observations = await client.species.getObservations(2); // Species ID for Little Grebe
console.log(`Found ${observations.count} observations.`);
// First, authenticate the client (see Authentication section)
await client.setAccessToken('YOUR_ACCESS_TOKEN');
const userInfo = await client.users.getInfo();
console.log(`Hello, ${userInfo.name}`);
For endpoints that require authentication (like creating or updating data), you'll need to authenticate the user using OAuth2. The client supports both Authorization Code and Password Grant flows.
For server-side applications or testing, you can use the password grant flow:
const client = new ObservationClient({
platform: 'nl',
test: false // Use production environment for OAuth
});
// Authenticate using password grant
const tokenResponse = await client.getAccessTokenWithPassword({
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
email: process.env.OAUTH_USERNAME,
password: process.env.OAUTH_PASSWORD,
});
console.log('Authentication successful!');
console.log(`Access token expires in: ${tokenResponse.expires_in} seconds`);
For web applications where users need to authorize your app:
First, initialize the client with your application's credentials:
const client = new ObservationClient({
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
redirectUri: 'YOUR_CALLBACK_URL',
});
Next, redirect the user to the generated authorization URL:
const scopes = ['read_observations', 'write_observations'];
const state = 'a-random-string-for-security'; // Generate and store this securely
const authUrl = client.getAuthorizationUrl(state, scopes);
// Redirect your user to authUrl
After the user authorizes your app, they will be sent to your redirectUri
, where you can exchange the received code
for an access token:
const code = '...'; // Get code from URL query parameters
const tokenResponse = await client.getAccessToken(code);
client.setAccessToken(tokenResponse.access_token);
To improve performance, observation-js
includes a configurable in-memory cache for GET
requests. It's enabled by default with a 1-hour TTL. You can easily configure it when initializing the client.
const client = new ObservationClient({
// ...other options
// Example: Disable the cache entirely
cache: {
enabled: false,
},
// Example: Set a default TTL of 15 minutes for all cacheable requests
cache: {
defaultTTL: 900, // TTL in seconds
},
});
You can also control caching on a per-request basis. This is useful for either disabling caching for a specific call or providing a unique TTL. Use the clientCache
option for this:
// This request will not be cached, regardless of global settings
const freshData = await client.countries.list({ clientCache: false });
// This request will be cached for 5 minutes (300 seconds)
const temporaryData = await client.species.get(123, {
clientCache: { ttl: 300 },
});
For more advanced caching options, such as injecting your own cache implementation, please refer to the ObservationClientOptions
in the generated API documentation.
You can globally inspect, modify, or handle all requests and responses using interceptors. This is useful for logging, adding custom headers, or other cross-cutting concerns.
// Log every outgoing request
client.interceptors.request.use((config) => {
// Note: The request config is a standard RequestInit object.
// We can't easily log the URL here as it's constructed later.
console.log(`Sending ${config.method || 'GET'} request...`);
return config;
});
// Add a custom header to every request
client.interceptors.request.use((config) => {
config.headers = new Headers(config.headers); // Ensure headers object exists
config.headers.set('X-Custom-Header', 'my-value');
return config;
});
// Log every incoming response status
client.interceptors.response.use((response) => {
console.log(`Received response with status: ${response.status}`);
return response;
});
For more detailed, runnable examples of how to use the various API resources, please see the files in the /examples
directory of this repository.
This project is licensed under the MIT License.