Code Monkey home page Code Monkey logo

engine-js's Introduction

Neuledge

Universal language to model, share, and interact with databases.

Build Status Dependency Status License Follow @neuledge Discord

Documentation   •   Discord   •   NPM   •   Issues   •   @neuledge   •   Website

Table of contents


❤️ Sponsored by

If you find Neuledge useful and would like to support its ongoing development and maintenance, please consider sponsoring us. Your sponsorship will help us to continue to improve and evolve this project. Thank you for your support!


👋 Introduction

Neuledge is an powerfull 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.

flowchart LR
  DraftPost -->|publish| PublishedPost
  DraftPost -->|delete| Void
  PublishedPost -->|archive| ArchivedPost
Loading

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.


🚀 Features

🌍  Intuitive schema
        Define your data models and business logic using a simple and intuitive schema language.

📏  Custom types
        Define custom scalar such as Integer(min: 5, max: 10) or Email(at: "my-company.com").

🛡️  Percise modeling
        Create different states for the same entity, each with its own set of fields and mutations.

🔄  Zero migrations
        Avoid data migrations with automatic query rewriting per each state.

🚦  Explicit mutations
        Define explicit mutations for each state, and allow only predefined alterations.

🔍  Type-safe queries
        Validate your queries at compile time, with a simple and powerful query language.

🔌  Database agnostic
        The schema language is identical for relational and non-relational databases.


Quick comparisons

Fetching entries from the database:

Without Neuledge With Neuledge
if (
  user.status === 'ACTIVE' &&
  user.email != null &&
  user.firstName != null
) {
  // handle user login..
  console.info(`Login ${user.firstName}`);
}
// skip null checks thanks to the schema state

if (user.$state === 'ActiveUser') {
  // handle user login..
  console.info(`Login ${user.firstName}`);
}

Validating data mutations:

Without Neuledge With Neuledge
// implmenet data mutations manually

await db.updateOne({
  find: {
    id: 1234,
    status: 'DRAFT',
    title: { $exists: true },
    content: { $exists: true },
  },
  set: {
    status: 'PUBLISHED',
    publishedAt: new Date(),
  },
});
// use the `publish` mutation defined 
// on the database schema

await db
  .alterUnique(DraftPost)
  .unique({ id: 1234 })
  .publish();

Handling legacy code and migrations:

Without Neuledge With Neuledge
let username;

if (user.username != null) {
  username = user.username;
} else if (user.migratedUsername != null) {
  username = user.migratedUsername;
} else {
  throw new Error('Username is missing');
}
// both `username` and `migratedUsername`
// are mapped to the same field by the engine
// so you can access them directly

const username = user.username;

Quering legacy code and migrations:

Without Neuledge With Neuledge
const user = await db.findOne({
  where: [
    {
      username: 'john',
    },
    {
      migratedUsername: 'john',
    },
  ],
});
// the engine will automatically transform
// the query to include both `username` and
// `migratedUsername` in the `where` clause

const user = await db.findUnique(...User).where({
  username: 'john',
});

Schema examples

Unique state for each status:

RegisteredUser ActiveUser
state RegisteredUser {
  id: Integer = 1
  email: Email = 2
  firstName?: String = 3
  lastName?: String = 4
  createdAt: DateTime = 5
}
state ActiveUser from RegisteredUser {
  firstName: String = 1
  lastName: String = 2
  passwordHash: Buffer = 3
  lastLoginAt: DateTime = 4
}

Percise data mutations by state:

Register a user Activate a user
register(
  email: Email,
  firstName?: String,
  lastName?: String,
): RegisteredUser => {
  createdAt: DateTime(),
}
RegisteredUser.activate(
  passwordHash: Buffer
): ActiveUser => {
  firstName: Required(value: this.firstName),
  lastName: Required(value: this.lastName),
  lastLoginAt: DateTime(),
}

Custom data validations:

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
}

Seemless data migrations on the fly:

Current state Original state
state User from LegacyUser {
  -slug
  @unique username: String = 1
}

# runtime database migration
(LegacyUser): User => {
  username: this.slug,
}
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 and give us a star ⭐️. If you are interested in using Neuledge in your project, please join our Discord server and we will be happy to help you.


Installation

Install the Neuledge engine and the MongoDB store:

npm install @neuledge/engine @neuledge/mongodb-store --save

Install a development dependency for the CLI:

npm install @neuledge/states-cli --save-dev

Add generate:states script on your package.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:

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
  -lastName
  -email

  deletedAt: 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

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

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 neuledge.com/docs.

For fully functional code examples, please check the examples folder.


🤝 Join the community

To get involved in the Neuledge community:

  • Give us a star ⭐️ on GitHub.
  • Follow us on Twitter.
  • Join our Discord community to connect with other users and get help.
  • Subscribe to our newsletter to stay up to date on the latest news and updates.
  • If you find any bugs or have any suggestions, please open an issue on GitHub or let us know on our Discord channel.

📜 License

Neuledge is Apache 2.0 licensed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.