Skip to content

imranakki/NexusORM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NexusORM

NexusORM is a lightweight, high-performance, compiler-based ORM (Object-Relational Mapping) generator written entirely in modern C++20.

Rather than relying on heavy reflection mechanisms or brittle runtime macros, NexusORM uses a bespoke compiler architecture to parse a minimal Domain Specific Language (DSL) and automatically emit strongly-typed, dependency-free C++ structs, repositories, Eloquent-style query builders, and raw SQL schemas.


📑 Table of Contents

  1. Features
  2. Installation & Building
  3. ⚡ End-to-End Quickstart: Installation → Execution
  4. 🚀 For End-Users: Using NexusORM
  5. 🛠️ For Developers: Compiler Architecture
  6. Roadmap

✨ Features

  • Blazing Fast: Generates zero-overhead C++ code. Queries execute natively via libpqxx (PostgreSQL) or equivalents without reflection mappings.
  • Custom DSL: Define your schema cleanly using .nexus files instead of bloated XML or annotations.
  • Enum Types: First-class enum support in the DSL — maps to native ENUM in SQL and enum class in C++ with automatic pqxx binding.
  • Eloquent-Style Query Builder: Chainable methods like .where(), .orWhere(), .orderBy(), and .limit().
  • Live Migrations: Built-in CLI arguments (--migrate, --force) to run generated SQL directly against your Database using your system's native clients.
  • Environment Aware: Automatic .env parsing incorporated generated code for database connection pooling.
  • Smart Types & Nuances: Native mapping to std::optional for nullable fields, and UPSERT default logic (ON CONFLICT DO UPDATE).
  • Colored CLI Output: Beautiful ANSI-colored terminal output for build steps, successes, warnings, and errors.

📦 Installation & Building

Tip

For a complete step-by-step guide including Ubuntu, Arch, macOS setup, CMake integration, and troubleshooting — see docs/INSTALL.md.

Quick Start

# 1. Clone
git clone https://github.com/imranakki/NexusORM.git
cd NexusORM

# 2. Install dependencies (Ubuntu/Debian)
sudo apt-get install build-essential cmake libpqxx-dev libpq-dev postgresql-client

# 3. Build
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel

# 4. (Optional) Install globally
sudo cp ./build/nexus /usr/local/bin/nexus

⚡ End-to-End Quickstart: Installation → Execution

This is the complete zero-to-running flow for using NexusORM on a fresh machine. Follow every step in order.

Step 1 — Install system dependencies

# Ubuntu / Debian
sudo apt-get update
sudo apt-get install -y \
    build-essential cmake git \
    libpqxx-dev libpq-dev \
    postgresql-client \
    libmysqlclient-dev default-mysql-client

Tip

On Arch Linux: sudo pacman -S base-devel cmake git libpqxx postgresql-libs mysql-libs
On macOS: brew install cmake libpqxx postgresql mysql-client


Step 2 — Clone & build the compiler

git clone https://github.com/imranakki/NexusORM.git
cd NexusORM

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel

# Confirm the binary works
./build/nexus version

Step 3 — (Optional) Install globally

sudo cp ./build/nexus /usr/local/bin/nexus
nexus version   # works from any directory now

Step 4 — Write your schema

Create a file called schema.nexus in your project directory:

enum Role { Admin, Editor, Viewer }

table User {
    id:         uuid     primary key
    username:   string   unique
    email:      string   unique
    bio:        text     null
    role:       Role
    is_active:  boolean
    created_at: datetime
}

table Post {
    id:       uuid   primary key
    title:    string index
    body:     text
    views:    int
}

Step 5 — Generate SQL + C++ header

# Targeting PostgreSQL
nexus generate schema.nexus --db=postgres --out=./

# Targeting MySQL
nexus generate schema.nexus --db=mysql --out=./

This produces:

  • schema_postgres.sql (or schema_mysql.sql) — ready-to-run DDL (CREATE TABLE, CREATE TYPE, indexes)
  • schema.h — self-contained C++ ORM header (zero dependencies except libpqxx/libmysqlclient)

Step 6 — Configure your database connection

Create a .env file at your project root (NexusORM loads it automatically):

# PostgreSQL
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASS=secret
PG_NAME=myapp

# — OR — a single URL:
# DATABASE_URL=postgresql://postgres:secret@localhost:5432/myapp

Step 7 — Apply the migration to your database

# Generate + apply in one shot
nexus migrate schema.nexus --db=postgres

# Force-drop existing tables first (development only!)
nexus migrate schema.nexus --db=postgres --force

NexusORM calls psql (or mysql) under the hood using the credentials from your .env. Passwords are always redacted in terminal output.


Step 8 — Use the generated API in your C++ project

Create main.cpp:

#include "schema.h"
#include <iostream>

int main() {
    // orm::Database::get() connects lazily using DATABASE_URL / PG_* env vars
    orm::UserRepository users;

    // Insert or update (UPSERT)
    orm::User u;
    u.setId("550e8400-e29b-41d4-a716-446655440000")
     .setUsername("alice")
     .setEmail("alice@example.com")
     .setRole(orm::Role::Admin)
     .setIs_active(true)
     .setCreated_at("2026-01-01 00:00:00");
    users.save(u);

    // Query builder
    auto admins = users.select()
                       .where("role", "'Admin'")
                       .where("is_active", "true")
                       .orderBy("username", "ASC")
                       .limit(50)
                       .get();

    for (const auto& admin : admins)
        std::cout << admin.username << "\n";

    // Find by primary key
    auto found = users.findById("550e8400-e29b-41d4-a716-446655440000");
    std::cout << "Found: " << found.username << "\n";

    // Delete
    users.remove("550e8400-e29b-41d4-a716-446655440000");

    return 0;
}

Compile, linking against libpqxx and libpq:

# g++ directly
g++ main.cpp -lpqxx -lpq -std=c++17 -o my_app

# Run
./my_app

Note

For MySQL targets, link against -lmysqlclient instead of -lpqxx -lpq.


Step 9 — (Advanced) Reverse-engineer an existing database

Already have a live database? Skip writing a schema and let NexusORM generate it for you:

# PostgreSQL → generate schema.nexus + schema.h
nexus pull --db=postgres --url="postgresql://user:pass@localhost/mydb" --out=./

# MySQL
nexus pull --db=mysql --url="mysql://root:secret@localhost:3306/mydb" --out=./

This introspects all tables and writes two files named after your database — e.g. for --db-name=myapp: myapp.nexus (editable DSL) and myapp.h (the C++ ORM header). No manual schema writing required.


🚀 For End-Users: Using NexusORM

1. The Nexus DSL (.nexus)

The .nexus file is the single source of truth for your data model. NexusORM reads it and automatically generates SQL DDL scripts and a fully-typed C++ ORM header — you never write boilerplate again.


DSL Syntax at a Glance

A .nexus file is made of two kinds of top-level declarations, always at file scope:

enum  <EnumName>  { Variant1, Variant2, … }
table <TableName> { <field declarations> }

Each field inside a table block follows this pattern:

<fieldName>: <type>  [modifier …]  // optional comment

Rules:

  • Field names and table names are case-sensitive.
  • A field may have zero, one, or two modifiers (e.g., string unique index is valid; null primary key is not).
  • enum declarations must come before any table that references them.
  • Inline comments start with // and can appear anywhere on a line.
  • Whitespace (spaces, tabs, newlines) is ignored — indentation is cosmetic only.

Field Types

Every field must be assigned exactly one type:

Nexus Type What it means PostgreSQL DDL MySQL DDL C++ type
int Whole number INTEGER INT int
string Short text (≤ 255 chars) VARCHAR(255) VARCHAR(255) std::string
text Unlimited-length text TEXT TEXT std::string
boolean True / false flag BOOLEAN BOOLEAN bool
float 32-bit floating point REAL FLOAT float
double 64-bit floating point DOUBLE PRECISION DOUBLE double
datetime Date + time value TIMESTAMP DATETIME std::string
uuid 128-bit unique identifier UUID CHAR(36) std::string
MyEnum Any enum declared in this file CREATE TYPE … AS ENUM ENUM(…) enum class MyEnum

Tip

Use uuid for primary keys. It avoids auto-increment collisions across distributed systems and maps cleanly to std::string in the generated C++ struct.


Field Modifiers

Modifiers are optional keywords placed after the type on the same line:

Modifier SQL effect C++ effect
primary key PRIMARY KEY constraint Field is used in findById(), save() UPSERT key
unique UNIQUE constraint No special C++ change
index CREATE INDEX on this column No special C++ change
null Column is NULL-able (default is NOT NULL) Field type becomes std::optional<T>

Rules for modifiers:

  • Every table must have exactly one field with primary key. The semantic validator enforces this — it will error if you have zero or more than one.
  • null and primary key cannot both appear on the same field (a primary key must always have a value).
  • unique and index can be combined: string unique index creates both a UNIQUE constraint and a separate index.
  • Modifier order does not matter: string null unique and string unique null behave identically.

Enums

Enums let you define a closed set of allowed values, enforced at the database level.

enum Status { Draft, Published, Archived }
enum Priority { Low, Medium, High, Critical }
  • Declare enums at the top of your file, before any table.
  • Variant names are PascalCase by convention (e.g., Admin, not admin).
  • In PostgreSQL, NexusORM emits CREATE TYPE "Status" AS ENUM ('Draft', 'Published', 'Archived') before the CREATE TABLE that references it.
  • In MySQL, NexusORM inlines the variants directly: ENUM('Draft', 'Published', 'Archived').
  • In C++, each enum becomes an enum class with a companion to_string() helper for safe PostgreSQL binding via pqxx.

Full Annotated Example

// ── Enum declarations (must come first) ───────────────────────────────────────

enum Role     { Admin, Editor, Viewer }
enum Status   { Draft, Published, Archived }

// ── Table definitions ──────────────────────────────────────────────────────────

table User {
    id:         uuid     primary key   // PK — used in findById / save UPSERT
    username:   string   unique        // UNIQUE constraint, VARCHAR(255)
    email:      string   unique        // No two users can share an email
    bio:        text     null          // Long text, nullable → std::optional<std::string>
    role:       Role                   // Enum field, no modifier needed
    score:      double                 // Non-null by default
    weight:     float    null          // Optional float
    is_active:  boolean                // bool, NOT NULL
    created_at: datetime               // TIMESTAMP / DATETIME, NOT NULL
}

table Post {
    id:         uuid     primary key
    title:      string   index         // Indexed for fast search, not unique
    body:       text
    views:      int
    status:     Status                 // Enum — Draft | Published | Archived
    author_id:  uuid     index         // Foreign-key pattern: index the FK column
}

table Tag {
    id:    uuid   primary key
    name:  string unique index         // Both unique constraint AND index
    slug:  string unique
}

What the compiler generates from this schema

Output file Contents
schema_postgres.sql CREATE TYPE for each enum, CREATE TABLE with constraints and indexes
schema_mysql.sql CREATE TABLE with inlined ENUM(…), INDEX, UNIQUE KEY
schema.h C++ structs, enum class types, *Repository classes, query builder

Common Validation Errors

The semantic validator catches these mistakes before any code is generated:

Error Cause Fix
No primary key defined for table 'X' Table has no primary key field Add <field>: uuid primary key
Unknown type 'MyEnum' Enum used in a table before being declared Move the enum block to the top of the file
Duplicate field 'name' in table 'X' Two fields share the same name Rename one
Duplicate table 'X' Two table blocks with the same name Rename one
'null' cannot be combined with 'primary key' Invalid modifier combo Remove null from the PK field

2. Command Line Interface (CLI)

The nexus binary uses a subcommand style interface:

nexus <command> [arguments] [flags]
Command Description
generate <schema.nexus> Parse & compile .nexus → SQL + .h (no DB needed)
migrate <schema.nexus> Same as generate, then apply SQL to a live database
pull Connect to a live DB, reverse-engineer its schema into .nexus, then generate .h
help Print full usage
version Print version string

nexus generate

# PostgreSQL
nexus generate schema.nexus --db=postgres --out=./

# MySQL
nexus generate schema.nexus --db=mysql --out=./output/

# Force drop & recreate existing tables
nexus generate schema.nexus --db=postgres --force

nexus migrate — Apply to a live database

Credentials are resolved in this priority order:

CLI flagsPG_* env vars (Postgres) → DB_* env vars (both) → DATABASE_URL

# Postgres — via .env
nexus migrate schema.nexus --db=postgres

# Postgres — explicit flags
nexus migrate schema.nexus --db=postgres --db-host=localhost --db-user=admin --db-pass=secret --db-name=mydb

# Postgres — connection URL
nexus migrate schema.nexus --db=postgres --url="postgresql://user:pass@localhost/db"

# MySQL — via URL
nexus migrate schema.nexus --db=mysql --url="mysql://root:secret@localhost:3306/mydb"

# Force drop & recreate
nexus migrate schema.nexus --db=postgres --force

nexus pull — Reverse migrate (DB → .nexus → .h)

Connect to an existing database, introspect its schema, and emit a .nexus file plus a ready-to-use C++ header — no DSL file needed.

# Postgres — via URL
nexus pull --db=postgres --url="postgresql://user:pass@localhost/mydb" --out=./

# Postgres — explicit credentials (output files named "mydb.*")
nexus pull --db=postgres --db-host=localhost --db-user=postgres --db-name=mydb

# MySQL — via URL (output files named "mydb.*")
nexus pull --db=mysql --url="mysql://root:secret@localhost:3306/mydb" --out=./

# MySQL — explicit credentials
nexus pull --db=mysql --db-host=localhost --db-user=root --db-name=mydb

# Override the output filename with --schema-name
nexus pull --db=postgres --db-name=mydb --schema-name=custom_name

Output for nexus pull:

  • <db-name>.nexus — editable DSL file named after your database (e.g. myapp.nexus)
  • <db-name>.h — ready-to-use C++ header with repositories and query builders

Note

The file base-name is automatically taken from your database name — from --db-name, or parsed from --url if --db-name is not given. Use --schema-name=<custom> to override it.

Connection & Environment

NexusORM reads your .env file automatically. Choose one of three credential styles:

Style Variables Priority
URL DATABASE_URL Lowest
Generic DB_HOST DB_PORT DB_USER DB_PASS DB_NAME Middle
Postgres-specific PG_HOST PG_PORT PG_USER PG_PASS PG_NAME Highest
# .env — Postgres
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASS=secret
PG_NAME=mydb

# .env — MySQL
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASS=secret
DB_NAME=mydb

Note

Passwords are always redacted (****) in terminal output. The actual credential is only sent to the database client process.

3. Generated C++ API Reference

The generated schema.h header contains everything you need to interface with your database safely. All emitted code is placed inside the orm:: namespace.

Compiling your project

When compiling your own C++ project that includes schema.h, you must link the corresponding database client library:

  • For PostgreSQL (--db=postgres): Add -lpqxx -lpq to your linker flags.
  • For MySQL (--db=mysql): Add -lmysqlclient to your linker flags.

Initialization & Environment

NexusORM automatically generates an orm::Database::get() singleton that connects to your database using the connection string defined in your .env file (DATABASE_URL). Connection singletons are lazily evaluated on the first query.

Fluent Models

All generated structs feature chained setters:

#include "schema.h"

int main() {
    orm::User user;
    user.setId(1)
        .setUsername("imranakki")
        .setEmail("test@test.com")
        .setIsActive(true);
}

The Repository & UPSERTs

Every table generates a repository (e.g. UserRepository). When you call save(), NexusORM executes native UPSERTs meaning it inserts the record, or updates the fields if the Primary Key already exists!

orm::UserRepository repo;
repo.save(user);      // Insterts into the database
user.setIsActive(false);
repo.save(user);      // Smoothly updates the existing record

Eloquent-Style Query Builder

NexusORM gives you an infinitely chainable query builder for fetching data, wrapping PostgreSQL parameterized protections automatically. Initiate the builder via .select().

// 1. Chained queries returning std::vector
auto activeUsers = repo.select()
                       .where("isActive", "true")
                       .orWhere("username", "imranakki")
                       .orderBy("id", "DESC")
                       .limit(10)
                       .get();

// 2. Fetch a single record natively mapped to a struct
auto firstUser = repo.select().where("id", "1").first();

// 3. Raw condition fallbacks
auto rawConds = repo.select().whereRaw("\"id\" > 5 AND \"isActive\" = true").get();

Raw Executions

You can bypass the structured builder completely while retaining automatic native mapping of the struct objects:

auto explicitUsers = repo.query("SELECT * FROM \"User\" WHERE \"id\" > 10 LIMIT 5");

🛠️ For Developers: Compiler Architecture

If you want to contribute, NexusORM is written in pure C++20 standard library (no external AST tools like ANTLR or Bison used).

Important

Before writing any code, please read CONTRIBUTING.md. It documents the project structure, the pipeline in detail, and — critically — why src/pqxx_abi_shim.cpp exists and must not be removed. Removing it will break the build on Ubuntu 22.04 and other distros shipping an older libpqxx.

Pipeline Overview

  1. Lexical Analysis (src/lexer/): Reads the .nexus file character-by-character, discarding whitespace/comments, and constructs a normalized std::vector<Token>.
  2. Parser (src/parser/): A hand-written Recursive Descent LL(1) parser evaluates the tokens into an Abstract Syntax Tree (AST). The AST classes (SchemaNode, TableNode, FieldNode) represent the graph memory.
  3. Semantic Validator (src/semantic/): Traverses the AST before generation to catch business-logic errors (e.g., duplicate tables/fields, missing primary keys, invalid modifier combinations).
  4. Code Generators (src/generator/): Takes the validated AST and synthesizes raw string outputs:
    • CppGenerator: Emits schema.h (Structs, Repositories, QueryBuilders, libpqxx wrappers).
    • PostgresGenerator / MySqlGenerator: Emits dialect-specific .sql scripts.
    • GeneratorContext: Acts as the factory distributing AST pieces to the relevant generator strategy.
  5. Introspection (src/introspect/): For nexus pull — connects to a live DB via libpq (PostgreSQL C API) or libmysqlclient (MySQL C API), reverse-engineers the schema into an AST, then passes it through NexusWriter (emits .nexus) and CppGenerator (emits .h).

Contributing & Internals

See CONTRIBUTING.md for:

  • Full annotated project directory layout
  • In-depth explanation of src/pqxx_abi_shim.cpp (the libpqxx ABI compatibility shim)
  • CMake target reference and library dependency breakdown
  • Step-by-step contribution workflow

Testing

NexusORM is heavily unit-tested. To run the Internal Compiler Test Suites:

cd build
./lexer_tests      # Tokenization validations
./parser_tests     # AST generation validations
./validator_tests  # Semantic rule validations
./ast_tests        # E2E Code Generation validations

🗺️ Roadmap

  1. Relationships Engine: Introduce 1-to-many and many-to-many DSL keywords to emit SQL FOREIGN KEY cascades and generated C++ methods like .with("relation") for eager-loading.
  2. AST-Diff Migrations: Upgrade the SQL generators from emitting complete CREATE / DROP queries to evaluating AST snapshots and emitting minimal, precise ALTER TABLE execution queries.
  3. Driver Expansions: Include code generators for SQLite bindings and asynchronous networking database drivers in C++.

About

No description, website, or topics provided.

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors