npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

observation-js

v1.15.0

Published

A fully-typed TypeScript client for the waarneming.nl API.

Downloads

98

Readme

observation-js

npm version CI codecov Documentation License: MIT

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.

Features

  • Fully Typed: Written in TypeScript for a great developer experience with auto-completion and type safety.
  • Comprehensive API Coverage: Implements a wide range of API resources, including Observations, Species, Users, Locations, and more.
  • Modern & Simple: Uses a clean, resource-based architecture (client.species, client.observations, etc.).
  • Authentication Handled: Built-in support for the OAuth2 Authorization Code Grant and Password Grant flows.
  • Custom Error Handling: Throws detailed, custom errors to simplify debugging.
  • Multi-language Support: Easily fetch API responses in different languages.
  • Configurable Caching: Built-in, configurable in-memory cache to reduce redundant API calls.
  • Request/Response Interceptors: Hook into the request lifecycle to globally modify requests and responses.
  • Powered by Bun: Built and tested with the modern Bun runtime.

Installation

# Using bun
bun add observation-js

# Using npm
npm install observation-js

# Using yarn
yarn add observation-js

Getting Started

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);

Usage Examples

The client is organized into resources, making the API intuitive to use.

Search for locations

const locations = await client.locations.search({ q: 'Amsterdam' });
console.log('Found locations:', locations.results);

Get observations for a species

const observations = await client.species.getObservations(2); // Species ID for Little Grebe
console.log(`Found ${observations.count} observations.`);

Get the current user's info (requires authentication)

// First, authenticate the client (see Authentication section)
await client.setAccessToken('YOUR_ACCESS_TOKEN');

const userInfo = await client.users.getInfo();
console.log(`Hello, ${userInfo.name}`);

Authentication

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.

Password Grant (Direct Authentication)

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`);

Authorization Code Flow (Web Applications)

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);

Caching

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.

Interceptors

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;
});

Examples

For more detailed, runnable examples of how to use the various API resources, please see the files in the /examples directory of this repository.

License

This project is licensed under the MIT License.