This project takes a Mermaid schema with enhancements and builds a ReST server for each entity. Validation logic is autogenerated.
The data is stored in a MongoDB database.
- Clone the repository
- Install dependencies:
pip install -r requirements.txt
- Add the bin directory to your PATH for easy access:
export PATH=$PATH:/path/to/schema2rest/bin
- Copy the S2R Makefile to a new project directory
- Edit the Makefile and set S2R_DIR to the cloned repo location
- Create a .mmd file with your schema
- Execute
make firsttime
on first run - Execute
make all
on changes to the schema
To convert an MMD schema to YAML:
./bin/convert_schema.py schema.mmd output_dir
Or from anywhere if you've added the bin directory to your PATH:
convert_schema.py schema.mmd output_dir
To generate a REST API from a schema:
./bin/schema2rest.py schema.yaml output_dir
Or from anywhere if you've added the bin directory to your PATH:
schema2rest.py schema.yaml output_dir
- Assumes MongoDB is installed
- Uses config.json to define the MongoDB config and server URL
- Allows a "BaseEntity" that all entities include (e.g. - id, updatedAt)
- Supports auto generation of fields (e.g. - id)
- Supports extensive validation logic
- Supports unique field constraints
- Automatically updates unique indexes for constraints (deletes unused and creates new)
- Support for UI metadata via the @ui decorator
- Convert MMD schema to YAML with minimal bloat
- Generate a FastAPI-based REST API from a schema
- Generate database connection code
- Generate model definitions
- Generate API routes
- Support for validation rules
- Support for UI metadata
The @ui
decorator allows you to customize how fields are displayed in the user interface. Only explicitly defined UI attributes will be included in the YAML, while sensible defaults are generated in the model phase.
Attribute | Description | Example | Default |
---|---|---|---|
displayName |
Human-readable name for the field | @ui firstName { displayName: "First Name" } |
Converted from camelCase to Title Case |
display |
When to display the field | @ui password { display: "form" } |
"always" (except passwords are "form") |
widget |
UI control type | @ui email { widget: "email" } |
Inferred from field type and validation |
placeholder |
Placeholder text | @ui email { placeholder: "[email protected]" } |
None |
helpText |
Helper text shown below the field | @ui password { helpText: "Min 8 characters" } |
None |
readOnly |
Whether field is read-only | @ui createdAt { readOnly: true } |
false |
displayAfterField |
Field to display after (for ordering) | @ui lastName { displayAfterField: "firstName" } |
Previous field name |
The display
attribute can have the following values:
always
- Display in all views (default)detail
- Display only in detail views, not in tables/listsform
- Display only in edit/create forms, not in read-only viewshidden
- Don't display in UI at all
Note: Setting display: "hidden"
is the preferred way to hide fields rather than using the deprecated hidden
attribute.
Common widget types include:
text
- Standard text input (default for strings)textarea
- Multi-line text input (default for strings with maxLength > 100)password
- Password input with masked textemail
- Email input with validationurl
- URL input with validationnumber
- Numeric input (default for Number/Integer)checkbox
- Boolean checkbox (default for Boolean)select
- Dropdown select (default for fields with enum)multiselect
- Multi-select control (default for Array types)date
- Date picker (default for ISODate)jsoneditor
- JSON editor (default for JSON type)reference
- Reference selector (default for ObjectId references)
erDiagram
BaseEntity {
ObjectId _id
ISODate createdAt
ISODate updatedAt
}
%% @validation BaseEntity
%% _id { type ObjectId, required true, autoGenerate true }
%% createdAt { type ISODate, required true, autoGenerate true }
%% updatedAt { type ISODate, required true, autoUpdate true }
Account {
ISODate expiredAt
}
%% @inherits BaseEntity
%% @validation Account
%% expiredAt: { type: ISODate, required: false }
User {
ObjectId accountId
String username
String email
String password
String firstName
String lastName
String gender
Boolean isAccountOwner
}
%% @inherits BaseEntity
%% @validation User
%% @unique username ### Note specified combined field indexes by "username + email"
%% accountId: { type: ObjectId, required: true }
%% username: { type: String, required: true, minLength: 3, maxLength: 50 }
%% email { type String, required true, minLength 8, maxLength 50, pattern ^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ }
%% @ui email { widget: "email", helpText: "Please enter a valid email address" }
%% password: { type: String, required: true, minLength: 8 }
%% @ui password { widget: "password", display: "form" }
%% firstName: { type: String, required: true, maxLength: 100 }
%% lastName: { type: String, required: true, maxLength: 100 }
%% gender { type String, required false, enum [male, female, other] }
%% isAccountOwner: { type: Boolean, required: true }
%% name: { type: String, required: true, maxLength: 100 }
%% preferences: { type: JSON, required: false }
%% radiusMiles: { type: Integer, required: false, min: 0 }
%% tag: { type: String, required: true, maxLength: 50 }
%% affinity: { type: Integer, required: true, min: -100, max: 100 }
%% url: { type: String, required: true, pattern: ^https?://[^\s]+$ }
%% title: { type: String, required: true, maxLength: 200 }
%% location: { type: String, required: false, maxLength: 200 }
%% cost: { type: Number, required: false, min: 0 }
%% recurrence: { type: String, required: false, enum: [daily, weekly, monthly, yearly] }