Official Node.js SDK for the Tango API – dynamic response shaping, typed models, and full endpoint coverage.
npm install @makegov/tango-nodeA modern Node.js SDK for the Tango API, featuring dynamic response shaping, strong TypeScript types, and full coverage of the core Tango endpoints.
> This is the Node/TypeScript port of the official Tango Python SDK.
- Dynamic Response Shaping – Ask Tango for exactly the fields you want using a simple shape syntax.
- Type-Safe by Design – Shape strings are validated against Tango schemas and mapped to generated TypeScript types.
- Comprehensive API Coverage – Agencies, business types, entities, contracts, vehicles, IDVs, forecasts, opportunities, notices, grants, and webhooks.
- Flexible Data Access – Plain JavaScript objects backed by runtime validation and parsing, materialized via the dynamic model pipeline.
- Modern Node – Built for Node 18+ with native fetch and ESM-first design.
- Tested Against the Real API – Integration tests (mirroring the Python SDK) keep behavior aligned.
Requirements: Node 18 or higher.
``bash`
npm install @makegov/tango-nodeor
yarn add @makegov/tango-nodeor
pnpm add @makegov/tango-node
`ts
import { TangoClient } from "@makegov/tango-node";
const client = new TangoClient({
apiKey: process.env.TANGO_API_KEY,
// baseUrl: "https://tango.makegov.com", // default
});
`
`ts
const agencies = await client.listAgencies();
for (const agency of agencies.results) {
console.log(agency.code, agency.name);
}
`
`ts`
const treasury = await client.getAgency("2000"); // Treasury
console.log(treasury.name, treasury.department?.name);
`ts
import { TangoClient, ShapeConfig } from "@makegov/tango-node";
const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });
const contracts = await client.listContracts({
shape: ShapeConfig.CONTRACTS_MINIMAL,
keyword: "cloud services",
awarding_agency: "4700",
fiscal_year: 2024,
limit: 10,
});
// Each contract is shaped according to CONTRACTS_MINIMAL
for (const c of contracts.results) {
console.log(c.piid, c.award_date, c.recipient.display_name);
}
`
`ts
import { TangoClient, ShapeConfig } from "@makegov/tango-node";
const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });
const entity = await client.getEntity("ABC123DEF456", {
shape: ShapeConfig.ENTITIES_COMPREHENSIVE,
});
console.log(entity.uei, entity.legal_business_name, entity.primary_naics);
`
The Node SDK uses the same model as the Python one: you can either pass the API key directly or read it from TANGO_API_KEY.
`ts
import { TangoClient } from "@makegov/tango-node";
const client = new TangoClient({
apiKey: "your-api-key-here",
});
`
`ts
import { TangoClient } from "@makegov/tango-node";
const client = new TangoClient();
// If apiKey is omitted, the client will look for process.env.TANGO_API_KEY
`
Response shaping is the core feature of Tango. Instead of always receiving huge objects with every field, you describe the fields you want with a compact shape string:
`ts`
const contracts = await client.listContracts({
shape: "key,piid,award_date,recipient(display_name),total_contract_value",
keyword: "software",
limit: 5,
});
Shapes:
- Reduce payload size (often massively).
- Keep responses focused on what your app actually uses.
- Drive type safety – the SDK maps the shape to a TypeScript type.
The Node SDK includes:
- A shape parser that validates shape strings.
- A schema registry that knows what fields exist on each resource.
- A type generator and model factory that convert raw API JSON into strongly-typed objects.
By default, nested fields are returned as nested objects:
`ts
// shape:
"key,piid,recipient(display_name,uei)";
//
contract.recipient.display_name;
contract.recipient.uei;
`
You can request a "flat" representation that uses dotted keys and then unflattens into nested objects on the client:
`ts`
const contracts = await client.listContracts({
shape: ShapeConfig.CONTRACTS_MINIMAL,
flat: true,
});
The Node SDK mirrors the Python client's behavior for shape, flat, and flat_lists.
The Node client mirrors the Python SDK's high-level API:
- listAgencies(options)getAgency(code)
- listBusinessTypes(options)
- listContracts(options)
- listEntities(options)
- getEntity(ueiOrCage, options)
- listForecasts(options)
- listOpportunities(options)
- listNotices(options)
- listGrants(options)
-
All list methods return a paginated response:
`ts`
interface PaginatedResponse
count: number;
next: string | null;
previous: string | null;
pageMetadata: Record
results: T[];
}
Errors are surfaced as typed exceptions, aligned with the Python SDK:
- TangoAPIError – Base error for unexpected issues.TangoAuthError
- – Authentication problems (e.g., invalid API key, 401).TangoNotFoundError
- – Resource not found (404).TangoValidationError
- – Invalid request parameters (400).TangoRateLimitError
- – Rate limit exceeded (429).
Shape-related errors:
- ShapeErrorShapeValidationError
- ShapeParseError
- TypeGenerationError
- ModelInstantiationError
-
Use them in your code:
`ts
import { TangoClient, TangoAPIError, TangoValidationError } from "@makegov/tango-node";
try {
const resp = await client.listContracts({ keyword: "cloud", limit: 5 });
} catch (err) {
if (err instanceof TangoValidationError) {
console.error("Bad request:", err.message);
} else if (err instanceof TangoAPIError) {
console.error("Tango API error:", err.message);
} else {
console.error("Unexpected error:", err);
}
}
`
`textnpm run build
tango-node/
├── src/ # Source TypeScript
│ ├── client.ts # TangoClient implementation
│ ├── config.ts # Default base URL + shape presets
│ ├── errors.ts # Error classes (API, auth, validation, etc.)
│ ├── index.ts # Public API exports
│ ├── types.ts # Shared types (options, PaginatedResponse)
│ ├── models/ # Lightweight model interfaces (Contract, Entity, etc.)
│ ├── shapes/ # Shape system (parser, generator, factory)
│ │ ├── explicitSchemas.ts # Predefined schemas for resources
│ │ ├── factory.ts # Instantiate typed models from data
│ │ ├── generator.ts # Type generation from shape specs
│ │ ├── index.ts # Shapes exports
│ │ ├── parser.ts # Shape string parser
│ │ ├── schema.ts # Schema registry + validation
│ │ ├── schemaTypes.ts # Schema data structures
│ │ └── types.ts # Shape spec types
│ └── utils/ # Helpers
│ ├── dates.ts # Date/time parsing utilities
│ ├── http.ts # HTTP client wrapper
│ ├── number.ts # Numeric parsing/formatting
│ └── unflatten.ts # Unflatten dotted-key responses
├── docs/ # Documentation
│ ├── API_REFERENCE.md
│ ├── DYNAMIC_MODELS.md
│ └── SHAPED.md
├── tests/ # Test suite (Vitest)
│ └── unit/
│ ├── client.test.ts
│ ├── errors.test.ts
│ ├── shapes.factory.test.ts
│ ├── shapes.generator.test.ts
│ ├── shapes.parser.test.ts
│ ├── shapes.schema.test.ts
│ ├── utils.dates.test.ts
│ ├── utils.http.test.ts
│ ├── utils.number.test.ts
│ └── utils.unflatten.test.ts
├── dist/ # Build output (compiled JS + d.ts) from `
├── eslint.config.js # ESLint flat config
├── .prettierrc # Prettier config
├── package.json # Package metadata/scripts
├── tsconfig.json # TypeScript config
├── README.md # Usage docs
├── CHANGELOG.md # Version history
└── LICENSE # MIT license
After cloning the repo:
`bash`
npm install
npm run build
npm test
Useful scripts:
- npm run build – Compile TypeScript to dist/.npm test
- – Run unit and integration tests.npm run coverage
- – Get test coverage report.npm run lint
- – Run ESLint.npm run format
- – Run Prettier.npm run typecheck
- – TS type checking without emit.
- Node 18 or higher.
- A valid Tango API key.
- API Reference - Detailed API documentation
- Shape System Guide - Comprehensive guide to response shaping
- Dynamic Models Guide - ynamic shaping system\\ works.
MIT License - see LICENSE for details.
For questions, issues, or feature requests:
- Email: tango@makegov.com
- Issues: GitHub Issues
- Documentation: https://tango.makegov.com/docs/tango-node _(Coming Soon!)_
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (git checkout -b feature/amazing-feature)npm run test
3. Run tests ()git commit -m 'Add amazing feature'
4. Commit your changes ()git push origin feature/amazing-feature`)
5. Push to the branch (
6. Open a Pull Request