Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/neuledge/engine-js
⚡️ Neuledge streamlines database modeling with clear data entity definitions, customizable validation, and advanced business-logic representation.
https://github.com/neuledge/engine-js
business-logic data-access-layer language mongodb mysql orm postgres state-machine state-management validation
Last synced: about 1 month ago
JSON representation
⚡️ Neuledge streamlines database modeling with clear data entity definitions, customizable validation, and advanced business-logic representation.
- Host: GitHub
- URL: https://github.com/neuledge/engine-js
- Owner: neuledge
- License: apache-2.0
- Archived: true
- Created: 2022-09-06T07:57:05.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2023-11-05T06:15:17.000Z (about 1 year ago)
- Last Synced: 2024-10-28T12:07:18.523Z (about 2 months ago)
- Topics: business-logic, data-access-layer, language, mongodb, mysql, orm, postgres, state-machine, state-management, validation
- Language: TypeScript
- Homepage:
- Size: 2.39 MB
- Stars: 237
- Watchers: 6
- Forks: 6
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-nodejs - Neuledge - Universal language for databases that provides state-of-the-art tools for data modeling, business logic representation, and schema validation. ![](https://img.shields.io/github/stars/neuledge/engine-js.svg?style=social&label=Star) (Repository / Database)
- awesome-mongodb - Neuledge - Universal schema-based ORM with multi-state representation for entities (Libraries / JavaScript)
- awesome-opensource-israel - Neuledge - Universal language to model, share, and interact with databases. ![GitHub last commit](https://img.shields.io/github/last-commit/neuledge/engine-js?style=flat-square) ![GitHub top language](https://img.shields.io/github/languages/top/neuledge/engine-js?style=flat-square) ![GitHub stars](https://img.shields.io/github/stars/neuledge/engine-js?style=flat-square) (Projects by main language / typescript)
README
Create data models directly from your business logic.
MongoDB ⇄
MySQL (soon) ⇄
PostgreSQL (soon)
## Main features
🌍 **Intuitive schema**
Define your data models using a simple and intuitive schema language.🔌 **Database agnostic**
Seamlessly switch between various database technologies.🏎️ **High performance**
Run efficiently on serverless or edge environments.🔍 **Type-safe queries**
Validate your queries at compile time, with a simple and powerful query language.🛡️ **State-based modeling**
Define different states for the same entity, each with its own set of fields and mutations, allowing for precise and controlled state transitions.🔄 **Automatic query rewriting**
Avoid data migrations with automatic query rewriting for each state, simplifying the migration process.🚦 **Controlled mutations**
Define explicit mutations for each state, allowing only predefined alterations and maintaining control over data changes during state transitions.📏 **Precise validation**
Create unique data types with custom restrictions like minimum and maximum values, regular expressions, and more.
### ❤️ Sponsored by
If you find Neuledge useful and would like to support its ongoing development and maintenance, please consider [sponsoring us](https://github.com/sponsors/neuledge). Your sponsorship will help us to continue to improve and evolve this project. Thank you for your support!
## Table of contents
- [Introduction](#-introduction)
- [How it works](#-how-it-works)
- [Getting started](#-getting-started)
- [Documentation & examples](#-documentation--examples)
- [Join the community](#-join-the-community)
- [License](#-license)
# 👋 Introduction
**Neuledge Engine** is a powerful language that simplifies data management and enhances data integrity for databases. It enables you to define your data models and business logic in a precise and customizable way. The schema language supports customizable scalar types, providing type-safe data models and ensuring that you always get the data you expect.
With Neuledge, you can create different states for the same entity, each with its own set of fields and mutations. These states are stored and accessed from the same table, with an abstraction layer that defines which fields are needed for each state. For example, you can define a "DraftPost" state with a set of fields and mutations, and then create a "PublishedPost" state that inherits from "DraftPost" and adds more fields and restrictions necessary for published posts.
```mermaid
flowchart LR
DraftPost -->|publish| PublishedPost
DraftPost -->|delete| Void
PublishedPost -->|archive| ArchivedPost
```The schema language is identical for relational and non-relational databases, giving you the flexibility to use it with any database of your choice. It allows you to define precise field types, validate data mutations, and enforce business rules across different states. Whether you are working with a small or complex data model, Neuledge makes it easy to manage and maintain your data.
# 🤔 How it works
Below are a few examples that demonstrate how Neuledge can be utilized in contrast to a conventional approach.
## Quick comparisons
### Fetching entries from the database:
Without Neuledge
With Neuledge
```ts
if (
user.status === 'ACTIVE' &&
user.email != null &&
user.firstName != null
) {
// handle user login..
console.info(`Login ${user.firstName}`);
}
```
```ts
// skip null checks thanks to the schema stateif (user.$state === 'ActiveUser') {
// handle user login..
console.info(`Login ${user.firstName}`);
}
```### Validating data mutations:
Without Neuledge
With Neuledge```ts
// implmenet data mutations manuallyawait db.updateOne({
find: {
id: 1234,
status: 'DRAFT',
title: { $exists: true },
content: { $exists: true },
},
set: {
status: 'PUBLISHED',
publishedAt: new Date(),
},
});
``````ts
// use the `publish` mutation defined
// on the database schemaawait db
.alterUnique(DraftPost)
.unique({ id: 1234 })
.publish();
```### Handling legacy code and migrations:
Without Neuledge
With Neuledge```ts
let username;if (user.username != null) {
username = user.username;
} else if (user.migratedUsername != null) {
username = user.migratedUsername;
} else {
throw new Error('Username is missing');
}
``````ts
// both `username` and `migratedUsername`
// are mapped to the same field by the engine
// so you can access them directlyconst username = user.username;
```### Querying legacy code and migrations:
Without Neuledge
With Neuledge```ts
const user = await db.findOne({
where: [
{
username: 'john',
},
{
migratedUsername: 'john',
},
],
});
``````ts
// the engine will automatically transform
// the query to include both `username` and
// `migratedUsername` in the `where` clauseconst user = await db.findUnique(...User).where({
username: 'john',
});
```
## Schema examples
### Unique state for each status:
RegisteredUser
ActiveUser```states
state RegisteredUser {
id: Integer = 1
email: Email = 2
firstName?: String = 3
lastName?: String = 4
createdAt: DateTime = 5
}
``````states
state ActiveUser from RegisteredUser {
firstName: String = 1
lastName: String = 2
passwordHash: Buffer = 3
lastLoginAt: DateTime = 4
}
```### Precise data mutations by state:
Register a user
Activate a user```states
register(
email: Email,
firstName?: String,
lastName?: String,
): RegisteredUser => {
createdAt: DateTime(),
}
``````states
RegisteredUser.activate(
passwordHash: Buffer
): ActiveUser => {
firstName: Required(value: this.firstName),
lastName: Required(value: this.lastName),
lastLoginAt: DateTime(),
}
```### Custom data validations:
```states
state Person {
name: String(normalize: true, trim: true, min: 3, max: 50) = 1
email: Email(lowercase: true, trim: true, at: "gmail.com") = 2
profilePicture?: URL(secure: true) = 3
age: Integer(min: 18, max: 100) = 4
createdAt: DateTime = 5
}
```### Seamless data migrations on the fly:
Current state
Original state```states
state User from LegacyUser {
-slug
@unique username: String = 1
}# runtime database migration
(LegacyUser): User => {
username: this.slug,
}
``````states
state LegacyUser {
id: Integer = 1
email: Email = 2
slug: String = 3
createdAt: DateTime = 4
}
```_(Runtime migrations are partially supported, will be fully supported in the future releases)_
# 🏁 Getting started
## ⚠️ Beta release
Neuledge is still in beta. Help us improve it by [join our community](#-join-the-community) and give us a star ⭐️. If you are interested in using Neuledge in your project, please join our [Discord server](https://discord.gg/49JMwxKvhF) and we will be happy to help you.
## Installation
Install the Neuledge engine and the MongoDB store:
```bash
npm install @neuledge/engine @neuledge/mongodb-store --save
```Install a development dependency for the CLI:
```bash
npm install @neuledge/states-cli --save-dev
```Add `generate:states` script on your `package.json`:
```json
{
"scripts": {
"generate:states": "states --output \"src/states.codegen.ts\" \"states/*.states\""
}
}
```On the next step, run `npm run generate:states` to generate the states code from your `*.states` files.
This will generate a `src/states.codegen.ts` file with all your business logic code.
You should add this file to your `.gitignore` file, as it will be generated automatically.
## Define your schema files
Create a `states` folder and your first `users.states` file:
```states
state CreatedUser {
@id(auto: 'increment') id: Integer = 1
firstName?: String = 2
lastName?: String = 3
@unique email: Email = 4
@index createdAt: DateTime = 6
}state ActiveUser from CreatedUser {
firstName: String = 1
lastName: String = 2
passwordHash?: Buffer = 3
updatedAt: DateTime = 4
}state SuspendedUser from ActiveUser {
suspendedAt: DateTime = 1
}state DeletedUser from CreatedUser {
-firstName
-lastNamedeletedAt: DateTime = 1
}create(
firstName: String,
lastName: String,
email: Email,
): CreatedUser => {
createdAt: DateTime(),
}CreatedUser.activate(
firstName: String,
lastName: String,
passwordHash?: Buffer,
): ActiveUser => {
updatedAt: DateTime(),
}create(
firstName: String,
lastName: String,
email: Email,
passwordHash?: Buffer,
): ActiveUser => {
createdAt: DateTime(),
updatedAt: DateTime(),
}ActiveUser.update(
firstName: String,
lastName: String,
email: Email,
passwordHash?: Buffer,
): ActiveUser => {
updatedAt: DateTime(),
}ActiveUser.suspend(): SuspendedUser => {
suspendedAt: DateTime(),
}SuspendedUser.activate(): ActiveUser => {
updatedAt: DateTime(),
}either User = ActiveUser | SuspendedUser
User.delete(): DeletedUser => {
deletedAt: DateTime(),
}CreatedUser.delete(): Void
```
## Initialize your database
```ts
import { NeuledgeEngine } from '@neuledge/engine';
import { MongoDBStore } from '@neuledge/mongodb-store';// import your generated code for the engine to use before initializing the engine
import `./states.codegen`;// use the MongoDBStore to connect to your database
const store = new MongoDBStore({
url: 'mongodb://localhost:27017',
name: 'example',
});// initialize the engine with the store and syncing the database schema
const engine = new NeuledgeEngine({
store,
});
```
## Query the database
```ts
import { CreatedUser, User } from './states.codegen';// create a new user
const createdUser = await engine
.initOne(CreatedUser)
.create({
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
})
.select();// activate the user
const activeUser = await engine
.alterUniqueOrThrow(CreatedUser)
.activate()
.unique({ id: createdUser.id })
.select();// update the user information
const updatedUser = await engine
.alterUniqueOrThrow(ActiveUser)
.update({
firstName: 'Jane',
lastName: 'Doe',
email: '[email protected]',
passwordHash: Buffer.from('password'),
})
.unique({ id: activeUser.id })
.select();// suspend the user
const suspendedUser = await engine
.alterUniqueOrThrow(ActiveUser)
.suspend()
.unique({ id: updatedUser.id })
.select();// list active and suspended users
const users = await engine.findMany(...User).limit(10);
```
# 📚 Documentation & examples
For more information, please visit [docs](https://github.com/neuledge/engine-js/tree/main/docs).
For fully functional code examples, please check the [examples](https://github.com/neuledge/engine-js/tree/main/examples) folder.
# 🤝 Join the community
To get involved in the Neuledge community:
- Give us a star ⭐️ on GitHub.
- Follow us on [Twitter](https://twitter.com/neuledge).
- Join our [Discord community](https://discord.gg/49JMwxKvhF) to connect with other users and get help.
- If you find any bugs or have any suggestions, please [open an issue](https://github.com/neuledge/engine-js/issues/new/choose) on GitHub or let us know on our [Discord channel](https://discord.gg/49JMwxKvhF).
# 📜 License
Neuledge is [Apache 2.0 licensed](https://github.com/neuledge/engine-js/blob/main/LICENSE).