Connector API
Low-level API reference for the NetSuite HTTP connector.
The suiteportal package exposes the low-level HTTP client and SuiteQL executor. You typically don't need these directly — createClient wraps them — but they're available for advanced use cases.
NetSuiteClient
The core HTTP client with automatic authentication, retries, and rate limiting. Supports both TBA (OAuth 1.0a) and OAuth 2.0 M2M.
import { NetSuiteClient } from 'suiteportal';
const client = new NetSuiteClient({
authType: 'tba',
accountId: '1234567_SB1',
consumerKey: '...',
consumerSecret: '...',
tokenId: '...',
tokenSecret: '...',
});import { NetSuiteClient } from 'suiteportal';
const client = new NetSuiteClient({
authType: 'oauth2',
accountId: '1234567_SB1',
clientId: '...',
certificateId: '...',
privateKey: fs.readFileSync('./private.pem', 'utf8'),
// algorithm: 'ES256', // default
// scopes: ['restlets', 'rest_webservices'], // default
});With OAuth 2.0, the client automatically handles token exchange and refresh. Tokens are cached in memory and refreshed 5 minutes before expiry.
Constructor Options
All options shared between both auth types:
| Option | Default | Description |
|---|---|---|
timeout | 30000 | Request timeout in milliseconds |
concurrency | 5 | Max concurrent requests |
maxRetries | 3 | Retry attempts for retryable failures |
requestsPerSecond | (disabled) | Proactive rate limit (token bucket) |
burstSize | requestsPerSecond | Token bucket burst capacity |
baseUrl | (derived) | Override the API base URL (testing) |
request
Make an authenticated request to any NetSuite REST endpoint:
const response = await client.request<ResponseType>({
method: 'GET',
path: '/services/rest/record/v1/customer/123',
});
// response.status, response.headers, response.datadestroy
Release resources (cancel pending token bucket timers):
client.destroy();executeSuiteQL
Execute a SuiteQL query and return the first page:
import { executeSuiteQL } from 'suiteportal';
const result = await executeSuiteQL(client, 'SELECT id FROM customer', {
limit: 100, // default: 1000
offset: 0, // default: 0
});
result.items; // SuiteQLRow[]
result.hasMore; // boolean
result.totalResults; // number | undefinedexecuteSuiteQLPaginated
Auto-paginate through all results:
import { executeSuiteQLPaginated } from 'suiteportal';
const allRows = await executeSuiteQLPaginated(client, 'SELECT id FROM customer', {
pageSize: 1000, // default: 1000
maxRows: 10000, // default: unlimited
});REST Record Operations
CRUD helpers for the REST Record API (/services/rest/record/v1):
import {
createRecord,
updateRecord,
deleteRecord,
getRecord,
} from 'suiteportal';createRecord
const record = await createRecord(client, 'customer', {
companyname: 'Acme Corp',
email: 'hello@acme.com',
});
// POST /services/rest/record/v1/customer
// Returns the created recordupdateRecord
const record = await updateRecord(client, 'customer', 123, {
email: 'new@acme.com',
});
// PATCH /services/rest/record/v1/customer/123
// Returns the updated recorddeleteRecord
await deleteRecord(client, 'customer', 123);
// DELETE /services/rest/record/v1/customer/123
// Returns void (204 No Content)getRecord
const record = await getRecord(client, 'customer', 123);
// GET /services/rest/record/v1/customer/123
// Returns the full recordError Types
All errors extend NetSuiteError:
import { NetSuiteError, AuthError, RateLimitError, TimeoutError } from 'suiteportal';
try {
await client.request({ ... });
} catch (error) {
if (error instanceof AuthError) {
// 401/403 — bad credentials
} else if (error instanceof RateLimitError) {
// 429 — too many requests
error.retryAfterMs; // suggested wait time
} else if (error instanceof TimeoutError) {
// Request timed out
}
}With OAuth 2.0 M2M, a 401 response automatically clears the cached token. The next request will trigger a fresh token exchange.
Config Helpers
import { deriveBaseUrl, getRealm, isOAuth2Config } from 'suiteportal';
deriveBaseUrl('1234567_SB1');
// → 'https://1234567-sb1.suitetalk.api.netsuite.com'
getRealm('1234567_SB1');
// → '1234567_SB1'
isOAuth2Config({ authType: 'oauth2', ... });
// → true