

GitHub - remult/remult: A CRUD framework for full stack TypeScript
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.

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
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;
}
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);
}
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"}
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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK