Formulaic Docs

Formulaic’s Data Types (FP)

Available on NPM as @formulaic/fp

Most Formulaic packages use a functional approach to handling data, taking inspiration from fp-ts and other languages which support complex return types.

Demo of @formulaic/fp features
import {
  AccessForbidden,
  Data,
  EntityNotFound,
  NotFound,
} from "@formulaic/fp";
import { ApiProperty } from "@nestjs/swagger";
import { Expose, instanceToPlain } from "class-transformer";

class SecureNote {
  @ApiProperty()
  @Expose() (1)
  kind: "SecureNote";

  /**
   * A unique identifier.
   */
  @ApiProperty()
  @Expose()
  id: string;
  /**
   * A list of usernames that can read this file.
   */
  canRead: string[];
  /**
   * The contents of the note.
   */
  @ApiProperty()
  @Expose()
  message: string;
}

var notes: SecureNote[] = [ /* ... */ ];

async function getNote(
  username: string,
  noteId: string,
): Promise<Data<SecureNote> | NotFound<SecureNote, "Note">> {
  const note = notes.find(note => note.id = noteId);
  if(!note) {
    return new EntityNotFound("Note", { id });
  }
  if(!note.canRead.includes(username)) {
    return new AccessForbidden("Note");
  }
  return new Data(note);
}

async function main() {
  const note = await getNote("test", "myNote123");
  const modified = note.mapData(data => data); (2)
  const formatted = instanceToPlain(modified, {
    groups: [
      "structure",
      // "exposeForbidden", (3)
    ],
  );
  // => SecureNote { kind: "SecureNote", id, message } or
  // => NotFound { kind: "NotFound", status: 404, entityName: "Note" }
}
1 @Expose will be used by class-transformer to filter output
2 @formulaic/fp includes utilities like .mapData which can alter successful values - this is similar to Promise.then.
3 @formulaic/fp built-in types come configured for class-transformer - unless you provide either "exposeForbidden" or "debug", AccessForbidden and EntityNotFound will appear to the user as the same object for security.