Skip to content

Quatrain/Core

Repository files navigation

Quatrain Core - A Modular BaaS Framework

Quatrain Core is a modular TypeScript framework designed to accelerate business application development with Backend as a Service (BaaS) solutions like Firebase and Supabase. It provides a clean separation of concerns between logic, data, and storage, using an adapter pattern to provide consistent interfaces across different BaaS providers.

🎯 Core Principles

  • BaaS-First Architecture: Built to leverage modern BaaS platforms like Firebase and Supabase for managed databases, authentication, storage, and serverless functions.
  • Clean Abstraction: Write your business logic once. The adapter pattern allows you to switch between different backend services (e.g., Firestore, PostgreSQL, Firebase Auth, Supabase Auth) with minimal code changes.
  • Deployment Flexibility: While optimized for SaaS, Quatrain fully supports self-hosted solutions. For example, a Supabase-powered application can be deployed via Docker, Kubernetes, or on-premise for data sovereignty, using the exact same codebase.
  • Modular by Design: The framework is split into scoped packages. Use only what you need, from core object modeling to authentication, storage, and message queues.
  • Standalone Core: The @quatrain/core package works entirely in-memory for defining models, validation, and business logic, without requiring any database connection.

🏗️ Architecture & Packages

The framework is organized as a monorepo with a foundation package and a suite of adapters for popular services. For a detailed explanation of the architecture and each package, please see PACKAGES_OVERVIEW.md.

@quatrain/core (Foundation - works standalone)
├── 🔥 Firebase Ecosystem
│   ├── @quatrain/backend-firestore (Firestore NoSQL)
│   ├── @quatrain/auth-firebase (Firebase Auth)
│   ├── @quatrain/storage-firebase (Firebase Storage)
│   ├── @quatrain/cloudwrapper-firebase (Firebase Functions)
│   └── @quatrain/messaging-firebase (FCM)
├── 🟢 Supabase Ecosystem
│   ├── @quatrain/backend-postgres (PostgreSQL - works with Supabase)
│   ├── @quatrain/auth-supabase (Supabase Auth)
│   ├── @quatrain/storage-supabase (Supabase Storage)
│   └── @quatrain/cloudwrapper-supabase (Supabase Edge Functions)

How to use

Create a model

import { BaseObject, Property } from '@quatrain/core'

export type Cat = {
   name: string
   color: `#${string}`
}

export class CatCore extends BaseObject {
   static COLLECTION = 'cats'

   static PROPERTIES = [
      {
         name: 'name',
         type: Property.STRING,
         minLength: 1,
         maxLength: 32,
      },
      {
         name: 'color',
         type: Property.STRING,
         minLength: 4,
         maxLength: 7,
      },
   ]
}

Instantiate a model

const catData: Cat = {
   name: 'Garfield',
   color: '#ffa502',
}

const garfield = Cat.fromObject(catData)

console.log(garfield._.name)
// > "Garfield"
console.log(garfield._.color)
// > "#ffa502"

Interact with backend

import { Backend } from '@quatrain/backend'
import { SqliteAdapter } from '@quatrain/backend-sqlite'

// Set up a default backend
Backend.addBackend(new SqliteAdapter(), 'sqlite', true)

// Let's save Garfield in our database
const savedCat = await garfield.save()

// Now, let's retrieve Garfield in the database
const persistedGarfield = await Cat.fromBackend(savedCat.path)
// cats/xyz

Using repositories

You can use repositories as an alternative way to persist and retrieve models in your backend.

This module provides a BaseRepository class that you can extend and override to apply your business logic when doing backend operations.

Let's create a repository for our Cat model, that prevents us from deleting a cat.

import { BackendInterface, BaseRepository } from '@quatrain/backend'

export default class CatRepository extends BaseRepository<Cat> {
   constructor(backendAdapter: BackendInterface = Backend.getBackend()) {
      super(Cat, backendAdapter)
   }

   async delete(uid: string) {
      throw Error("Don't delete the cats!")
   }
}

Now, let's use our new CatRepository.

const repository = new CatRepository()

repository.create(garfield)
repository.read('garfield')
repository.update(persistedGarfield)
repository.delete('garfield') // Will throw "Don't delete the cats!"

About

Core objects for Business Apps

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •