@common-grants/sdk
v0.4.0
Published
The CommonGrants protocol TypeScript SDK
Downloads
223
Readme
@common-grants/sdk
The CommonGrants protocol TypeScript SDK.
Table of contents
Installation
npm install @common-grants/sdkUsage
API Client
The SDK provides an HTTP client for interacting with CommonGrants-compatible APIs:
import { Client, Auth } from "@common-grants/sdk/client";
// Create a client with API key authentication
const client = new Client({
baseUrl: "https://api.example.org",
auth: Auth.apiKey("your-api-key"),
});
// List opportunities (auto-paginates by default)
const allOpportunities = await client.opportunities.list();
console.log(`Found ${allOpportunities.items.length} opportunities`);
for (const opp of allOpportunities.items) {
console.log(`${opp.id}: ${opp.title}`);
}
// List a specific page
const page2 = await client.opportunities.list({ page: 2, pageSize: 10 });
for (const opp of page2.items) {
console.log(`${opp.id}: ${opp.title}`);
}
// Search opportunities with filters
const results = await client.opportunities.search({
query: "education",
statuses: ["open"],
});
// View details for a single opportunity
const opportunityId = allOpportunities.items[0].id;
const opportunity = await client.opportunities.get(opportunityId);
console.log(opportunity.title);Authentication
The client supports multiple authentication methods:
// API Key (default header: X-API-Key)
Auth.apiKey("your-api-key");
// API Key with custom header
Auth.apiKey("your-api-key", "X-Custom-Header");
// Bearer token
Auth.bearer("your-jwt-token");
// No authentication
Auth.none();Client Configuration
const client = new Client({
baseUrl: "https://api.example.org", // Required (or set CG_API_BASE_URL env var)
auth: Auth.apiKey("key"), // Optional: Authentication method
timeout: 30000, // Optional: Request timeout in ms (default: 30000)
pageSize: 100, // Optional: Default page size (default: 100)
maxItems: 1000, // Optional: Max items for auto-pagination (default: 1000)
});Config values can also be set via environment variables:
| Config | Environment Variable | Default |
| ---------- | ------------------------- | ---------- |
| baseUrl | CG_API_BASE_URL | required |
| timeout | CG_API_TIMEOUT | 30000 |
| pageSize | CG_API_PAGE_SIZE | 100 |
| maxItems | CG_API_LIST_ITEMS_LIMIT | 1000 |
Low-level HTTP Methods
For custom endpoints or advanced use cases, use the low-level methods:
// GET request with query params
const response = await client.get("/custom/endpoint", {
params: { filter: "value" },
});
const data = await response.json();
// POST request with body
const response = await client.post("/custom/endpoint", {
field: "value",
});
// Raw fetch for full control
const response = await client.fetch("/custom/endpoint", {
method: "PUT",
body: JSON.stringify({ field: "value" }),
});Note: For runnable examples, see the examples folder.
Validation
Use the SDK schemas to validate JSON data and convert it to a typed object:
import * as Schemas from "@common-grants/sdk/schemas";
import * as Types from "@common-grants/sdk/types";
// Successful validation
const oppData = {
id: "ac201443-5480-4e36-9799-a39765225153",
title: "Test Opportunity",
description: "This is a test opportunity",
status: { value: "open" },
createdAt: "2025-01-01T00:00:00Z",
lastModifiedAt: "2025-01-01T00:00:00Z",
};
const opportunity: Types.OpportunityBase = Schemas.OpportunityBaseSchema.parse(oppData);
console.log(opportunity.title);
console.log(opportunity.createdAt.getFullYear());Which should print:
Test Opportunity
2025You can also gracefully handle errors using the safeParse() method:
import * as Schemas from "@common-grants/sdk/schemas";
import * as Types from "@common-grants/sdk/types";
// Validation error
const invalidOppData = {
id: "invalid-id",
title: "Test Opportunity",
description: "This is a test opportunity",
status: { value: "invalid-status" },
createdAt: "2025-01-01T00:00:00Z",
lastModifiedAt: "2025-01-01T00:00:00Z",
};
const result = Schemas.OpportunityBaseSchema.safeParse(invalidOppData);
if (!result.success) {
console.error(result.error.message);
} else {
console.log("Validation successful");
}Which should print:
[
{
"validation": "uuid",
"code": "invalid_string",
"message": "Invalid uuid",
"path": ["id"]
},
{
"received": "invalid-status",
"code": "invalid_enum_value",
"options": ["forecasted", "open", "closed", "custom"],
"path": ["status", "value"],
"message": "Invalid enum value. Expected 'forecasted' | 'open' | 'closed' | 'custom', received 'invalid-status'"
}
]Type-safe code
Import TypeScript types and runtime constants for consistent, type-safe operations:
import type { OpportunityBase, OppStatus } from "@common-grants/sdk/types";
import { OppStatusOptions } from "@common-grants/sdk/constants";
function processOpportunity(opp: OpportunityBase): void {
console.log(opp.title);
// Use constants instead of magic strings for better type safety
if (opp.status.value === OppStatusOptions.open) {
console.log("Opportunity is open");
}
// Type-safe status handling
const statusLabel = getStatusLabel(opp.status);
console.log(statusLabel);
}
function getStatusLabel(status: OppStatus): string {
switch (status.value) {
case OppStatusOptions.open:
return "Currently accepting applications";
case OppStatusOptions.closed:
return "Not accepting applications";
case OppStatusOptions.forecasted:
return "Coming soon";
default:
return "Unknown status";
}
}
// Example usage
const opp: OpportunityBase = {
id: "123",
title: "Test Opportunity",
description: "This is a test opportunity",
status: { value: OppStatusOptions.open },
createdAt: new Date(),
lastModifiedAt: new Date(),
};
processOpportunity(opp);Which should print:
Test Opportunity
Opportunity is open
Currently accepting applicationsGeneric response schemas
The SDK provides generic response schemas that allow you to flexibly validate API responses for different resource types:
import { PaginatedSchema, OpportunityBaseSchema } from "@common-grants/sdk/schemas";
// Create a paginated response schema for opportunities
const PaginatedOpportunitiesSchema = PaginatedSchema(OpportunityBaseSchema);
// Validate a paginated API response
const paginatedResponse = PaginatedOpportunitiesSchema.parse({
status: 200,
message: "Success",
items: [
{
id: "ac201443-5480-4e36-9799-a39765225153",
title: "Test Opportunity 1",
description: "This is a test opportunity",
status: { value: "open" },
createdAt: "2025-01-01T00:00:00Z",
lastModifiedAt: "2025-01-01T00:00:00Z",
},
{
id: "bc201443-5480-4e36-9799-a39765225154",
title: "Test Opportunity 2",
description: "Another test opportunity",
status: { value: "open" },
createdAt: "2025-01-02T00:00:00Z",
lastModifiedAt: "2025-01-02T00:00:00Z",
},
],
paginationInfo: {
page: 1,
pageSize: 20,
totalItems: 100,
totalPages: 5,
},
});
// Access the validated data
console.log(
`Page ${paginatedResponse.paginationInfo.page} of ${paginatedResponse.paginationInfo.totalPages}`
);
console.log(`Found ${paginatedResponse.items.length} opportunities`);Which should print:
Page 1 of 5
Found 2 opportunitiesOther generic response schemas include OkSchema<T>, SortedSchema<T>, FilteredSchema<ItemsT, FilterT>, CreatedSchema<T>, and error schemas like ErrorSchema, UnauthorizedSchema, and NotFoundSchema.
Extensions and Plugins
The SDK provides an extension framework for adding typed custom fields to CommonGrants schemas, either ad hoc with withCustomFields() or as reusable plugins with definePlugin(). See the Extensions documentation for the full guide, including best practices for publishing plugin packages and a complete API reference.
For runnable examples, see custom-fields.ts and plugins.ts.
License
CC0-1.0
