title: Model API DSL Generator description: Describe your backend once — models, enums, endpoints — and generate a working API from it. tags: [Python, Compiler, DSL, ANTLR4, Code-Generation, Django]
🧬 Model API DSL Generator
Describe your backend once. Generate it everywhere.
Getting Started • DSL Reference • Examples • Architecture • Contributing
📖 Table of Contents
- About The Project
- Features
- Getting Started
- DSL Reference
- Examples
- Architecture
- Contributing
- Authors
- License
- Acknowledgments
✨ About The Project
Building a backend usually means writing the same boilerplate over and over — models, serializers, validators, routes, and hand-rolled queries — for every single project.
Model API DSL Generator skips that step. You write a small, human-readable specification describing:
- 🗂️ Models — your data schema, with field types, constraints, and relationships
- 🎭 Enums — closed sets of values, reusable across models
- 🌐 Endpoints — routes and HTTP methods, with the query logic behind them expressed directly as relational algebra
...and the compiler — an ANTLR4 grammar, a parse-tree listener, an AST, and a code generator — turns that spec into real, runnable backend code.
Currently targets Django. The compiler pipeline (grammar → AST → generator) is designed so new target frameworks can be plugged in later.
🚀 Features
- 📦 Declarative models with types, primary keys, uniqueness, nullability, and validation rules
- 🔗 Foreign keys to express relationships between models
- 🎭 First-class enums, usable as field types
- 🌐 REST endpoints (
GET,POST,PUT,DELETE) with path parameters - 🧮 A built-in relational algebra query language for endpoint responses —
Select,Project,Join,Union,Intersection,Difference,Cartesian,Orderby,Limit,Len - 🧠 Arithmetic expressions (
+ - * /, parentheses) inside query conditions, with URL path parameters usable as variables - 📝 Raw JSON request bodies for endpoints that need custom input shapes
- 🏗️ A generated, inspectable AST (with a visualization helper) sitting between your spec and the generated code
🛠️ Getting Started
Dependencies
- Python 3.9+
antlr4-python3-runtime(must match the ANTLR version the parser ingen/was generated with)pipx(recommended for running this as a CLI tool)
Installing
With pipx (recommended):
pipx install model-api-dsl-generator
With pip, inside a virtualenv:
pip install model-api-dsl-generator
From source, for development:
git clone https://github.com/MatinHAB05/Model-API-DSL-Generator.git
cd Model-API-DSL-Generator
pipx install --editable .
📌 See the pipx packaging guide below if you're setting this up for the first time — it walks through the exact steps to make the project installable.
Once installed, the compiler runs as a normal command — no more python -m ...:
modelapi path/to/spec.txt
First Program
A minimal spec: one model, one endpoint, no foreign keys, no relational algebra.
model User {
username : String @pk @non-nullable @unique;
age : Int @nullable @valid[min=0,max=120];
}
endpoint getUsers : GET "/users" {
response : User;
}
Run it:
modelapi user_api.txt
Command Line Options
| Flag | Description | Default |
|---|---|---|
-i, --input |
Input DSL specification file path (Required) | None |
-o, --output |
Output directory name | generated_app |
--target |
Target framework for code generation from AST tree | Django |
--baseinput |
Base directory for input file | . |
--baseoutput |
Base directory for output file | . |
--generate |
Enable Code Generator | True |
--astimg |
Show AST visualization image after parsing | False |
--version |
Show program's version number and exit | None |
-h, --help |
Show help message and exit | None |
📚 DSL Reference
A spec file is just a sequence of model, enum, and endpoint declarations, in any order.
Comments
// a single-line comment
"""
a multi-line comment,
opening and closing on separate lines
"""
""" a multi-line comment fully on one line """
Enums
enum Role {
"ADMIN",
"USER",
"GUEST",
"MANAGER"
}
Enum values are just literals (usually strings). Once declared, an enum can be used as a field type in any model.
Models
model Person {
username : String @pk @non-nullable @unique @valid[wildpattern="...[a-z]"];
age : Int @nullable @valid[min=8,max=14];
role : Role @non-nullable @valid[exclude={"ADMIN"}];
bth : Date @valid[min="2020-01-01", max="2024-06-11"];
}
Each field is name : type followed by zero or more @annotations.
Field Types
| Type | Meaning |
|---|---|
String |
Text |
Int |
Integer |
Double |
Floating point number |
Date |
Calendar date |
Time |
Time of day |
DateTime |
Date + time |
<EnumName> |
Any enum declared elsewhere in the spec |
Field Annotations
| Annotation | Meaning |
|---|---|
@pk |
Marks the field as (part of) the primary key |
@unique |
Enforces uniqueness |
@nullable / @non-nullable |
Whether the field accepts null |
@foreign-key(Model.field) |
References another model's field |
@valid[...] |
Attaches one or more validation rules |
Validation Rules
Used inside @valid[...], comma-separated:
| Rule | Applies to | Example |
|---|---|---|
min=, max= |
numeric, date, or time bounds | @valid[min=8,max=14] |
wildpattern="..." |
string pattern matching | @valid[wildpattern="...[a-z]"] |
include={...} |
allow-list of values | @valid[include={"USER","MANAGER"}] |
exclude={...} |
deny-list of values | @valid[exclude={"ADMIN"}] |
Endpoints
endpoint <name> : <GET|POST|PUT|DELETE> "<path>" {
response : <ModelName> | relational { ... };
input : "<raw json>"; // optional
}
- Path parameters written as
{x}in the URL (e.g."/users/{x}/{y}") become variables you can reference inside the endpoint body — including inside relational-algebra conditions and arithmetic. responseandinputcan appear in either order;inputis optional.inputholds a raw JSON string that is not grammar-checked — malformed JSON fails at runtime, not at compile time.
Relational Algebra
Instead of hand-writing queries, an endpoint's response can be a relational { ... } block: a small sequence of named steps followed by a final -> ...; statement saying what to return.
relational {
step_1 = <expression>;
step_2 = <expression>;
-> <final expression>;
}
Built-in functions:
| Function | Signature | Purpose |
|---|---|---|
Select |
Select<field OP value, ...>(expr) |
Filter rows |
Project |
Project<field, ...>(expr) |
Pick columns |
Join_inner / Join_outter / Join_left / Join_right |
Join_x<field1,field2>(exprA, exprB) |
Join two relations |
Union / Intersection / Difference / Cartesian |
Fn(exprA, exprB) |
Set operations |
Orderby |
Orderby(expr, True|False) |
Sort ascending/descending |
Limit |
Limit<start,length,step>(expr) |
Slice/paginate a relation |
Len |
Len(expr) |
Row count |
Comparison operators for Select conditions: eq, lst (less than), grt (greater than), lsteq, grteq, and their negations not-eq, not-lst, not-grt, not-lsteq, not-grteq.
Arithmetic (+ - * /, with parentheses and standard precedence) can be used freely inside conditions, and combined with path parameters:
Select<age grt 18*x-(y/z)>(User)
🧾 Examples
1. Model & Enum
enum Role {
"ADMIN",
"USER",
"GUEST",
"MANAGER"
}
model Person {
username : String @pk @non-nullable @unique @valid[wildpattern="...[a-z]"];
age : Int @nullable @valid[min=8,max=14];
role : Role @non-nullable @valid[exclude={"ADMIN"}];
second_role : Role @non-nullable @valid[include={"MANAGER","USER"}];
bth : Date @valid[min="2020-01-01", max="2024-06-11"];
}
2. Model Relations (foreign keys)
model Attendance {
username : String @pk @foreign-key(Person.username);
entryTime : Time @valid[min="00:00", max="12:00"];
}
model Phone {
id : String @pk @foreign-key(Person.username);
phone : String @pk @unique @valid[wildpattern="..."];
}
Each Attendance and Phone row points back to a Person through its username — a one-to-many relationship expressed with a single annotation.
3. Simple Endpoints
endpoint listUsers : GET "/users" {
response : User;
}
endpoint sign_in : POST "/users/sign-in" {
input : "
{
name : 'mamad',
age : 18,
sex : 'Male'
}
";
response : User;
}
4. Query Endpoints (relational algebra)
endpoint first_User : GET "/users/{x}/{y}" {
response : relational {
r_1 = Select<name eq y, age lst x>(User);
r_2 = Project<name,lastname>(r_1);
r_3 = Select<>(Person);
r_4 = Join_inner<name,username>(r_2,r_3);
r_5 = Project<name,lastname,bth>(r_4);
len_temp = Len(r_5);
r_6 = Limit<1,len_temp,3>(r_5);
-> r_6;
};
}
Functions nest and combine freely:
endpoint sort_user : GET "/users/first/{x}-{y}-{z}" {
response : relational {
r1 = User;
r2 = Animals;
r10 = Union(r1,r2);
r20 = Cartesian(r1,r2);
r30 = Intersection(r1,r2);
r40 = Difference(r1,r2);
-> Orderby(Union(Cartesian(r1,r2),Difference(r10,r20)), False);
};
}
More annotated examples live in test_grammer_files/.
🏗️ Architecture
backendgrammer.g4
│ (ANTLR4)
▼
gen/ — generated Lexer, Parser, Listener, Visitor
│
▼
CustomListner_ast_tree.py — walks the parse tree
│
▼
ast_tree.py / ast_tree_node_info.py — the AST
│
▼
django_code_generator.py — emits framework code
│
▼
Generated Django project
helper_functions/ holds supporting tooling used along the way — debug.py and visualzation_ast.py for inspecting the AST while developing, and handling_build_ast_nodes_in_Listner.py for the listener's node-building logic.
🤝 Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contribution you make is greatly appreciated.
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
You can also open an issue with the enhancement tag. Don't forget to star the project ⭐
✍️ Authors
Matin Hasanali Baki GitHub · Email · Telegram
Mani Zamani GitHub · Email · Telegram
📄 License
This project is licensed under the MIT License — see LICENSE for details.
🙏 Acknowledgments
- ANTLR4 Official Documentation — Reference manual and documentation for the ANTLR4 parser generator
- ANTLR4 GitHub Repository — Official source repository and Python3 runtime library powering
backendgrammer.g4 - Domain-Specific Language (DSL) — Wikipedia — Conceptual overview of domain-specific languages and declarative design
- Compiler — Wikipedia — Theoretical background on parsing, AST generation, and code emission
- Graphviz Graph Visualization Software — Rendering engine used in
- DSL Specification Examples — Internal suite of test files demonstrating models, enums, relational algebra, and endpoints
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file model_api_dsl_generator-1.0.0.tar.gz.
File metadata
- Download URL: model_api_dsl_generator-1.0.0.tar.gz
- Upload date:
- Size: 56.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f36c72c2a3bca9231ebb39c0f795892c741ba51e02aa2257aefe2155e2767db
|
|
| MD5 |
3bb340bdebbe1b1dc6a0f7c0ca55287c
|
|
| BLAKE2b-256 |
90882ee6625151248d6fa01bb42d4eaca0841ee407629e412360996b327bf49a
|
File details
Details for the file model_api_dsl_generator-1.0.0-py3-none-any.whl.
File metadata
- Download URL: model_api_dsl_generator-1.0.0-py3-none-any.whl
- Upload date:
- Size: 59.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b7d28f327e37e438388b6a035d8d549614f66e827669a1274b1d9b457c00a76
|
|
| MD5 |
4052ede57e6fe9ffd7d94a8813ec2a0e
|
|
| BLAKE2b-256 |
e28dbbf8e75df8afbcd7473e269a72bbb96fad45e0a28a36f536e665e62da55a
|