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

@abby-inc/node

v1.0.0

Published

Node.js, Bun, and Deno library for the Abby API

Readme

Abby Node.js, Bun & Deno SDK

npm version npm downloads CI codecov License: MIT

The official Node.js, Bun, and Deno library for the Abby API. Abby is an all-in-one business management platform for freelancers and micro-enterprises.

Table of Contents

Installation

Install the package using your preferred package manager:

npm install @abby-inc/node
# or
yarn add @abby-inc/node
# or
pnpm add @abby-inc/node
# or
bun add @abby-inc/node

For Deno, you can import directly from npm:

import Abby from 'npm:@abby-inc/node';

Requirements

  • Node.js 18.0.0 or higher, Bun 1.0.0 or higher, or Deno 2.0.0 or higher
  • An Abby account with an API key

Usage

Initialization

Get your API key from the Abby settings.

import Abby from '@abby-inc/node';

const abby = new Abby('your_api_key');

Services

The SDK provides several services to interact with different parts of the Abby API:

  • abby.company: Current company info and preferences.
  • abby.invoice: Create and manage invoices.
  • abby.estimate: Create and manage estimates (quotes).
  • abby.contact: Manage your contacts (customers).
  • abby.organization: Manage organizations.
  • abby.billing: Shared billing utilities (PDFs, emails).
  • abby.opportunity: CRM and opportunities management.
  • abby.asset: Manage business assets.
  • abby.advance: Manage advance payments.

Basic Example

import Abby from '@abby-inc/node';

const abby = new Abby('your_api_key');

async function main() {
  // Get current company information
  const { data: me } = await abby.company.getMe();
  console.log(`Welcome back, ${me.user.firstname}!`);

  // List all contacts
  const { data: contacts } = await abby.contact.retrieveContacts({
    query: { limit: 10 },
  });

  console.log(`Found ${contacts.docs.length} contacts.`);
}

main().catch(console.error);

Configuration

You can pass optional configuration settings when initializing the SDK:

import Abby from '@abby-inc/node';

const abby = new Abby('your_api_key', {
  baseUrl: 'https://api.app-abby.com',
  timeout: 30000,
  headers: {
    'X-Custom-Header': 'value',
  },
});

| Option | Default | Description | | --------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | baseUrl | 'https://api.app-abby.com' | Base URL for the Abby API. | | timeout | 30000 | Request timeout in milliseconds. | | headers | undefined | Additional headers to include in every request. | | fetch | globalThis.fetch | Custom fetch implementation for proxies, logging, or testing. See Custom Fetch & Proxy Support. |

Custom Fetch & Proxy Support

The SDK allows you to provide a custom fetch implementation for advanced use cases like proxies, custom logging, or testing.

Using a Proxy

With undici (recommended for Node.js):

import Abby from '@abby-inc/node';
import { fetch as undiciFetch, ProxyAgent } from 'undici';

const proxyAgent = new ProxyAgent('http://proxy.example.com:8080');

const abby = new Abby('your_api_key', {
  fetch: (url, init) =>
    undiciFetch(url, {
      ...init,
      dispatcher: proxyAgent,
    }),
});

With node-fetch and https-proxy-agent:

import Abby from '@abby-inc/node';
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';

const proxyAgent = new HttpsProxyAgent('http://proxy.example.com:8080');

const abby = new Abby('your_api_key', {
  fetch: (url, init) =>
    fetch(url, {
      ...init,
      agent: proxyAgent,
    }) as Promise<Response>,
});

Custom Logging

const abby = new Abby('your_api_key', {
  fetch: async (url, init) => {
    console.log(`[Abby SDK] ${init?.method ?? 'GET'} ${url}`);
    const start = Date.now();

    const response = await globalThis.fetch(url, init);

    console.log(`[Abby SDK] ${response.status} in ${Date.now() - start}ms`);
    return response;
  },
});

Testing with Mock Fetch

import { vi } from 'vitest';

const mockFetch = vi.fn(
  async () =>
    new Response(JSON.stringify({ success: true }), {
      status: 200,
      headers: { 'Content-Type': 'application/json' },
    })
);

const abby = new Abby('your_api_key', {
  fetch: mockFetch,
});

Error Handling

The SDK throws descriptive errors when a request fails. You can catch these errors to handle different failure scenarios:

try {
  const { data } = await abby.invoice.getInvoice({
    path: { invoiceId: 'inv_invalid' },
  });
} catch (error) {
  if (error.status === 404) {
    console.error('Invoice not found');
  } else if (error.status === 401) {
    console.error('Invalid API key');
  } else {
    console.error('An unexpected error occurred:', error.message);
  }
}

Event Listeners

The SDK provides an event emitter pattern for global error handling and logging. This is useful for integrating with error tracking services like Sentry or for centralized logging.

Listening for Errors

Subscribe to all API errors across your application:

import Abby from '@abby-inc/node';

const abby = new Abby('your_api_key');

// Global error handler - fires on any 4xx/5xx response
abby.on('error', (error) => {
  console.error(`API Error: ${error.status} ${error.statusText}`);
  console.error(`URL: ${error.method} ${error.url}`);
  console.error(`Message: ${error.message}`);
  console.error(`Duration: ${error.duration}ms`);

  // Send to error tracking service
  Sentry.captureException(new Error(error.message), {
    extra: {
      status: error.status,
      url: error.url,
      requestId: error.requestId,
      body: error.body,
    },
  });
});

Listening for All Responses

Subscribe to all API responses (both successful and failed):

// Log all API calls for debugging/monitoring
abby.on('response', (response) => {
  console.log(`${response.method} ${response.url} - ${response.status} (${response.duration}ms)`);
});

Removing Listeners

Use off() to remove a listener:

const errorHandler = (error) => console.error(error);

// Add listener
abby.on('error', errorHandler);

// Remove listener later
abby.off('error', errorHandler);

Event Types

| Event | Description | Payload Type | | ---------- | -------------------------- | ------------------- | | error | Fires on 4xx/5xx responses | AbbyErrorEvent | | response | Fires on all responses | AbbyResponseEvent |

AbbyErrorEvent properties:

| Property | Type | Description | | ------------ | ---------- | ---------------------------------------------- | | status | number | HTTP status code (4xx or 5xx) | | statusText | string | HTTP status text | | url | string | Request URL | | method | string | HTTP method (GET, POST, etc.) | | duration | number | Request duration in milliseconds | | message | string? | Error message from response body, if available | | body | unknown? | Response body, if available | | requestId | string? | X-Request-Id header, if available |

AbbyResponseEvent properties:

| Property | Type | Description | | ---------- | --------- | ----------------------------------------- | | status | number | HTTP status code | | url | string | Request URL | | method | string | HTTP method (GET, POST, etc.) | | duration | number | Request duration in milliseconds | | ok | boolean | Whether the response was successful (2xx) |

Interceptors

Interceptors allow you to hook into the request/response lifecycle:

const client = abby.getClient();

// Request Interceptor: Add a custom header to every request
client.interceptors.request.use((request) => {
  request.headers.set('X-Request-ID', crypto.randomUUID());
  return request;
});

// Response Interceptor: Log every response status
client.interceptors.response.use((response) => {
  console.log(`API Response: ${response.status}`);
  return response;
});

Raw Requests

If you need to call an endpoint that isn't covered by the SDK, or prefer to specify request details directly, you can use the underlying HTTP client:

import Abby from '@abby-inc/node';

const abby = new Abby('your_api_key');
const client = abby.getClient();

// GET request
const { data } = await client.get({
  url: '/v2/some/endpoint',
});

// POST request with body
const { data: result } = await client.post({
  url: '/v2/some/endpoint',
  body: {
    field: 'value',
  },
});

// Other methods available: put, patch, delete, head, options

The client automatically includes your API key and SDK headers in all requests.

Validation

The SDK includes built-in Zod validation for all API requests and responses, ensuring type safety at runtime.

Automatic Validation

All SDK methods automatically validate:

  • Request data: Parameters, query strings, and request bodies are validated before sending
  • Response data: API responses are validated to ensure they match the expected schema

If validation fails, a ZodError is thrown with detailed information about what went wrong:

import Abby from '@abby-inc/node';
import { ZodError } from 'zod';

const abby = new Abby('your_api_key');

try {
  // This will fail validation if the request body is invalid
  await abby.contact.createContact({
    body: {
      // Missing required fields will trigger a ZodError
    },
  });
} catch (error) {
  if (error instanceof ZodError) {
    console.error('Validation failed:', error.errors);
  }
}

Using Zod Schemas Directly

All Zod schemas are exported and can be used for your own validation needs:

import Abby, { zCreateContactDto, zReadContactDto } from '@abby-inc/node';

// Validate user input before sending to the API
const userInput = {
  firstname: 'John',
  lastname: 'Doe',
  email: '[email protected]',
};

// Parse and validate (throws ZodError if invalid)
const validatedContact = zCreateContactDto.parse(userInput);

// Or use safeParse for non-throwing validation
const result = zCreateContactDto.safeParse(userInput);
if (result.success) {
  console.log('Valid:', result.data);
} else {
  console.error('Invalid:', result.error.errors);
}

// Infer TypeScript types from schemas
import { z } from 'zod';
type CreateContactInput = z.infer<typeof zCreateContactDto>;

Available schema patterns:

  • z{DtoName} - Schemas for DTOs (e.g., zCreateContactDto, zReadInvoiceDto)
  • z{ControllerMethod}Data - Request data schemas (e.g., zContactControllerCreateContactData)

TypeScript Support

The SDK is written in TypeScript and provides complete type definitions for all API resources and responses.

import Abby, { ReadMeDto } from '@abby-inc/node';

const abby = new Abby('your_api_key');

async function getCompanyData() {
  const { data }: { data: ReadMeDto } = await abby.company.getMe();
  return data;
}

You can also infer types directly from Zod schemas:

import { z } from 'zod';
import { zReadContactDto } from '@abby-inc/node';

// Infer the type from the Zod schema
type Contact = z.infer<typeof zReadContactDto>;

Versioning

This SDK uses independent versioning from the Abby API:

  • SDK version (version in package.json): Follows semver for SDK changes (bug fixes, new features, breaking changes)
  • API version (apiVersion in package.json): Tracks which Abby API version was used to generate the SDK
{
  "version": "1.2.0", // SDK version
  "apiVersion": "1.5.0" // API spec version used to generate
}

This allows the SDK to receive bug fixes and improvements independently from API changes.

Development

Contributions are welcome! Please see our Contributing Guidelines for more information.

Support

If you encounter any issues or have questions, please check the following resources:

License

MIT License - see LICENSE for details.