A generic Orbit.js source adapter for Supabase PostgreSQL databases
npm install orbit-supabase


> A generic Orbit.js source adapter for Supabase PostgreSQL databases
- š Design Document - Complete package specification
- š» Prototype Implementation - Working proof-of-concept (~700 lines)
- š Usage Example - How Swach would use it
This package provides a generic, reusable way to connect Orbit.js applications to Supabase backends without needing JSONAPI format or custom transformation logic.
When using Orbit.js with custom backends (non-JSONAPI), developers typically:
1. Extend the base Source class
2. Implement _query() and _update() methods
3. Write custom transformation logic for every model
4. Handle relationships manually
5. Deal with snake_case ā camelCase conversion
6. Manage RLS and authentication
Result: 500+ lines of boilerplate code per project
orbit-supabase provides convention-based defaults with full configuration flexibility:
``typescript
// Zero-config setup
const remote = new SupabaseSource({
supabase: supabaseClient,
schema: orbitSchema,
getUserId: () => currentUser?.id,
});
// Works out of the box!
`
ā
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
`bash`
npm install orbit-supabase @orbit/core @orbit/data @supabase/supabase-js
`typescript
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);
`
`typescript`
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),
});
| 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 |
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
- [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
This package was extracted from a real-world Orbit.js + Supabase integration. If you're interested in helping build this for the community:
1. Review the design document
2. Check out the prototype
3. See the usage example
4. Open an issue or PR to discuss
```
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 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 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
- Orbit.js - Client-side data management
- Supabase - Open source Firebase alternative
- @orbit/jsonapi - JSONAPI source
- ember-orbit - Ember.js integration
MIT (proposed)
Extracted from Swach by Robert Wagner (@RobbieTheWagner)