Skip to content

Latest commit

 

History

76 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 22.13.0 or higher (CI: 22, 24, 26), 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.
  • abby.customerPortal: Customer portal payments and billing themes.

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 API requests, catching malformed payloads before they leave your process.

Automatic Validation

All SDK methods automatically validate request data: parameters, query strings, and request bodies are checked before sending.

Responses are not validated. Response schemas are generated and exported (see below), but the SDK does not run them — a spec/API mismatch would otherwise turn a successful call into a thrown error. Types on responses remain compile-time only; parse with the exported z*Dto schemas yourself if you need a runtime guarantee.

If request 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: 'john@example.com',
};

// 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.

About

Node.js library for the Abby API

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages