Library / SDK
fnc12/sqlite_orm avatar
fnc12/sqlite_orm

sqlite_orm: a header-only C++ ORM for SQLite that maps tables to structs

❤️ SQLite ORM light header only library for modern C++

2,693 stars343 forksC++AGPL-3.0

At a glance

What is it?
sqlite_orm is a single-header C++14/17/20 library that turns SQLite tables into C++ structs with a fluent CRUD API. It is fast to adopt, but the AGPL-3.0 licence and the absence of a documented rollback story shape who can use it.
Who is it for?
Adopt sqlite_orm if you write C++14 or newer, want typed CRUD over SQLite without hand-written SQL strings, and can live with AGPL-3.0 or buy the MIT licence. Do not adopt it for a closed-source product unless the 50 USD commercial licence is acceptable, and do not expect it to hide SQLite's own limits.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What sqlite_orm solves, and who it is for

The problem is the gap between a C++ struct and a SQLite table. Without a mapping layer, every insert, select and update is a string that the compiler cannot check, and every schema change is a manual edit in two places. sqlite_orm closes that gap by letting you describe the schema once, in C++, and then query through the same description. The README states the goal plainly: "No raw string queries" and "one code line per single query".

The intended user is a C++ developer on C++14 or newer who already has SQLite in the build and wants typed access to it. The README lists the dependency as exactly one thing, libsqlite3, and describes the library as header-only, so there is no separate runtime to link or ship. That combination is the selling point: a single header plus the SQLite library you were going to link anyway.

The library is not a database engine and does not try to be. It manages objects with a primary key and without one, per the README, and it supports CRUD, pure select queries, prepared statements, UNION, EXCEPT and INTERSECT, foreign keys, composite keys, JOIN, transactions, migrations, indexes and user-defined functions. That list is broad, but each item is a thin typed wrapper over something SQLite already does.

How the storage object maps structs to tables

The mechanism is a storage object built from table and column descriptors. You pass a database filename and a list of make_table calls; each make_table takes a table name and a list of make_column calls; each make_column takes the column name in the database and a pointer to a member of your struct. The mapped type is deduced from the member pointer, so you never write the SQL type by hand. The README gives this example, where User and UserType are plain structs with no ORM base class:

c++
using namespace sqlite_orm;
auto storage = make_storage("db.sqlite",
                            make_table("users",
                                       make_column("id", &User::id, primary_key().autoincrement()),
                                       make_column("first_name", &User::firstName),
                                       make_column("last_name", &User::lastName),
                                       make_column("birth_date", &User::birthDate),
                                       make_column("image_url", &User::imageUrl),
                                       make_column("type_id", &User::typeId)),
                            make_table("user_types",
                                       make_column("id", &UserType::id, primary_key().autoincrement()),
                                       make_column("name", &UserType::name, default_value("name_placeholder"))));

Column constraints are extra arguments: primary_key, autoincrement, default_value, unique and generated_always_as. The README notes that not_null and null are deduced from the type but can be added manually with null() and not_null(), and that the order of arguments does not matter. The storage object then carries the CRUD interface, so queries are issued against it rather than against a raw sqlite3 handle.

The design choice worth noticing is the single responsibility principle the README claims: your data model classes stay free of ORM code. User has no base class, no macros and no generated files. The cost is that the mapping lives entirely in the make_storage call, and a struct with private or protected members needs setter and getter functions instead of member pointers, which the README points to an example for.

Installing sqlite_orm and running a first query

There is no package manager step documented in the README. The repository ships CMakeLists.txt, a cmake/ directory, a vcpkg/ directory and two example folders named examples/fetch_content/ and examples/find_package/, which is where the integration paths are described. The dependency to satisfy first is libsqlite3.

The integration route is CMake. The README does not print the CMake commands themselves, so read examples/fetch_content/ and examples/find_package/ for the exact calls; the repository also carries a CMakeLists.txt at the root and a cmake/ directory. Both routes assume libsqlite3 is already available to the build.

With the storage built as in the previous section, the first useful call is a query rather than an insert, because it proves the mapping matches the existing schema. The README describes selects as pure select query support and shows conditions, ORDER BY, LIMIT and OFFSET as supported. A select through the storage returns typed objects, so a mismatch between the column name in make_column and the real table shows up as a SQLite error at runtime, not at compile time. That is the first thing to check: run one select against a table you know, and confirm the row count matches what sqlite3 reports for the same file.

Where sqlite_orm stops, and where it is the wrong tool

The licence is the first hard boundary. The README says the project has AGPL-3.0 for open source projects and an MIT licence after purchasing it for 50 USD, with COMM-LICENSE and LICENSE both present at the repository root. For a closed-source product, AGPL-3.0 is not a licence you can quietly ignore, and the commercial option is a purchase rather than a checkbox. The README also says to read the licence precisely, which is fair warning that the terms are not boilerplate.

The second boundary is the database. sqlite_orm is a mapping layer, so every concurrency limit, write-lock behaviour and type-affinity quirk of SQLite is still yours to handle. If your workload needs many concurrent writers, a client-server database is the right answer and no ORM changes that.

The third is the type system. Because the mapped type is deduced from a member pointer, a mismatch between your struct and the actual schema is a runtime error. The README does not describe a schema-validation step that would catch it earlier, and it does not document what happens when a migration step fails. Migrations are listed as a feature, but rollback is not documented, so a failed migration is a case you have to plan around yourself rather than assume the library handles. If you need compile-time schema checking against a live database, this library does not offer it.

sqlite_orm compared with raw SQLite and with generated bindings

The nearest alternative is not another ORM but the C API you already have. sqlite3_prepare_v2 plus sqlite3_bind and sqlite3_column gives you full control, no licence question, and no template instantiation cost. The difference in approach is where the schema lives: with raw SQLite it lives in SQL strings scattered through your code, and with sqlite_orm it lives in one make_storage expression that the compiler type-checks against your structs. If your queries are dynamic enough that the storage description would need constant editing, the ORM is overhead and the C API is simpler.

The other alternative is a code generator that reads a schema file and emits C++ classes. That approach gives you a schema that exists outside the C++ source, which helps when a DBA or another language owns the schema. sqlite_orm takes the opposite position: the C++ types are the schema definition, and the database is derived from them. Choose the generator when the schema is the source of truth; choose sqlite_orm when the C++ model is.

Note that the related searches show people looking for sqlite ORM bindings in Python, Rust, TypeScript, Node.js, JavaScript, Flutter and Dart. sqlite_orm is none of those. It is a C++ library, and the only language it exposes is C++14 and newer.

Maintenance, releases and upgrade cost

The repository is not archived, and the last push was on 2026-09-21, which is recent. The release history is slower than the commit history: v1.9.1 on 2025-02-03, v1.9 on 2024-08-24, and v1.8.2 on 2023-03-24. That pattern suggests development continues on the master and dev branches between tagged releases, so pinning to a release tag gives you stability and pinning to master gives you newer features with less certainty.

The upgrade cost is mostly compile time. The library is header-only and built on C++14/17/20 template features, so every translation unit that includes it pays the instantiation cost. There is no compiled library to link once and share. On a large project that is a real build-time line item, and it is the main reason to keep the include behind a narrow interface rather than spreading it across the codebase.

On licensing, the practical point is that the AGPL-3.0 terms and the 50 USD MIT option are both stated in the README, and the repository carries COMM-LICENSE and LICENSE files. If your product is closed source, resolve that question before writing code against the library, not after. This is a description of what the repository states, not legal advice.

Editorial conclusion

Adopt sqlite_orm if you write C++14 or newer, want typed CRUD over SQLite without hand-written SQL strings, and can live with AGPL-3.0 or buy the MIT licence. Do not adopt it for a closed-source product unless the 50 USD commercial licence is acceptable, and do not expect it to hide SQLite's own limits. Before committing, verify two things in the repository: that the CMake integration path you choose (FetchContent or find_package) actually resolves libsqlite3 on your platform, and what the migration API does when a migration step fails, since the README does not document rollback.

Frequently asked questions

What is sqlite_orm?

It is a header-only C++ library that maps SQLite tables to C++ structs and provides a typed CRUD interface. The README describes it as a light header-only library for modern C++ with no raw string queries, and lists libsqlite3 as its only dependency.

Is sqlite_orm a C++ library?

Yes. The README states it is built with modern C++14/C++17/C++20 features and uses no macros or external scripts. The repository topics include cplusplus, cplusplus-14 and modern-cpp.

How do I install sqlite_orm in a CMake project?

The README does not give install steps, but the repository ships examples/fetch_content/ and examples/find_package/ for the two integration routes, plus a cmake/ directory. Either way, libsqlite3 is the dependency you must satisfy.

What licence does sqlite_orm use?

The README states the project has an AGPL licence for open source projects and an MIT licence after purchasing it for 50 USD. Both LICENSE and COMM-LICENSE files are present at the repository root.

Can sqlite_orm map a class with private members?

Yes. The README says that if your data model classes have private or protected members, you can build a storage with setter and getter functions instead of member pointers, and points to examples/private_class_members.cpp.

Official sources

  1. fnc12/sqlite_orm on GitHub
  2. Issues
  3. License: AGPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fnc12-sqlite-orm.svg)](https://hysenlabs.com/projects/fnc12-sqlite-orm)