CREA DDF is a TypeScript package for working with the CREA DDF API, syncing the feed into Postgres, and reading that synced data from frontend applications.
The core flow is:
CREA DDF API -> syncDdfDatabaseOnce -> Postgres/Drizzle tables -> DdfDbClient -> frontend
You can still use the API client directly for live DDF reads, but the normal application path is now database-first: run the sync job, store the normalized listing/member/office/open-house data, then let the frontend query the local database through the exported DB client.
- Bun
1.3.0or newer - Node.js
24.14.0or newer - PostgreSQL
- CREA DDF API credentials
bun installThe install runs the Effect TypeScript patch through the package prepare script. Keep that in place; the Effect LSP warnings are useful and catch real SDK mistakes.
Create a local .env file or export these variables in your shell:
DATABASE_URL="postgres://user:password@localhost:5432/crea_ddf"
CREA_DDF_CLIENT_ID="your-client-id"
CREA_DDF_CLIENT_SECRET="your-client-secret"
# Optional defaults
CREA_DDF_BASE_URL="https://ddfapi.realtor.ca"
CREA_DDF_AUTH_URL="https://identity.crea.ca/connect/token"
CREA_ANALYTICS_URL=""
# Optional database-sync scope. Accepts comma-separated values or a JSON array.
CREA_CHOSEN_AOR_KEYS="76,77,93"CREA_DDF_BASE_URL should be the API host only, not /odata/v1.
Run migrations before the first sync:
bun run db:migrateUseful database commands:
bun run db:generate
bun run db:push
bun run db:studiodb:generate creates migrations after schema changes. db:migrate applies the checked-in migrations. db:studio opens Drizzle Studio against DATABASE_URL.
Run a full database sync with file telemetry:
bun run sync:otelThat command:
- authenticates with CREA DDF
- runs database migrations
- syncs destinations, properties, members, offices, and open houses
- saves database watermarks for incremental property/member/office sync
- records sync runs and sync errors
- writes logs, spans, and Prometheus-style metrics under
.otel/
Open houses do not have a DDF replication endpoint, so they are synced from the listings already stored in the database and default to a 30-day date window.
Use the ./db export in server loaders, API routes, or other backend code that feeds the frontend. The frontend should query your app server; it should not receive CREA credentials.
import { Effect, Layer } from "effect";
import { DdfDatabase, DdfDbClient } from "crea-ddf/db";
const dbReadLayer = DdfDbClient.layer.pipe(
Layer.provide(DdfDatabase.layerConfig),
);
const listingsForSearchPage = Effect.gen(function* () {
const ddf = yield* DdfDbClient;
return yield* ddf.properties.list({
filters: {
active: true,
city: "Toronto",
province: "Ontario",
minBedrooms: 2,
},
include: {
media: { select: ["mediaUrl", "preferredPhoto"] },
listOffice: { select: ["officeName", "phone"] },
},
limit: 24,
orderBy: [{ field: "modificationTimestamp", direction: "desc" }],
});
}).pipe(Effect.provide(dbReadLayer));The database client also exposes members, offices, openHouses, and destinations, with typed filters, field projections, pagination, and includes.
For direct DDF API reads, provide the DDF layer and call the resource helpers:
import { Effect } from "effect";
import { listProperties, makeDdfLayerFromEnv } from "crea-ddf";
const program = Effect.gen(function* () {
return yield* listProperties({
top: 10,
orderby: ["ModificationTimestamp desc", "ListingKey asc"],
});
}).pipe(Effect.provide(makeDdfLayerFromEnv));Prefer the database path for frontend search and detail pages once sync is enabled.
bun run typecheck
bun run test
bun run build
bun run check:metadataLive CREA checks are opt-in and require credentials:
bun run test:liveGenerated build output goes to dist/. Database migrations are copied to dist/db/migrations during bun run build so the published package can run migrations at runtime.
Start with @docs/README.md for the full SDK/API guide and @docs/09-current-implementation.md for the current package, runtime, database, sync, telemetry, and test status.
Built by Warya Wayne, @waryawayne.
- GitHub: @WaryaWayne
- X: @waryawayne