npm.io
1.0.0-beta.0.6 • Published 6d ago

@reinaldorock/netsuite-rest-integration

Licence
MIT
Version
1.0.0-beta.0.6
Deps
1
Size
171 kB
Vulns
0
Weekly
491

@reinaldorock/netsuite-rest-integration

TypeScript library for integrating with the NetSuite REST API using OAuth 2.0 Client Credentials (M2M) authentication.

Installation

pnpm add @reinaldorock/netsuite-rest-integration

Quick Start

The simplest way to get started is to use createNetSuiteClientFromEnv(), which reads all configuration from environment variables:

import { createNetSuiteClientFromEnv } from '@reinaldorock/netsuite-rest-integration';

// Reads auth config from env vars and creates a ready-to-use client
const client = createNetSuiteClientFromEnv();

const customer = await client.get('/record/v1/customer/123');
console.log(customer);

Required environment variables:

  • NS_ACCOUNT_ID — Your NetSuite account ID
  • NS_CLIENT_ID — OAuth2 client ID
  • NS_CERTIFICATE_ID — Certificate ID
  • NS_CERTIFICATE_PRIVATE_KEY — PKCS8 formatted private key
  • NS_ALGORITHM — (optional, defaults to ES512) JWT signing algorithm
Option 2: Explicit Instantiation

For more control, create auth and client instances explicitly:

import { NetSuiteAuth, NetSuiteClient } from '@reinaldorock/netsuite-rest-integration';

const auth = new NetSuiteAuth({
  accountId: process.env.NS_ACCOUNT_ID!,
  clientId: process.env.NS_CLIENT_ID!,
  certificateId: process.env.NS_CERTIFICATE_ID!,
  privateKey: process.env.NS_CERTIFICATE_PRIVATE_KEY!,
  algorithm: 'ES512', // optional
});

const client = new NetSuiteClient(
  { accountId: process.env.NS_ACCOUNT_ID! },
  auth
);

const customer = await client.get('/record/v1/customer/123');
Option 3: Using Individual Factories

Create auth and client separately using factory functions:

import { createNetSuiteAuth, createNetSuiteClient } from '@reinaldorock/netsuite-rest-integration';

// Read auth config from env vars
const auth = createNetSuiteAuth();

// Create client with the auth instance
const client = createNetSuiteClient(auth);

const customer = await client.get('/record/v1/customer/123');

You can also override specific config values:

// Override account ID
const auth = createNetSuiteAuth({ accountId: '9999999' });

// Override base URL
const client = createNetSuiteClient(auth, { baseUrl: 'https://custom.api.url' });

Authentication

NetSuiteAuth Class

Handles OAuth 2.0 Client Credentials authentication. It automatically:

  • Builds and signs JWT assertions
  • Fetches access tokens from NetSuite
  • Caches tokens until expiration
  • Clears cache on token invalidation (401 invalid_grant)
import { NetSuiteAuth } from '@reinaldorock/netsuite-rest-integration';

const auth = new NetSuiteAuth({
  accountId: '1234567',
  clientId: 'your-client-id',
  certificateId: 'your-cert-id',
  privateKey: '-----BEGIN PRIVATE KEY-----\n...',
});

// Get an access token (cached internally)
const token = await auth.getAccessToken();

// Clear cache (forces re-fetch on next call)
auth.clearTokenCache();
Configuration
Property Required Description
accountId Yes Your NetSuite account ID
clientId Yes OAuth2 client ID from NetSuite
certificateId Yes Certificate ID registered in NetSuite
privateKey Yes PKCS8 formatted private key (PEM format)
algorithm No JWT signing algorithm (default: ES512)

Client

NetSuiteClient Class

HTTP client for making authenticated requests to the NetSuite REST API.

Methods
// GET request
const result = await client.get('/record/v1/customer/123');

// POST request (returns { id: "..." } on 204 with Location header)
const created = await client.post('/record/v1/customer', {
  firstName: 'John',
  lastName: 'Doe',
  email: 'john@example.com',
});

// PATCH request (returns undefined on 204, or response body on 200)
const updated = await client.patch('/record/v1/customer/123', {
  email: 'newemail@example.com',
});

// DELETE request (returns undefined)
await client.del('/record/v1/customer/123');
Configuration
Property Description
baseUrl Optional: Full base URL (e.g., https://custom.api.url/services/rest)
accountId Optional: Account ID to construct default base URL

If neither baseUrl nor accountId is provided, they are read from environment variables.

Features
  • Automatic retries on 429 (rate limit) and 401 invalid_grant errors
  • JSON field handling — Automatically appends null terminator to JSON strings in text fields (NetSuite quirk workaround)
  • Error handling — Throws NetSuiteError with status code and detailed error messages
  • Custom headers — Pass extra headers for each request
const result = await client.post('/record/v1/customer', { ... }, {
  'X-NetSuite-PropertyNameValidation': 'Error',
});

Environment Setup

1. Create .env file
NS_ACCOUNT_ID=1234567
NS_CLIENT_ID=your-client-id
NS_CERTIFICATE_ID=your-cert-id
NS_CERTIFICATE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQE...
-----END PRIVATE KEY-----"
NS_ALGORITHM=ES512
2. Load environment variables
import 'dotenv/config';

const client = createNetSuiteClientFromEnv();

Or in your build/deployment process, ensure these variables are set in your environment.

Usage Examples

Fetching a Customer Record
const client = createNetSuiteClientFromEnv();

const customer = await client.get('/record/v1/customer/123');
console.log(customer.firstName, customer.email);
Creating a Record
const newCustomer = await client.post('/record/v1/customer', {
  firstName: 'Jane',
  lastName: 'Smith',
  email: 'jane@example.com',
  subsidiary: { id: '1' },
});

console.log(`Created customer ID: ${newCustomer.id}`);
Updating a Record
const result = await client.patch('/record/v1/customer/123', {
  email: 'newemail@example.com',
  phone: '+1 (555) 123-4567',
});

console.log('Customer updated');
Running SuiteQL Queries
const result = await client.post('/query/v1/suiteql', {
  q: 'SELECT id, firstName, lastName, email FROM customer LIMIT 10',
});

console.log(`Found ${result.items.length} customers`);

Error Handling

The client throws NetSuiteError for non-2xx responses:

import { NetSuiteError } from '@reinaldorock/netsuite-rest-integration';

try {
  const customer = await client.get('/record/v1/customer/99999');
} catch (error) {
  if (error instanceof NetSuiteError) {
    console.error(`NetSuite error ${error.status}: ${error.message}`);
  }
}

Dependency Injection

The INetSuiteClient interface makes the client mockable for testing:

import type { INetSuiteClient } from '@reinaldorock/netsuite-rest-integration';

function processCustomer(client: INetSuiteClient) {
  return client.get('/record/v1/customer/123');
}

// In tests, pass a mock
const mockClient: INetSuiteClient = {
  get: vi.fn().mockResolvedValue({ id: '123', firstName: 'John' }),
  post: vi.fn(),
  patch: vi.fn(),
  del: vi.fn(),
};

await processCustomer(mockClient);

Token Caching

Authentication tokens are cached automatically until expiration. To manually clear the cache (e.g., after credential rotation):

const auth = createNetSuiteAuth();

// Use client...
await client.get('/record/v1/customer/123');

// Clear token cache
auth.clearTokenCache();

// Next request will fetch a new token
await client.get('/record/v1/customer/456');

Testing

The library is built with strong TypeScript and test coverage. All factories and constructors throw synchronous errors when required environment variables are missing:

// Throws synchronously if env vars are missing
expect(() => createNetSuiteClientFromEnv()).toThrow(
  'Missing required environment variable: NS_ACCOUNT_ID'
);

License

MIT

Keywords