cppbessot is a CMake-driven schema pipeline for projects that keep their application data model in OpenAPI and need consistent generated artifacts across C++, TypeScript, Zod, ODB, SQL DDL, and database migrations.
The OpenAPI file under a schema directory is the single source of truth. From that, cppbessot can:
- generate C++ model headers and JSON serdes sources
- generate ODB ORM sources for SQLite and PostgreSQL
- generate SQL DDL snapshots for SQLite and PostgreSQL
- generate TypeScript types
- generate Zod schemas
- generate SQL migration artifacts between two schema versions
- build linkable generated C++ libraries
- run live DB actions against
dev,prod,proddev, ortests
The embedded module entry point is:
cmake/CppBeSSOT.cmake
The repo also ships test fixtures under:
db/test-schema-v1.1db/test-schema-v1.2
Each schema directory is expected to look roughly like this:
<CPPBESSOT_WORKDIR>/<schema-dir>/
openapi/openapi.yaml
generated-cpp-source/
generated-odb-source/
generated-sql-ddl/
generated-ts-types/
generated-zod/
Migration artifacts live under:
<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/
sqlite/
postgre/
pre-structural-backfill.sh # optional
post-structural-backfill.sh # optional
cppbessot checks its dependencies during configure.
Always required:
gitjavanpmnpxodbsqlite3CLI- SQLite development headers and client library
- PostgreSQL development headers and client library
nlohmann_json- npm packages:
@openapitools/openapi-generator-cliopenapi-zod-client
Conditionally required:
psql- required when PostgreSQL live DB actions are configured
- required when real PostgreSQL db-action tests are configured
When configuring the standalone cppbessot repo itself, DB_SCHEMA_DIR_TO_GENERATE must be set explicitly:
cmake -S cmake/cppbessot -B build-cppbessot -DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2The standalone top-level file also exposes these DB target mapping cache variables:
CPPBESSOT_DB_SQLITE_PROD_PATHCPPBESSOT_DB_SQLITE_DEV_PATHCPPBESSOT_DB_SQLITE_PRODDEV_PATHCPPBESSOT_DB_SQLITE_TESTS_PATHCPPBESSOT_DB_PGSQL_PROD_CONNSTRCPPBESSOT_DB_PGSQL_DEV_CONNSTRCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTRCPPBESSOT_DB_PGSQL_TESTS_CONNSTR
Minimal parent-project integration:
cmake_minimum_required(VERSION 3.20)
project(my_app LANGUAGES CXX)
set(CPPBESSOT_WORKDIR "db")
set(DB_SCHEMA_DIR_TO_GENERATE "v1.2")
include(path/to/cppbessot/cmake/CppBeSSOT.cmake)By default, including CppBeSSOT.cmake auto-registers all generation targets, live DB action targets, and generated libraries.
If you want manual control:
set(CPPBESSOT_AUTO_ENABLE OFF)
include(path/to/cppbessot/cmake/CppBeSSOT.cmake)
cppbessot_enable()-
CPPBESSOT_WORKDIR- default:
db - schema root folder, relative to
PROJECT_SOURCE_DIRor absolute
- default:
-
DB_SCHEMA_DIR_TO_GENERATE- default in module mode:
v1.1 - required in standalone top-level configure
- basename of the schema directory to generate artifacts for
- default in module mode:
-
DB_SCHEMA_DIR_MIGRATION_FROM- default: empty
- source schema basename for
db_gen_migrations
-
DB_SCHEMA_DIR_MIGRATION_TO- default: empty
- target schema basename for
db_gen_migrations
-
DB_SCHEMA_CHANGES_ARE_ERROR- default:
OFF - used by schema-drift checking logic
- default:
-
CPPBESSOT_AUTO_ENABLE- default:
ON - when
ON, includingCppBeSSOT.cmakeimmediately registers targets and libraries
- default:
-
DB_TARGET- default:
dev - selected live DB target for
db_createfromanddb_migrate - allowed values:
prod,proddev,dev,tests
- default:
-
DB_CREATEFROM_SCHEMA_DIR- default:
DB_SCHEMA_DIR_TO_GENERATE - schema basename whose generated SQL DDL should be used by
db_createfrom
- default:
-
DB_MIGRATE_WITH- default: empty
- migration directory basename under
<CPPBESSOT_WORKDIR>/migrations - example:
v1.1-v1.2
-
DB_MIGRATE_PRODDEV_USE_STALE- default:
OFF - when
ONandDB_TARGET=proddev, reuse the existing proddev target instead of recloning from prod
- default:
Exactly one backend mapping must be set for the selected DB_TARGET.
CPPBESSOT_DB_SQLITE_PROD_PATHCPPBESSOT_DB_SQLITE_DEV_PATHCPPBESSOT_DB_SQLITE_PRODDEV_PATHCPPBESSOT_DB_SQLITE_TESTS_PATH
These point at the SQLite DB file to act on for prod, dev, proddev, or tests.
CPPBESSOT_DB_PGSQL_PROD_CONNSTRCPPBESSOT_DB_PGSQL_DEV_CONNSTRCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTRCPPBESSOT_DB_PGSQL_TESTS_CONNSTR
These are psql-compatible PostgreSQL connection strings for prod, dev, proddev, or tests.
These are only needed for DB_TARGET=proddev when DB_MIGRATE_PRODDEV_USE_STALE=OFF.
CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMANDCPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND
They must be shell command strings that clone the prod database into proddev for the chosen backend.
All custom targets are EXCLUDE_FROM_ALL, so they run only when explicitly requested or when a dependent library is built.
Purpose:
- checks for git-tracked schema changes under
CPPBESSOT_WORKDIR
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_CHANGES_ARE_ERROR
Output:
- no generated files
- emits success/failure diagnostics about dirty schema state
Purpose:
- generates TypeScript types from
openapi/openapi.yaml
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_TO_GENERATE
Output:
<CPPBESSOT_WORKDIR>/<schema>/generated-ts-types/
Purpose:
- generates Zod schemas from
openapi/openapi.yaml
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_TO_GENERATE
Output:
<CPPBESSOT_WORKDIR>/<schema>/generated-zod/schemas.ts
Purpose:
- generates C++ model headers and JSON serdes/model sources from OpenAPI
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_TO_GENERATE
Output:
<CPPBESSOT_WORKDIR>/<schema>/generated-cpp-source/include/<CPPBESSOT_WORKDIR>/<schema>/generated-cpp-source/src/
Purpose:
- generates ODB ORM sources for both SQLite and PostgreSQL
- depends on
db_gen_cpp_headers
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_TO_GENERATE
Output:
<CPPBESSOT_WORKDIR>/<schema>/generated-odb-source/sqlite/<CPPBESSOT_WORKDIR>/<schema>/generated-odb-source/postgre/
Purpose:
- generates SQL DDL snapshots for both SQLite and PostgreSQL
- depends on
db_gen_cpp_headers
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_TO_GENERATE
Output:
<CPPBESSOT_WORKDIR>/<schema>/generated-sql-ddl/sqlite/<CPPBESSOT_WORKDIR>/<schema>/generated-sql-ddl/postgre/
Purpose:
- generates migration SQL artifacts between two schema versions
Primary variables:
CPPBESSOT_WORKDIRDB_SCHEMA_DIR_MIGRATION_FROMDB_SCHEMA_DIR_MIGRATION_TO
Output:
<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/sqlite/<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/postgre/
Notes:
fromandtomust differ- if either migration variable is empty,
db_gen_migrationsis still registered but intentionally fails with guidance
Purpose:
- aggregate generation target
Runs:
db_gen_tsdb_gen_zoddb_gen_cpp_headersdb_gen_odb_logicdb_gen_sql_ddl
Output:
- no unique output of its own
- produces the union of the five generation targets above
Purpose:
- recreates a live
devorproddatabase from pre-generated SQL DDL artifacts
Primary variables:
DB_TARGETDB_CREATEFROM_SCHEMA_DIR- one backend mapping for the selected target
Behavior:
DB_TARGET=proddevis illegal and aborts- validates that
DB_CREATEFROM_SCHEMA_DIRexists and hasopenapi/openapi.yaml - chooses backend by inspecting the selected target mapping
- requires non-empty SQL files under:
<schema>/generated-sql-ddl/sqlite/, or<schema>/generated-sql-ddl/postgre/
- SQLite path:
- deletes the current DB file if it exists
- recreates parent directories
- applies non-empty
.sqlfiles in sorted order usingsqlite3
- PostgreSQL path:
- resets the
publicschema in the target DB - applies non-empty
.sqlfiles in sorted order usingpsql
- resets the
Output:
- a recreated live database matching the chosen schema snapshot
Purpose:
- applies a generated migration directory, plus optional backfill hooks, to the selected live DB target
Primary variables:
DB_TARGETDB_MIGRATE_WITHDB_MIGRATE_PRODDEV_USE_STALE- one backend mapping for the selected target
- optional clone hook variable for
proddev
Behavior:
- validates that
<CPPBESSOT_WORKDIR>/migrations/<DB_MIGRATE_WITH>exists - chooses backend by inspecting the selected target mapping
- for
DB_TARGET=proddev:- if
DB_MIGRATE_PRODDEV_USE_STALE=OFF, runs the configured clone command first - if
DB_MIGRATE_PRODDEV_USE_STALE=ON, requires that the stale proddev target already exists
- if
- hook order:
pre-structural-backfill.shif present- non-empty structural SQL files for the selected backend if present
post-structural-backfill.shif present
- hooks run with these environment variables:
CPPBESSOT_DB_TARGETCPPBESSOT_DB_BACKENDCPPBESSOT_DB_MIGRATION_DIRCPPBESSOT_DB_MIGRATE_WITHCPPBESSOT_DB_SCHEMA_DIR_TO_GENERATECPPBESSOT_DB_CREATEFROM_SCHEMA_DIRCPPBESSOT_DB_SQLITE_PATHCPPBESSOT_DB_PGSQL_CONNSTR
Output:
- a migrated live database for the selected target
Registers:
cppBeSsotOpenAiModelGen- alias
cppbessot::openai_model_gen
Behavior:
- depends on
db_gen_cpp_headers - building a consumer that links
cppbessot::openai_model_genforces model generation first
Consumes generated output from:
<schema>/generated-cpp-source/include/<schema>/generated-cpp-source/src/
Registers:
cppBeSsotOdbSqlitecppBeSsotOdbPgSql- aliases:
cppbessot::odb_sqlitecppbessot::odb_pgsql
Behavior:
- depends on
db_gen_odb_logic - also depends on
db_gen_sql_ddlso ORM libs stay aligned with the same schema generation pass - building a consumer that links these libs forces ODB generation first
Consumes generated output from:
<schema>/generated-cpp-source/include/<schema>/generated-odb-source/sqlite/<schema>/generated-odb-source/postgre/
Registers all three generated libraries above.
This is the umbrella library-registration entry point used by cppbessot_enable().
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2
cmake --build build --target db_gen_orm_serdes_and_zodThis produces:
generated-ts-typesgenerated-zodgenerated-cpp-sourcegenerated-odb-sourcegenerated-sql-ddl
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_SCHEMA_DIR_MIGRATION_FROM=v1.1 \
-DDB_SCHEMA_DIR_MIGRATION_TO=v1.2
cmake --build build --target db_gen_migrationsThis writes migration SQL under:
db/migrations/v1.1-v1.2/sqlite/db/migrations/v1.1-v1.2/postgre/
target_link_libraries(my_app PRIVATE cppbessot::openai_model_gen)That is valid if your application wants generated C++ models and JSON serdes but does not want ODB ORM libraries.
target_link_libraries(my_app PRIVATE
cppbessot::openai_model_gen
cppbessot::odb_sqlite
cppbessot::odb_pgsql
)cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=dev \
-DCPPBESSOT_DB_SQLITE_DEV_PATH=/tmp/myapp-dev.sqlite
cmake --build build --target db_gen_sql_ddl
cmake --build build --target db_createfromThis recreates /tmp/myapp-dev.sqlite from:
db/v1.2/generated-sql-ddl/sqlite/
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=prod \
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_prod user=postgres password=postgres"
cmake --build build --target db_gen_sql_ddl
cmake --build build --target db_createfromThis resets the public schema in the target PostgreSQL DB, then reapplies the generated DDL.
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=dev \
-DDB_MIGRATE_WITH=v1.1-v1.2 \
-DCPPBESSOT_DB_SQLITE_DEV_PATH=/tmp/myapp-dev.sqlite
cmake --build build --target db_migrateIf present, these run in order:
db/migrations/v1.1-v1.2/pre-structural-backfill.sh- non-empty SQL files under
db/migrations/v1.1-v1.2/sqlite/ db/migrations/v1.1-v1.2/post-structural-backfill.sh
SQLite example:
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=proddev \
-DDB_MIGRATE_WITH=v1.1-v1.2 \
-DCPPBESSOT_DB_SQLITE_PROD_PATH=/srv/myapp/prod.sqlite \
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/srv/myapp/proddev.sqlite \
-DCPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND='cp /srv/myapp/prod.sqlite /srv/myapp/proddev.sqlite'
cmake --build build --target db_migratePostgreSQL example:
cmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=proddev \
-DDB_MIGRATE_WITH=v1.1-v1.2 \
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_prod user=postgres password=postgres" \
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_proddev user=postgres password=postgres" \
-DCPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND='psql "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres" -v ON_ERROR_STOP=1 -c "DROP DATABASE IF EXISTS myapp_proddev;" -c "CREATE DATABASE myapp_proddev TEMPLATE myapp_prod;"'
cmake --build build --target db_migratecmake -S . -B build \
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
-DDB_TARGET=proddev \
-DDB_MIGRATE_WITH=v1.1-v1.2 \
-DDB_MIGRATE_PRODDEV_USE_STALE=ON \
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/srv/myapp/proddev.sqlite
cmake --build build --target db_migrateThis skips the clone step and aborts if the stale target does not already exist.
git -C cmake/cppbessot submodule update --init --recursive tests/googletest
cmake -S cmake/cppbessot -B build-cppbessot-tests \
-DBUILD_TESTING=ON \
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2ctest --test-dir build-cppbessot-tests --output-on-failureProvide DB target mappings:
cmake -S cmake/cppbessot -B build-cppbessot-tests \
-DBUILD_TESTING=ON \
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2 \
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/tmp/cppbessot-odb.sqlite \
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_odb_test user=postgres password=postgres"These tests are only registered when all of the following are true:
BUILD_TESTING=ONpsqlis available- the required target connstr variables for the individual test are non-empty
Example:
cmake -S cmake/cppbessot -B build-cppbessot-tests \
-DBUILD_TESTING=ON \
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2 \
-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_dev user=postgres password=postgres" \
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_prod user=postgres password=postgres" \
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_proddev user=postgres password=postgres"
ctest --test-dir build-cppbessot-tests -R 'cppbessot_db_action_pgsql_.*_real' --output-on-failure- Schema directory names must be basenames, not paths.
DB_TARGETmust resolve to exactly one backend mapping.db_createfromanddb_migrateoperate on pre-generated SQL artifacts. If those artifacts are stale, regenerate them first.- The PostgreSQL live-action path resets the
publicschema. Use dedicated databases and be deliberate withprodmappings.