@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-integrationQuick Start
Option 1: Using the Convenience Factory (Recommended)
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 IDNS_CLIENT_ID— OAuth2 client IDNS_CERTIFICATE_ID— Certificate IDNS_CERTIFICATE_PRIVATE_KEY— PKCS8 formatted private keyNS_ALGORITHM— (optional, defaults toES512) 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
NetSuiteErrorwith 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