igolf-sdk
v2.0.1
Published
A typed, server-side Node.js SDK for the iGolf API
Maintainers
Readme
igolf-sdk
A small, typed, server-side Node.js SDK for making signed requests to the iGolf API.
The SDK creates iGolf HMAC-SHA256 action URLs, sends JSON POST requests, and returns a discriminated response object that works cleanly in JavaScript and TypeScript.
Requirements
- Node.js 18.17 or newer
- An iGolf application key and secret
- An HTTPS iGolf API endpoint
[!IMPORTANT] Use this SDK only in trusted server-side code. Supplying
appSecretin browser or mobile code exposes the secret to end users.
Installation
npm install igolf-sdkQuick start
Node.js
Create the client in trusted server-side code and keep credentials in environment variables:
const { IGolfController } = require("igolf-sdk");
const igolf = new IGolfController({
baseUrl: process.env.IGOLF_BASE_URL,
appKey: process.env.IGOLF_APP_KEY,
apiVersion: "1.0",
signVersion: "1.0",
signMethod: "HMAC-SHA256",
appSecret: process.env.IGOLF_APP_SECRET,
});
async function main() {
const response = await igolf.requestWithActionCode("CourseList", {
referenceLatitude: 40.71,
referenceLongitude: -74.0,
radius: 50,
page: 1,
});
if (response.stat) {
console.log(response.data);
} else {
console.error(response.data);
}
}
main().catch(console.error);ES module and TypeScript imports are also supported:
import { IGolfController } from "igolf-sdk";NestJS
Register one shared client through a custom provider. The example uses @nestjs/config so secrets stay outside source code.
// igolf.constants.ts
export const IGOLF_CLIENT = Symbol("IGOLF_CLIENT");// golf.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { IGolfController } from "igolf-sdk";
import { GolfService } from "./golf.service";
import { IGOLF_CLIENT } from "./igolf.constants";
@Module({
imports: [ConfigModule],
providers: [
{
provide: IGOLF_CLIENT,
inject: [ConfigService],
useFactory: (config: ConfigService) =>
new IGolfController({
baseUrl: config.getOrThrow<string>("IGOLF_BASE_URL"),
appKey: config.getOrThrow<string>("IGOLF_APP_KEY"),
apiVersion: config.get<string>("IGOLF_API_VERSION", "1.0"),
signVersion: config.get<string>("IGOLF_SIGN_VERSION", "1.0"),
signMethod: "HMAC-SHA256",
appSecret: config.getOrThrow<string>("IGOLF_APP_SECRET"),
}),
},
GolfService,
],
exports: [GolfService],
})
export class GolfModule {}Inject that provider into an application service:
// golf.service.ts
import { Inject, Injectable } from "@nestjs/common";
import { IGolfController } from "igolf-sdk";
import { IGOLF_CLIENT } from "./igolf.constants";
interface CourseListResponse {
Status: 1;
Courses: Array<{ Id: number; Name: string }>;
}
@Injectable()
export class GolfService {
constructor(
@Inject(IGOLF_CLIENT)
private readonly igolf: IGolfController,
) {}
listCourses(latitude: number, longitude: number) {
return this.igolf.requestWithActionCode<CourseListResponse>("CourseList", {
referenceLatitude: latitude,
referenceLongitude: longitude,
radius: 50,
page: 1,
});
}
}Import GolfModule from the NestJS feature or root module that needs it. Reusing one provider avoids rebuilding client configuration for every request.
Configuration
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| baseUrl | string | Yes | Absolute iGolf API base URL. /rest/action may be included or omitted. |
| appKey | string | Yes | iGolf application key. |
| apiVersion | string | Yes | iGolf API version, such as "1.0". |
| signVersion | string | Yes | iGolf signing version, such as "1.0". |
| signMethod | "HMAC-SHA256" | Yes | Supported signing method. |
| appSecret | string | Yes | Private signing secret. Never expose or commit it. |
| timeoutMs | number | No | Default request timeout. Defaults to 30,000 ms; maximum 600,000 ms. |
The constructor validates its configuration immediately. It rejects unsupported protocols, malformed URLs, invalid timeouts, and unsupported signing methods before a request is sent.
API
requestWithActionCode<T>()
requestWithActionCode<T = unknown>(
actionCode: string,
params?: Record<string, unknown>,
options?: RequestOptions,
): Promise<ApiResponse<T>>The method sends one signed JSON POST request. actionCode must be a single non-empty URL path segment.
Request options:
interface RequestOptions {
signal?: AbortSignal;
timeoutMs?: number;
}Use timeoutMs to override the configured timeout for one request:
const response = await igolf.requestWithActionCode(
"CourseList",
{ radius: 25 },
{ timeoutMs: 5_000 },
);Use an AbortSignal for caller-controlled cancellation:
const controller = new AbortController();
const request = igolf.requestWithActionCode(
"CourseList",
{},
{ signal: controller.signal },
);
controller.abort();
const response = await request;Response model
ApiResponse<T> is a discriminated union:
type ApiResponse<T> =
| { stat: true; data: T }
| { stat: false; data: string };stat: truemeans the HTTP request succeeded and iGolf returnedStatus: 1.stat: falsecontains a validation, HTTP, iGolf, timeout, cancellation, or transport error message.- Request failures are returned as values. Invalid SDK inputs still throw synchronously or reject the async call with a
TypeError/RangeError.
How signing works
For each request, the SDK:
- Creates a timestamp in
YYMMDDHHmmss±HHmmformat. - Joins the action code, application key, API version, signing version, signing method, timestamp, and
JSONresponse format with/. - Signs that string with HMAC-SHA256 using
appSecret. - Encodes the signature using URL-safe Base64 without padding.
- Places the signature and timestamp in the iGolf action URL.
The iGolf signing format covers URL authentication metadata; it does not hash the JSON request body. Always use HTTPS and keep the secret on a trusted server.
Migrating from 1.x
Version 2 is a major release because it corrects behavior visible to callers:
- Node.js 18.17+ is required.
- iGolf responses whose
Statusis not1now returnstat: falseinstead of a successful empty array. requestWithActionCode<T>()now implements the generic response type documented by version 1.- Request parameters use
Record<string, unknown>instead ofRecord<string, any>. - The malformed repeated-timezone timestamp was replaced by one
±HHmmoffset. - Axios, CryptoJS, Moment, and the obfuscation build have been removed.
Development
npm ci
npm run checkUseful scripts:
| Command | Purpose |
| --- | --- |
| npm run typecheck | Check source and type-level examples. |
| npm test | Build and run behavioral tests. |
| npm run test:package | Pack and install the tarball in a clean consumer project. |
| npm run check | Run every release check. |
Generated dist files are intentionally not committed. npm pack and npm publish run the complete release check through prepack.
Support and contributing
- Report a bug
- Request a feature
- View all issues
- Read CONTRIBUTING.md before opening a pull request.
- Report security problems according to SECURITY.md, not through a public issue.
License
MIT © Mayank Anand
