@withvlibe/official-sdk
v1.0.2
Published
Official SDK for Vlibe Official Apps - includes authentication, shared database, payments (Stripe Connect), and React hooks
Downloads
304
Maintainers
Readme
@withvlibe/official-sdk
Official SDK for Vlibe Official Apps - provides authentication, shared database access, payments (Stripe Connect), and React hooks for apps created through Vlibe Briefs.
Installation
npm install @withvlibe/official-sdk
# or
yarn add @withvlibe/official-sdk
# or
pnpm add @withvlibe/official-sdkQuick Start
1. Environment Setup
Your Vlibe Official App already has these credentials in .env:
VLIBE_PROJECT_ID=your_project_id
VLIBE_DB_TOKEN=your_database_token
# Optional: For SSO authentication
VLIBE_APP_ID=your_app_id
VLIBE_APP_SECRET=your_app_secret2. Database Client Setup
Create a database client file:
// lib/vlibe-db.ts
import { VlibeDatabase } from '@withvlibe/official-sdk';
let db: VlibeDatabase | null = null;
export function getDatabase(): VlibeDatabase {
if (!db) {
db = new VlibeDatabase({
projectId: process.env.VLIBE_PROJECT_ID!,
databaseToken: process.env.VLIBE_DB_TOKEN!,
});
}
return db;
}
export { db };3. Basic CRUD Operations
import { getDatabase } from '@/lib/vlibe-db';
const db = getDatabase();
// Create a table (first time only)
await db.createTable('documents', {
columns: [
{ name: 'title', type: 'string', required: true },
{ name: 'content', type: 'string' },
{ name: 'published', type: 'boolean', defaultValue: false },
],
});
// Insert a record
const doc = await db.insert('documents', {
title: 'My Document',
content: 'Hello world',
});
console.log(doc.id); // Auto-generated ID
// Query records
const allDocs = await db.query('documents');
const publishedDocs = await db.query('documents', {
where: { published: true },
orderBy: { column: 'created_at', direction: 'desc' },
limit: 10,
});
// Get single record
const doc = await db.getById('documents', 'doc-123');
// Update a record
await db.update('documents', 'doc-123', {
title: 'Updated Title',
});
// Delete a record
await db.delete('documents', 'doc-123');React Hooks
Import hooks from the /react entry point:
import { useCollection, useKV, useVlibeAuth } from '@withvlibe/official-sdk/react';useCollection
Manage a collection of records with CRUD operations and optional real-time updates:
'use client';
import { useCollection } from '@withvlibe/official-sdk/react';
import { getDatabase } from '@/lib/vlibe-db';
interface Document {
id: string;
title: string;
content: string;
created_at?: string;
}
function DocumentList() {
const db = getDatabase();
const { data, loading, error, create, update, remove, refetch } = useCollection<Document>(
db,
'documents',
{ realtime: true } // Enable real-time updates
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<button onClick={() => create({ title: 'New Doc', content: '' })}>
Add Document
</button>
{data.map(doc => (
<div key={doc.id}>
<h3>{doc.title}</h3>
<button onClick={() => update(doc.id, { title: 'Updated' })}>
Edit
</button>
<button onClick={() => remove(doc.id)}>
Delete
</button>
</div>
))}
</div>
);
}useKV
Manage key-value pairs for settings, preferences, and simple state:
'use client';
import { useKV } from '@withvlibe/official-sdk/react';
import { getDatabase } from '@/lib/vlibe-db';
interface Settings {
theme: 'light' | 'dark';
fontSize: number;
}
function SettingsPanel() {
const db = getDatabase();
const { value: settings, loading, set } = useKV<Settings>(db, 'settings');
if (loading) return <div>Loading...</div>;
return (
<div>
<select
value={settings?.theme || 'dark'}
onChange={(e) => set({ ...settings, theme: e.target.value as 'light' | 'dark' })}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}useVlibeAuth
Handle SSO authentication with Vlibe:
'use client';
import { useVlibeAuth } from '@withvlibe/official-sdk/react';
import { VlibeAuth } from '@withvlibe/official-sdk';
const auth = new VlibeAuth({
appId: process.env.NEXT_PUBLIC_VLIBE_APP_ID!,
appSecret: process.env.VLIBE_APP_SECRET!,
});
function AuthenticatedApp() {
const { user, loading, login, logout, hasFeature, hasSubscription } = useVlibeAuth(auth);
if (loading) return <div>Verifying session...</div>;
if (!user) {
return (
<div>
<h1>Welcome to My App</h1>
<button onClick={() => login('/dashboard')}>
Login with Vlibe
</button>
</div>
);
}
return (
<div>
<p>Hello, {user.name || user.email}!</p>
{hasSubscription() && <p>Thanks for subscribing!</p>}
{hasFeature('premium') && <PremiumFeatures />}
<button onClick={logout}>Logout</button>
</div>
);
}Key-Value Store
For simple data that doesn't need a full table:
const db = getDatabase();
// Set a value
await db.setKV('theme', 'dark');
// Set with namespace (e.g., per-user settings)
await db.setKV('preferences', { notifications: true }, 'user_123');
// Get a value
const theme = await db.getKV<string>('theme');
const prefs = await db.getKV<{ notifications: boolean }>('preferences', 'user_123');
// Delete a value
await db.deleteKV('theme');
// List all keys in a namespace
const allSettings = await db.listKV('user_123');Real-Time Subscriptions
Subscribe to changes for live updates:
const db = getDatabase();
const subscription = db.subscribe('documents', (payload) => {
console.log('Event:', payload.eventType); // INSERT, UPDATE, DELETE
console.log('New data:', payload.new);
console.log('Old data:', payload.old);
});
// Later: stop listening
subscription.unsubscribe();
// Or unsubscribe from everything
db.unsubscribeAll();Payments (Stripe Connect)
VlibePayments handles subscriptions and revenue for Vlibe Official Apps through Stripe Connect. This class is server-side only - never import it in client code.
Setup
// lib/vlibe-payments.ts (SERVER-SIDE ONLY)
import { VlibePayments } from '@withvlibe/official-sdk';
let payments: VlibePayments | null = null;
export function getPayments(): VlibePayments {
if (!payments) {
payments = new VlibePayments({
appId: process.env.VLIBE_APP_ID!,
appSecret: process.env.VLIBE_APP_SECRET!,
});
}
return payments;
}Create Checkout Session
// app/api/subscribe/route.ts
import { NextResponse } from 'next/server';
import { getPayments } from '@/lib/vlibe-payments';
export async function POST(req: Request) {
const { userId, userEmail, tierId } = await req.json();
const payments = getPayments();
const checkoutUrl = await payments.createCheckoutSession({
userId,
userEmail,
tierId,
successUrl: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancelUrl: `${process.env.APP_URL}/pricing`,
billingPeriod: 'monthly', // or 'yearly'
});
return NextResponse.json({ checkoutUrl });
}Check Subscription Status
// app/api/subscription/route.ts
import { NextResponse } from 'next/server';
import { getPayments } from '@/lib/vlibe-payments';
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const userId = searchParams.get('userId');
const payments = getPayments();
const subscription = await payments.getSubscription(userId!);
if (!subscription || subscription.status !== 'active') {
return NextResponse.json({ hasAccess: false });
}
return NextResponse.json({
hasAccess: true,
tier: subscription.tier,
features: subscription.features,
expiresAt: subscription.currentPeriodEnd,
});
}Revenue Dashboard
// app/api/dashboard/revenue/route.ts
import { NextResponse } from 'next/server';
import { getPayments } from '@/lib/vlibe-payments';
export async function GET() {
const payments = getPayments();
const revenue = await payments.getRevenue('month');
const payouts = await payments.getPayoutHistory(6);
return NextResponse.json({
// Revenue is in cents, convert to dollars
monthlyRevenue: revenue.netRevenue / 100,
totalRevenue: revenue.grossRevenue / 100,
platformFees: revenue.platformFees / 100,
subscriberCount: revenue.subscriberCount,
activeSubscribers: revenue.activeSubscribers,
payouts: payouts.map(p => ({
month: p.month,
amount: p.netAmount / 100,
status: p.status,
})),
});
}Cancel Subscription
// app/api/subscription/cancel/route.ts
import { NextResponse } from 'next/server';
import { getPayments } from '@/lib/vlibe-payments';
export async function POST(req: Request) {
const { userId, immediately } = await req.json();
const payments = getPayments();
// Cancel at period end (default) or immediately
await payments.cancelSubscription(userId, immediately);
return NextResponse.json({ success: true });
}Issue Refund
// app/api/refund/route.ts
import { NextResponse } from 'next/server';
import { getPayments } from '@/lib/vlibe-payments';
export async function POST(req: Request) {
const { userId, amount, reason } = await req.json();
const payments = getPayments();
const refundId = await payments.createRefund({
userId,
amount, // Optional - full refund if not specified
reason: reason || 'requested_by_customer',
});
return NextResponse.json({ refundId });
}API Reference
VlibeDatabase
| Method | Description |
|--------|-------------|
| createTable(name, schema) | Create a new table |
| listTables() | List all tables for this project |
| getTable(name) | Get table info |
| query(table, options?) | Query records |
| getById(table, id) | Get single record |
| insert(table, data) | Insert a record |
| update(table, id, data) | Update a record |
| delete(table, id) | Delete a record |
| insertMany(table, records) | Batch insert |
| deleteMany(table, ids) | Batch delete |
| setKV(key, value, namespace?) | Set key-value |
| getKV(key, namespace?) | Get key-value |
| deleteKV(key, namespace?) | Delete key-value |
| listKV(namespace?) | List all key-values |
| subscribe(table, callback) | Real-time subscription |
| unsubscribeAll() | Remove all subscriptions |
| healthCheck() | Test connection |
VlibeAuth
| Method | Description |
|--------|-------------|
| verifySession(token) | Verify SSO token |
| getLoginUrl(redirect?) | Get SSO login URL |
| getLogoutUrl(redirect?) | Get logout URL |
| hasFeature(user, feature) | Check feature access |
| hasSubscription(user) | Check subscription status |
| getTier(user) | Get subscription tier |
VlibePayments (Server-Side Only)
| Method | Description |
|--------|-------------|
| createCheckoutSession(options) | Create Stripe checkout session |
| getSubscription(userId) | Get user's subscription info |
| hasActiveSubscription(userId) | Check if user has active subscription |
| getRevenue(period?) | Get revenue data for your app |
| getPayoutHistory(limit?) | Get payout history |
| cancelSubscription(userId, immediately?) | Cancel a subscription |
| createRefund(options) | Issue a refund |
| getPricingTiers() | Get all pricing tiers |
React Hooks
| Hook | Description |
|------|-------------|
| useCollection(db, table, options?) | CRUD operations with real-time |
| useKV(db, key, namespace?) | Key-value management |
| useKVList(db, namespace?) | List all KV pairs |
| useAuth(auth, options?) | Basic auth hook |
| useVlibeToken() | Extract SSO token from URL |
| useVlibeAuth(auth) | Full auth with auto token handling |
Column Types
When creating tables, you can use these column types:
| Type | Description | PostgreSQL |
|------|-------------|------------|
| string | Text data | TEXT |
| number | Numeric data | DOUBLE PRECISION |
| boolean | True/false | BOOLEAN |
| json | JSON objects | JSONB |
| timestamp | Date/time | TIMESTAMPTZ |
TypeScript Support
The SDK is fully typed. Define your record types for better IntelliSense:
interface Document {
id: string;
title: string;
content: string;
published: boolean;
created_at?: string;
updated_at?: string;
}
// Typed queries
const docs = await db.query<Document>('documents');
const doc = await db.insert<Document>('documents', { title: 'Hello', content: '', published: false });License
MIT - Vlibe
