Skip to main content
Wildo.ai Coming soon

Storage and queries

Choose the database that fits your application

Keep the same resource model while PostgreSQL or MongoDB provides its storage through the shared repository layer.

One task definition can be used with PostgreSQL or MongoDB storage.

Choose the database that fits your application

A customer, invoice or task keeps its business meaning whichever database stores it. Wildo separates the definition of that record from the database implementation, so your application can use PostgreSQL or MongoDB without introducing database-driver calls throughout its resource services.

The schema still describes the fields, relationships and constraints. Operations still describe what people can do. The repository layer translates those decisions into the selected database’s queries and writes.

This helps an application fit an existing technology estate and gives the team a consistent way to work with its records. Database administration, migrations and moving existing data remain deliberate parts of the deployment.

Example — Use the company’s PostgreSQL estate

An internal task application can use PostgreSQL because that is the database the company already operates. Its task definitions, permissions and interfaces continue to use Wildo’s resource model; the deployment configuration selects the store that implements it.

For engineers

Select the application store in the environment

The application’s applicationDatabase selection belongs in its infrastructure environment. The platform’s own database selection is a separate setting: the service managing applications and the application’s business records need not share a database engine.

Wonder Todos declares both selections in infrastructure/local/wildo.infra.local.config.ts. This excerpt keeps the relevant properties of the environment configuration; its other backing services are omitted:

database: { engine: DatabaseEngine.POSTGRESQL, source: BackingServiceSource.SELF },
applicationDatabase: { engine: DatabaseEngine.POSTGRESQL },
backingServices: {
  postgresql: {
    source: BackingServiceSource.SELF,
    host: 'localhost',
    port: 5432,
    database: 'wonder-todos-db',
  },
},
SettingWhat it selects
databaseThe platform’s own database engine and source.
applicationDatabaseThe database engine used for this application’s stored resources by default.
backingServices.postgresqlThe PostgreSQL service that this environment makes available.

These are environment properties, not fields to paste into a resource schema. Selecting an engine and supplying its service configuration are separate parts of setup; neither converts existing records from another database.

Resolve the adapter consistently

Repository construction resolves a resource’s explicit adapter first, then the available defaults. This complete resolver excerpt from resolve-persistence-adapter.backend.utils.ts shows the order; source comments are omitted:

export function resolveDefaultPersistenceAdapter(
  configService: PersistenceAdapterConfigSource | undefined | null,
): PersistenceAdapterType {
  if (!configService) return DEFAULT_PERSISTENCE_ADAPTER;

  const platformOverride = configService.platformModeDefaultPersistenceAdapter;
  if (platformOverride !== undefined) return platformOverride;

  if (!configService.isInitialized?.()) return DEFAULT_PERSISTENCE_ADAPTER;
  try {
    return configService.config?.database?.defaultAdapter ?? DEFAULT_PERSISTENCE_ADAPTER;
  } catch {
    return DEFAULT_PERSISTENCE_ADAPTER;
  }
}

export function resolvePersistenceAdapterForConfiguration(
  declaredAdapter: PersistenceAdapterType | undefined,
  configService: PersistenceAdapterConfigSource | undefined | null,
): PersistenceAdapterType {
  return declaredAdapter ?? resolveDefaultPersistenceAdapter(configService);
}

The first function resolves the default, including the platform bootstrap override used before normal configuration is initialized. The second lets a resource state its own adapter. Repository creation, transaction routing and other database consumers use this common resolution rather than each interpreting configuration independently.

Work through the repository contract

The stored adapters implement BackendResourceRepository: ordinary reads and writes, collections and the additional primitives used by framework services. MongoDB uses Mongoose; PostgreSQL uses Kysely. Shared filters carry resource and caller context into the adapter, which compiles the query into its own database vocabulary.

Keep application operations at that contract when they need portable behavior. A deliberately database-specific query belongs to a consciously selected implementation, with the database choice apparent to its author.

Plan physical changes with the application

PostgreSQL table, column and index changes travel through migrations derived from the resource plan. MongoDB builds its storage and reconciles indexes through its own initialization path. A schema default affects new values; it is not an instruction to backfill every existing row.

Use one stored adapter for work that must be atomic together. A resource-level exception requires an explicit isolation declaration, and a transaction spanning different adapters is rejected. Changing the selected database changes how repositories are constructed; moving an existing dataset is a separate migration task.

The virtual resource and introspection adapters use the resource interface for sources that do not keep ordinary local rows.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.