orbit-supabase
v0.1.1
Published
A generic Orbit.js source adapter for Supabase PostgreSQL databases
Maintainers
Readme
orbit-supabase Package
A generic Orbit.js source adapter for Supabase PostgreSQL databases
Quick Links
- 📋 Design Document - Complete package specification
- 💻 Prototype Implementation - Working proof-of-concept (~700 lines)
- 📖 Usage Example - How Swach would use it
Overview
This package provides a generic, reusable way to connect Orbit.js applications to Supabase backends without needing JSONAPI format or custom transformation logic.
Why This Package?
The Problem
When using Orbit.js with custom backends (non-JSONAPI), developers typically:
- Extend the base
Sourceclass - Implement
_query()and_update()methods - Write custom transformation logic for every model
- Handle relationships manually
- Deal with snake_case ↔ camelCase conversion
- Manage RLS and authentication
Result: 500+ lines of boilerplate code per project
The Solution
orbit-supabase provides convention-based defaults with full configuration flexibility:
// Zero-config setup
const remote = new SupabaseSource({
supabase: supabaseClient,
schema: orbitSchema,
getUserId: () => currentUser?.id,
});
// Works out of the box!Key Features
✅ Convention over Configuration
- Auto-pluralization (post → posts)
- Auto snake_case ↔ camelCase
- Foreign key relationships inferred
✅ Fully Configurable
- Custom table names
- Custom attribute mappings
- Custom serializers
- Relationship overrides
✅ RLS-Aware
- Automatic user_id injection
- Per-type RLS configuration
- Works with Supabase Row Level Security
✅ Type-Safe
- Full TypeScript support
- Generic types for compile-time safety
✅ Framework-Agnostic
- Works with vanilla Orbit.js
- Compatible with ember-orbit
Installation
npm install orbit-supabase @orbit/core @orbit/data @supabase/supabase-jsBasic Usage
import { SupabaseSource } from 'orbit-supabase';
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const remote = new SupabaseSource({
supabase,
schema: orbitSchema,
name: 'remote',
getUserId: () => currentUser?.id,
});
// Use with Orbit coordinator
coordinator.addSource(remote);Advanced Configuration
const remote = new SupabaseSource({
supabase,
schema: orbitSchema,
getUserId: () => currentUser?.id,
// Custom table mapping
typeMap: {
'blog-post': {
tableName: 'articles',
relationships: {
author: { type: 'hasOne', foreignKey: 'author_id' },
tags: { type: 'manyToMany', junctionTable: 'post_tags' },
},
},
},
// Custom pluralization (e.g., using ember-inflector)
pluralize: (word) => pluralizeWord(word),
singularize: (word) => singularizeWord(word),
});Benefits vs Custom Implementation
| Aspect | Custom | orbit-supabase | |--------|--------|----------------| | Lines of code | ~500 | ~50 | | Maintenance | High | Low | | Testing burden | Every model | Config only | | Community support | None | Shared | | Bug fixes | Manual | Automatic | | New features | DIY | Free |
Real-World Impact
Swach (the color palette app this was extracted from):
- Before: 500 lines of custom transformation logic
- After: 50 lines of configuration
- Reduction: 90% less code to maintain
Project Status
- [x] Design complete
- [x] Prototype implemented
- [x] Create npm package
- [x] Write comprehensive tests (27 tests, 76% coverage)
- [x] CI/CD with GitHub Actions
- [ ] Publish to npm
- [ ] Documentation site
- [ ] Example applications
Contributing
This package was extracted from a real-world Orbit.js + Supabase integration. If you're interested in helping build this for the community:
- Review the design document
- Check out the prototype
- See the usage example
- Open an issue or PR to discuss
Architecture
┌─────────────────────────────────────────────┐
│ Orbit.js Application (Store) │
└──────────────────┬──────────────────────────┘
│
│ InitializedRecord
│
┌──────────────────▼──────────────────────────┐
│ orbit-supabase │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ SupabaseSource │ │
│ │ - Convention-based mapping │ │
│ │ - Configurable overrides │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ RecordSerializer │ │
│ │ - Orbit ↔ Supabase transformation │ │
│ │ - snake_case ↔ camelCase │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ RelationshipHandler │ │
│ │ - Foreign keys │ │
│ │ - Junction tables │ │
│ └─────────────────────────────────────────┘ │
└──────────────────┬──────────────────────────┘
│
│ SQL via supabase-js
│
┌──────────────────▼──────────────────────────┐
│ Supabase PostgreSQL │
│ - RLS enforcement │
│ - Automatic timestamps │
│ - Foreign key constraints │
└─────────────────────────────────────────────┘Related Projects
- Orbit.js - Client-side data management
- Supabase - Open source Firebase alternative
- @orbit/jsonapi - JSONAPI source
- ember-orbit - Ember.js integration
License
MIT (proposed)
Author
Extracted from Swach by Robert Wagner (@RobbieTheWagner)
