The @figentra/contracts package is the canonical home for every TypeScript interface and type that crosses a package boundary in your monorepo. Instead of duplicating type definitions or importing from sibling packages directly, all consuming packages declare a dependency on @figentra/contracts and import their shared types from there. This keeps your dependency graph acyclic, your types consistent, and your refactoring surface small.
Installation
Core Concepts
Contracts define the shape of data passed between packages and services — not the implementation. A contract says “this service accepts X and returns Y” without dictating how it does so. Every consuming package imports exclusively from @figentra/contracts rather than from each other, which prevents circular dependencies and makes the inter-package API surface explicit and versioned.
Think of @figentra/contracts as a shared vocabulary: if two packages need to agree on a type, that type lives here.
Built-in Types
@figentra/contracts ships several foundational types that the rest of the Figentra ecosystem builds on.
ServiceContract<T>
The base interface for any service that operates on a resource of type T. Extend this interface when defining domain-specific service contracts.
HealthStatus
The standard shape of a health check response. Used by @figentra/health and any service that exposes a /health endpoint.
ErrorContract
The standard error payload shape. Use this type when serializing errors to HTTP responses or inter-service messages.
PaginatedResponse<T>
A generic wrapper for any paginated list response. Use it whenever an endpoint returns a subset of a larger collection.
Defining Your Own Contracts
Extend the built-in types to define domain-specific contracts. The pattern below creates a UserService contract by extending ServiceContract<User>, giving you the base interface plus your domain methods.
Place this file inside @figentra/contracts/src/user.ts (or a relevant sub-module) so all packages share the exact same definition.
Usage Example
The following example shows how two packages coordinate through a shared contract without depending on each other.
Package A — @acme/users (implementation)
Package B — @acme/api (consumer)
Package B only depends on @figentra/contracts — it has no compile-time knowledge of @acme/users. You can swap the implementation at any time without touching @acme/api.
Keep contracts minimal. Only define what actually crosses a package boundary.
Internal types — those only used within a single package — belong in that
package, not here. A bloated contracts package becomes a bottleneck and forces
unnecessary rebuilds across the monorepo.