CommonGrants Protocol Data Models: Zod Schemas for Grant Opportunities
The CommonGrants protocol defines grant opportunity data as Zod schemas, including OpportunityBase, OppStatus, OppFunding, OppTimeline, and filtering/sorting models, implemented in both the Express.js reference API and TypeScript SDK.
The CommonGrants protocol provides a standardized way to represent grant opportunities through strict TypeScript validation. In the hhs/simpler-grants-protocol repository, every element of a grant is modeled as a Zod schema, ensuring runtime type safety across the reference API and client SDK.
Core Data Models in the CommonGrants Protocol
The protocol centers on the OpportunityBase model, which composes several embedded entities describing status, funding, timeline, and eligibility.
OpportunityBase
The OpportunityBaseSchema serves as the central object combining all grant metadata. Defined in lib/ts-sdk/src/schemas/zod/models.ts (lines 15-45) and mirrored in templates/express-js/src/api/schemas/models.ts (lines 78-105), this schema includes:
- A UUID identifier validated by UuidSchema
- Title and description fields
- Embedded OppStatus, OppFunding, and OppTimeline objects
- Source URL and custom fields map
- SystemMetadataSchema for created/modified timestamps
OppStatus
The OppStatusSchema enumerates opportunity states: forecasted, open, closed, or custom. When using the custom value, the schema accepts an optional customValue string and description field. This schema appears in both templates/express-js/src/api/schemas/models.ts (lines 14-29) and lib/ts-sdk/src/schemas/zod/models.ts (lines 15-26).
OppFunding
Financial details are encapsulated in OppFundingSchema, found in lib/ts-sdk/src/schemas/zod/models.ts (lines 69-92). This model tracks:
- Total amount available via MoneySchema
- Minimum and maximum award amounts
- Award count ranges (minimum and maximum)
- Optional free-form
detailsstring
OppTimeline
The OppTimelineSchema holds critical dates including post/open date, close date, and any additional otherDates array. This schema ensures consistent date formatting across implementations and appears in both the Express.js and SDK versions of models.ts.
ApplicantType
The ApplicantTypeSchema appears exclusively in the SDK at lib/ts-sdk/src/schemas/zod/models.ts (lines 32-64). It categorizes eligible applicants as individuals, organizations, or various government levels (local, state, federal), with a custom fallback for implementation-specific categories.
Query and Filter Models
Beyond base opportunity data, the CommonGrants protocol defines schemas for searching and filtering grant listings.
OppSortBy and OppSorting
The OppSortByEnum (or oppSortByEnum in Express.js) enumerates sortable fields: lastModifiedAt, title, status.value, keyDates.closeDate, and funding amounts. Defined in lib/ts-sdk/src/schemas/zod/models.ts (lines 50-61), this enum drives the sorting capabilities of the protocol.
The OppSortingSchema wraps a sortBy value with an optional sortOrder (asc/desc). The SDK version additionally supports a customSortBy string for implementation-specific sorting keys.
OppDefaultFilters and OppFilters
OppDefaultFiltersSchema provides pre-defined filter parameters including status arrays, close-date ranges, and funding amount ranges. OppFiltersSchema extends these defaults with an optional customFilters map, allowing any implementation-defined filter fields. These schemas enable the searchable API endpoints implemented in lib/ts-sdk/src/client/opportunities.ts.
Reusable Field Schemas
The protocol relies on auxiliary building blocks defined in lib/ts-sdk/src/schemas/zod/fields.ts and lib/ts-sdk/src/schemas/zod/filters.ts:
- EventSchema: Represents date events with descriptions
- MoneySchema: Combines numeric amount and ISO currency code
- CustomFieldSchema: Supports arbitrary key-value pairs with type annotations (
string,enum, etc.) - SystemMetadataSchema: Tracks creation and modification timestamps
- UuidSchema: Validates UUIDv4 identifiers
- Filter schemas:
StringArrayFilterSchema,DateRangeFilterSchema, etc.
Implementation Differences: Express.js vs. TypeScript SDK
While both implementations share logical entity definitions, the TypeScript SDK (lib/ts-sdk) differs from the Express.js reference API (templates/express-js) in two key ways:
- Nullability: The SDK uses
nullish()instead ofoptional(), allowing explicit null values rather than undefined - Applicant Types: Only the SDK includes the ApplicantTypeSchema, as applicant categorization primarily concerns client-side filtering and display logic
Working with CommonGrants Data Models
Developers interact with these schemas through runtime validation and TypeScript type inference (z.infer<typeof Schema>).
Validate a Raw Opportunity Payload
import {
OpportunityBaseSchema,
} from "@hhs/simpler-grants-protocol-sdk";
// Example payload (could come from an HTTP request, a file, etc.)
const raw = {
id: "d5f5e5c2-4c8b-4b1f-9a5f-0b9afc3a1e57",
title: "Community Health Grant 2025",
status: { value: "open" },
description: "Funding for community health initiatives.",
funding: { totalAmountAvailable: { amount: 500000, currency: "USD" } },
keyDates: { postDate: { date: "2024-11-01T00:00:00Z" } },
source: "https://example.org/grants/123",
};
try {
const opportunity = OpportunityBaseSchema.parse(raw);
console.log("✅ Valid opportunity:", opportunity);
} catch (e) {
console.error("❌ Validation error:", e);
}
This validates the structure against the OpportunityBaseSchema definition in lib/ts-sdk/src/schemas/zod/models.ts.
Build a Search Request with Sorting and Filters
import {
OppSortingSchema,
OppFiltersSchema,
} from "@hhs/simpler-grants-protocol-sdk";
const sorting = OppSortingSchema.parse({
sortBy: "funding.maxAwardAmount",
sortOrder: "desc",
});
const filters = OppFiltersSchema.parse({
status: { in: ["open", "forecasted"] },
totalFundingAvailableRange: { gte: 100000, lte: 1000000 },
});
console.log("Sorting:", sorting);
console.log("Filters:", filters);
These schemas correspond to the definitions in lib/ts-sdk/src/schemas/zod/models.ts lines 50-89.
Extend the Base Model with Custom Fields
import { OpportunityBaseSchema } from "@hhs/simpler-grants-protocol-sdk";
const opportunityWithCustom = OpportunityBaseSchema.parse({
id: "a1b2c3d4-5678-90ab-cdef-1234567890ab",
title: "Education Innovation Grant",
status: { value: "custom", customValue: "pending_review" },
description: "Supports novel educational tools.",
customFields: {
// Arbitrary key/value pairs defined by the implementer
programArea: { type: "string", value: "STEM" },
eligibility: { type: "enum", value: "non-profit" },
},
});
The customFields property accepts any key-value pairs conforming to CustomFieldSchema, as defined in templates/express-js/src/api/schemas/models.ts and mirrored in the SDK.
Summary
- The CommonGrants protocol data models are implemented as Zod schemas in
hhs/simpler-grants-protocol - OpportunityBaseSchema serves as the central entity, composing status, funding, and timeline data via embedded objects
- ApplicantTypeSchema, OppSortingSchema, and OppFiltersSchema support client-side filtering, sorting, and eligibility categorization
- Schemas exist in parallel implementations: the Express.js reference API and the TypeScript SDK (which uses
nullishand adds applicant types) - All models provide runtime validation via
.parse()and TypeScript type inference for compile-time safety
Frequently Asked Questions
What is the primary schema for grant opportunities in the CommonGrants protocol?
The OpportunityBaseSchema is the primary schema representing a grant opportunity. Defined in lib/ts-sdk/src/schemas/zod/models.ts and templates/express-js/src/api/schemas/models.ts, it combines metadata (UUID, title, description), embedded objects for status and funding, and system metadata like creation timestamps.
How does the TypeScript SDK differ from the Express.js reference implementation?
The TypeScript SDK uses nullish() instead of optional() for optional fields, allowing explicit null values. It also includes the ApplicantTypeSchema for categorizing eligible applicants, which does not appear in the Express.js template. Both implementations maintain identical logical structures for core opportunity data.
Can I add custom fields to grant opportunities?
Yes. The OpportunityBaseSchema includes a customFields property that accepts any key-value pairs conforming to CustomFieldSchema. This allows implementations to extend the base model with program-specific data while maintaining protocol compliance.
Where are the filter and sorting schemas defined?
The OppSortingSchema and OppFiltersSchema are defined in lib/ts-sdk/src/schemas/zod/models.ts (lines 50-96) and mirrored in templates/express-js/src/api/schemas/models.ts. These schemas support querying opportunities by status, funding ranges, dates, and custom filter parameters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →