7

GitHub - remult/remult: A CRUD framework for full stack TypeScript

 2 years ago
source link: https://github.com/remult/remult
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Remult

What is Remult?

Remult is a full-stack CRUD framework that uses your TypeScript model types to provide:

  • Secure REST API (highly configurable)
  • Type-safe frontend API client
  • Type-safe backend query builder

Remult heart Code Sharing

With model types shared between frontend and backend code, Remult can enforce data validation and constraints, defined once, both in the front-end and within back-end API routes.

Getting started

The best way to learn Remult is by following a tutorial of a simple Todo web app with a Node.js Express backend.

Installation

npm i remult

Usage

Setup API backend using a Node.js Express middleware

import express from 'express';
import { remultExpress } from 'remult/remult-express';

const port = 3001;
const app = express();

app.use(remultExpress());

app.listen(port, () => {
  console.log(`Example API listening at http://localhost:${port}`);
});

Define model classes

import { Entity, Fields } from 'remult';

@Entity('products', {
    allowApiCrud: true
})
export class Product {
  @Fields.string()
  name = '';

  @Fields.number()
  unitPrice = 0;
}

rocket API Ready

> curl http://localhost:3001/api/products

[{"name":"Tofu","unitPrice":5}]

Find and manipulate data in type-safe frontend code

async function increasePriceOfTofu(priceIncrease: number) {
  const productsRepo = remult.repo(Product);

  const product = await productsRepo.findFirst({ name: 'Tofu' });
  product.unitPrice += priceIncrease;
  productsRepo.save(product);
}

...exactly the same way as in backend code

@BackendMethod({ allowed: Allow.authenticated })
static async increasePriceOfTofu(priceIncrease: number, remult?: Remult) {
  const productsRepo = remult!.repo(Product);

  const product = await productsRepo.findFirst({ name: 'Tofu' });
  product.unitPrice += priceIncrease;
  productsRepo.save(product);
}

ballot_box_with_check Data validation and constraints - defined once

import { Entity, Fields } from 'remult';

@Entity('products', {
    allowApiCrud: true
})
export class Product {
    @Fields.string<Product>({
        validate: product => {
            if (product.name.trim().length == 0)
                throw 'required';
        }
    })
    name = '';

    @Fields.number({
        validate: (_, field) => {
            if (field.value < 0)
                throw "must not be less than 0";
        }
    })
    unitPrice = 0;
}

Enforced in frontend:

const product = productsRepo.create();

try {
  await productsRepo.save(product);
}
catch (e: any) {
  console.error(e.message); // Browser console will display - "Name: required"
}

Enforced in backend:

> curl http://localhost:3001/api/products -H "Content-Type: application/json" -d "{""unitPrice"":-1}"

{"modelState":{"unitPrice":"must not be less than 0","name":"required"},"message":"Name: required"}

lock Secure the API with fine-grained authorization

@Entity<Article>('Articles', {
    allowApiRead: true,
    allowApiInsert: remult => remult.authenticated(),
    allowApiUpdate: (remult, article) => article.author.id == remult.user.id
})
export class Article {
    @Fields.string({ allowApiUpdate: false })
    slug = '';
    
    @Field(() => Profile, { allowApiUpdate: false })
    author!: Profile;

    @Fields.string()
    content = '';
}

Example App

CRM demo with a React + MUI front-end and Postgres database.

Contributing

Contributions are welcome. See CONTRIBUTING.

License

Remult is MIT Licensed.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK