typeorm-sm v0.1.4
TypeORM SM
An elegant and efficient solution for integrating state machines with your TypeORM entities. This package provides an easy and robust approach to managing complex states in your applications, ensuring consistent state transitions and well-defined business rules. Perfect for projects that require advanced state control in a TypeORM environment.
Installation
Install using NPM:
npm install typeorm typeorm-sm --save
Install using Yarn:
yarn add typeorm typeorm-sm
Install using Pnpm:
pnpm add typeorm typeorm-sm
Usage
In your DataSource settings, add StateMachineSubscriber
to subscribers:
import { DataSource } from 'typeorm'
import { StateMachineSubscriber } from 'typeorm-sm'
export default new DataSource({
subscribers: [StateMachineSubscriber],
// ...
})
Now to have the state machine in your entity, simply extend your class with StateMachine
:
The
StateMachine
abstract class by default is ActiveRecord
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { StateMachine } from 'typeorm-sm'
enum UserState {
VERIFYING_EMAIL = 'VERIFYING_EMAIL',
VERIFIED_EMAIL = 'VERIFIED_EMAIL',
RETRY = 'RETRY',
}
@Entity()
export class User extends StateMachine<UserState> {
@PrimaryGeneratedColumn()
id: number
@Column({ unique: true })
email: string
static readonly initialState = UserState.PENDING
static readonly transitions = Object.freeze<StateTransitions<UserState>>({
[UserState.PENDING]: { from: [], to: UserState.VERIFYING_EMAIL }, // Initial state
[UserState.VERIFYING_EMAIL]: { from: [UserState.VERIFIED_EMAIL, UserState.RETRY] },
[UserState.VERIFIED_EMAIL]: { from: [UserState.VERIFYING_EMAIL] }, // Final state
[UserState.RETRY]: { from: [UserState.VERIFYING_EMAIL] },
})
}
Note that in the example above we did not define any columns to store the state. This is not necessary because the StateMachine model already internally adds this column for you.
By default the state column is indexed, not null of type varchar with a length of 255, but you can change these settings by rewriting this column, example:
@Entity()
export class User extends StateMachine<UserState> {
// ...
@Index()
@Column({ type: 'integer' })
state: UserState
// ...
}
Validation
If you use the class-validator library to validate the value of some columns, you may only want to validate if the column has a value or if the record is in a certain state. With that in mind, we have two decorators to help us:
ValidateIfStateIs
// ... import { ValidateIfStateIs} from 'typeorm-sm' @Entity() export class User extends StateMachine<UserState> { // ... @ValidateIfStateIs([UserState.VERIFIED]) name?: string // ... }
The column will only be validated if it has a value or if the current state of the record is the same as that entered
ValidateIfStateIsNot
// ... import { ValidateIfStateIsNot } from 'typeorm-sm' @Entity() export class User extends StateMachine<UserState> { // ... @ValidateIfStateIsNot([UserState.PENDING]) name?: string // ... }
The column will only be validated if it has a value or if the current state of the record is different from that informed