@dataql/supabase-adapter
v1.4.0
Published
Supabase adapter for DataQL with zero API changes
Downloads
20
Maintainers
Readme
@dataql/supabase-adapter
Migrate from Supabase to DataQL with zero API changes. This adapter provides a Supabase Client-compatible API that uses DataQL under the hood.
Installation
npm install @dataql/core @dataql/supabase-adapterQuick Start
import { createClient } from "@dataql/supabase-adapter";
// Create Supabase client with DataQL backend
const supabase = createClient(
"https://your-project.supabase.co",
"your-anon-key",
{
appToken: "your-app-token",
}
);
// Register table schemas (optional but recommended)
supabase.registerTable("users", {
id: { type: "ID", required: true },
email: { type: "String", required: true, unique: true },
name: { type: "String" },
avatar_url: { type: "String" },
created_at: { type: "Date", default: "now" },
updated_at: { type: "Date", default: "now" },
});
// Use familiar Supabase syntax
const { data, error } = await supabase
.from("users")
.select("id, name, email")
.eq("email", "[email protected]")
.single();
if (error) {
console.error("Error:", error.message);
} else {
console.log("User:", data);
}
// Insert data
const { data: newUser, error: insertError } = await supabase
.from("users")
.insert({
name: "John Doe",
email: "[email protected]",
avatar_url: "https://example.com/avatar.jpg",
});
// Query with filters and pagination
const { data: users, error: queryError } = await supabase
.from("users")
.select("*")
.gte("created_at", "2024-01-01")
.order("created_at", { ascending: false })
.range(0, 9);
// Real-time subscriptions
const subscription = supabase.from("users").on("INSERT", (payload) => {
console.log("New user:", payload.new);
});
// Unsubscribe when done
subscription.unsubscribe();API Compatibility
Supported Supabase Features
Query Builder
- ✅
.from(table)- Select table - ✅
.select(columns)- Column selection with count options - ✅
.insert(data)- Insert records - ✅
.update(data)- Update records - ✅
.upsert(data)- Insert or update records - ✅
.delete()- Delete records
Filtering
- ✅
.eq(column, value)- Equals - ✅
.neq(column, value)- Not equals - ✅
.gt(column, value)- Greater than - ✅
.gte(column, value)- Greater than or equal - ✅
.lt(column, value)- Less than - ✅
.lte(column, value)- Less than or equal - ✅
.like(column, pattern)- Pattern matching - ✅
.ilike(column, pattern)- Case-insensitive pattern matching - ✅
.in(column, values)- In array - ✅
.contains(column, value)- Array/JSON contains - ✅
.textSearch(column, query)- Full-text search
Modifiers
- ✅
.order(column, options)- Ordering - ✅
.range(from, to)- Pagination - ✅
.limit(count)- Limit results - ✅
.single()- Return single record - ✅
.maybeSingle()- Return single record or null
Real-time
- ✅
.on(event, callback)- Real-time subscriptions - ✅ Event types:
INSERT,UPDATE,DELETE,* - ✅ Subscription management with
unsubscribe()
Response Format
- ✅ Supabase response format with
data,error,status - ✅ Error handling with detailed error messages
- ✅ Count support for pagination
DataQL Enhancements
While maintaining Supabase compatibility, you also get DataQL's additional features:
- Offline-first: Automatic offline support and sync
- Multi-region: Global data distribution
- Schema evolution: Dynamic schema updates
- WAL support: Write-ahead logging for reliability
- Unique document creation:
createUnique()method to prevent duplicates
Migration Guide
From Supabase
Replace imports:
// Before import { createClient } from "@supabase/supabase-js"; // After import { createClient } from "@dataql/supabase-adapter";Update client configuration:
// Before const supabase = createClient( "https://your-project.supabase.co", "your-anon-key" ); // After const supabase = createClient( "https://your-project.supabase.co", "your-anon-key", { appToken: "your-app-token", } );Register table schemas (recommended):
// Define your table schemas for better type safety and validation supabase.registerTable("users", { id: { type: "ID", required: true }, email: { type: "String", required: true, unique: true }, name: { type: "String" }, created_at: { type: "Date", default: "now" }, });Your queries work the same:
// This works exactly the same const { data, error } = await supabase .from("users") .select("*") .eq("active", true);
Configuration
const supabase = createClient(supabaseUrl, supabaseKey, {
appToken: "your-app-token", // Authentication token
env: "prod", // 'dev' or 'prod'
devPrefix: "dev_", // Prefix for dev tables
// Supabase-specific options (mocked for now)
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
},
realtime: {
params: {
eventsPerSecond: 10,
},
},
global: {
headers: {
"X-Custom-Header": "value",
},
},
});TypeScript Support
Full TypeScript support with inferred types:
import { createClient, SupabaseResponse } from "@dataql/supabase-adapter";
interface User {
id: string;
email: string;
name?: string;
avatar_url?: string;
created_at?: string;
updated_at?: string;
}
const supabase = createClient(supabaseUrl, supabaseKey, options);
// Type-safe operations
const response: SupabaseResponse<User[]> = await supabase
.from("users")
.select("*");
if (response.error) {
console.error("Error:", response.error.message);
} else {
const users: User[] = response.data || [];
console.log("Users:", users);
}Real-time Subscriptions
Real-time features work just like Supabase:
// Table-level subscription
const subscription = supabase.from("messages").on("INSERT", (payload) => {
console.log("New message:", payload.new);
});
// Channel-based subscription
const channel = supabase.channel("room-1");
channel
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "messages",
},
(payload) => {
console.log("Database change:", payload);
}
)
.subscribe();
// Cleanup
subscription.unsubscribe();
channel.unsubscribe();Advanced Queries
Complex queries with multiple filters and joins:
// Complex filtering
const { data: posts } = await supabase
.from("posts")
.select(
`
id,
title,
content,
author:users!author_id (
id,
name,
email
),
tags:post_tags (
tag:tags (
name
)
)
`
)
.eq("published", true)
.gte("created_at", "2024-01-01")
.textSearch("title", "javascript OR typescript")
.order("created_at", { ascending: false })
.range(0, 19);
// Aggregation queries
const { data: userCount } = await supabase
.from("users")
.select("*", { count: "exact" })
.eq("active", true);
console.log("Active users:", userCount?.count);Error Handling
Standard Supabase error handling:
const { data, error } = await supabase
.from("users")
.insert({ email: "invalid-email" });
if (error) {
console.error("Error code:", error.code);
console.error("Error message:", error.message);
console.error("Error details:", error.details);
console.error("Error hint:", error.hint);
} else {
console.log("Success:", data);
}Limitations
Some advanced Supabase features are not yet supported:
- Auth: Authentication methods are not implemented (implement your own authentication layer)
- Storage: File storage methods are mocked (use DataQL's storage instead)
- Edge Functions: Not supported (use DataQL's serverless functions instead)
- RPC: Remote procedure calls are not yet supported
- Advanced joins: Complex table joins need DataQL enhancement
- Row Level Security: Not implemented (use DataQL's security features instead)
- Triggers: Database triggers are not supported
If you need these features, please open an issue.
Examples
Basic CRUD Operations
// Create
const { data: newPost, error } = await supabase.from("posts").insert({
title: "Getting Started with DataQL",
content: "DataQL provides powerful offline-first capabilities...",
author_id: "user-123",
published: true,
});
// Read
const { data: posts } = await supabase
.from("posts")
.select("id, title, content, created_at")
.eq("published", true)
.order("created_at", { ascending: false });
// Update
const { data: updatedPost } = await supabase
.from("posts")
.update({ title: "Updated Title" })
.eq("id", "post-123");
// Delete
const { data: deletedPost } = await supabase
.from("posts")
.delete()
.eq("id", "post-123");Real-time Chat Application
// Listen for new messages
const messagesSubscription = supabase
.from("messages")
.on("INSERT", (payload) => {
addMessageToUI(payload.new);
});
// Listen for message updates
const updatesSubscription = supabase
.from("messages")
.on("UPDATE", (payload) => {
updateMessageInUI(payload.new);
});
// Send a message
async function sendMessage(text: string, userId: string, roomId: string) {
const { data, error } = await supabase.from("messages").insert({
text,
user_id: userId,
room_id: roomId,
});
if (error) {
console.error("Failed to send message:", error.message);
}
}
// Cleanup subscriptions
function cleanup() {
messagesSubscription.unsubscribe();
updatesSubscription.unsubscribe();
}License
MIT
