@dypai-ai/client-sdk
v1.12.0
Published
Official JavaScript/TypeScript SDK for DYPAI — backend-as-a-service with visual workflows, AI agents, and MCP.
Maintainers
Readme
@dypai-ai/client-sdk
The official JavaScript/TypeScript SDK for DYPAI — backend-as-a-service with visual workflows, AI agents, and MCP.
npm install @dypai-ai/client-sdkQuick Start
import { createClient } from '@dypai-ai/client-sdk';
const dypai = createClient('https://your-project.dypai.ai');
// Sign in
const { data, error } = await dypai.auth.signInWithPassword({
email: '[email protected]',
password: 'securepassword',
});
// Call an endpoint
const { data: products } = await dypai.api.get('list_products');Table of Contents
- Installation
- Creating the Client
- Authentication
- Database (CRUD)
- Endpoints (Custom API)
- File Upload
- User Management (Admin)
- React Integration
- TypeScript Support
- API Reference
Installation
npm install @dypai-ai/client-sdkReact hooks (optional):
# React hooks are included — no extra package needed
# Import from '@dypai-ai/client-sdk/react'Creating the Client
import { createClient } from '@dypai-ai/client-sdk';
// Basic — JWT-authenticated apps
const dypai = createClient('https://your-project.dypai.ai');
// With API key — for public endpoints that don't require auth
const dypai = createClient(
'https://your-project.dypai.ai',
'your-public-api-key'
);
// With options
const dypai = createClient('https://your-project.dypai.ai', {
auth: {
autoRefreshToken: true, // default: true
persistSession: true, // default: true
},
redirects: {
passwordRecovery: '/reset-password',
signIn: '/dashboard',
},
});Authentication
Sign Up
const { data, error } = await dypai.auth.signUp({
email: '[email protected]',
password: 'securepassword',
name: 'John Doe',
});
if (error) {
console.error(error.message);
} else if (data.confirmationRequired) {
console.log('Check your email to confirm your account');
}Sign In with Password
const { data, error } = await dypai.auth.signInWithPassword({
email: '[email protected]',
password: 'securepassword',
});
if (data) {
console.log('Welcome!', data.user.email);
}Sign In with OAuth
await dypai.auth.signInWithOAuth('google');
// Redirects to Google login pageSign In with Magic Link (OTP)
// Send magic link
const { error } = await dypai.auth.signInWithOtp({ email: '[email protected]' });
// Verify OTP code
const { data, error } = await dypai.auth.verifyOtp({
email: '[email protected]',
token: '123456',
type: 'email',
});Get Current User
const { data: user, error } = await dypai.auth.getUser();
console.log(user.email, user.role);Get Session
const { data: session, error } = await dypai.auth.getSession();
if (session) {
console.log('Token:', session.token);
console.log('User:', session.user.email);
}Password Recovery
// Request reset email
const { error } = await dypai.auth.resetPasswordForEmail('[email protected]');
// Set new password on the passwordRecovery route.
// The SDK stores the reset token and cleans the URL automatically.
const { error } = await dypai.auth.setPassword('new-secure-password');Resend Confirmation Email
const { error } = await dypai.auth.resendConfirmationEmail('[email protected]');Update User Profile
const { data, error } = await dypai.auth.updateUser({
data: { name: 'Jane Doe', avatar_url: 'https://...' },
});Sign Out
await dypai.auth.signOut();Listen to Auth Changes
const { data: { subscription } } = dypai.auth.onAuthStateChange((event, session) => {
console.log(event); // 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | ...
if (event === 'SIGNED_IN') {
console.log('User:', session.user.email);
}
if (event === 'SIGNED_OUT') {
window.location.href = '/login';
}
});
// Stop listening
subscription.unsubscribe();Database (CRUD)
Interact with your project's database tables using a fluent API:
Select (Read)
// Get all rows
const { data, error } = await dypai.db.from('products').select();
// With filters
const { data, error } = await dypai.db.from('products').select({
category: 'electronics',
active: true,
});Insert (Create)
const { data, error } = await dypai.db.from('products').insert({
name: 'Wireless Headphones',
price: 59.99,
category: 'electronics',
});Update
const { data, error } = await dypai.db.from('products').update('prod_123', {
price: 49.99,
on_sale: true,
});Delete
const { data, error } = await dypai.db.from('products').delete('prod_123');Endpoints (Custom API)
Call your custom DYPAI endpoints (workflows exposed as APIs):
GET
const { data, error } = await dypai.api.get('list_products');
// With query parameters
const { data, error } = await dypai.api.get('search', {
params: { q: 'headphones', limit: 20 },
});POST
const { data, error } = await dypai.api.post('create_order', {
product_id: 'prod_123',
quantity: 2,
});PUT / PATCH
const { data, error } = await dypai.api.put('update_profile', {
name: 'Jane Doe',
bio: 'Developer',
});
const { data, error } = await dypai.api.patch('update_settings', {
theme: 'dark',
});DELETE
const { data, error } = await dypai.api.delete('cancel_subscription', {
params: { subscription_id: 'sub_123' },
});File Upload
Upload files using Smart Upload (signed URLs, direct upload to cloud storage):
const file = document.querySelector('input[type="file"]').files[0];
const { data, error } = await dypai.api.upload('storage_files', file, {
params: {
operation: 'upload',
file_path: `avatars/${userId}.jpg`,
// Domain params are fine here, e.g. task_id/file_name/content_type.
// Do not pass confirm or client_upload; the SDK controls Smart Upload.
},
onProgress: (percent) => {
console.log(`Upload: ${percent}%`);
},
});confirm and client_upload are internal Smart Upload flags. They may exist in the endpoint input schema, but browser code should not include them in params.
Download
await dypai.api.download('storage_files', {
operation: 'download',
file_path: 'reports/monthly.pdf',
});Direct Database Access (Admin)
Server-side only. This is for scripts, migrations, seeds, and backend code. Never expose the
serviceRoleKeyin browser/client-side code. For end-user data access, use Endpoints or Database CRUD.
Direct database access bypasses endpoints and workflows. It requires a serviceRoleKey:
import { createClient } from '@dypai-ai/client-sdk';
// Server-side only (Node.js, Bun, Deno)
const dypai = createClient('https://your-project.dypai.ai', {
serviceRoleKey: process.env.DYPAI_SERVICE_ROLE_KEY,
});Select with Filters
const { data } = await dypai.db.direct
.from('products')
.eq('category', 'electronics')
.gt('price', 50)
.orderBy('price', 'DESC')
.limit(20)
.select();Bulk Insert
// Auto-chunked at 1,000 rows per request
const { data, error } = await dypai.db.direct
.from('products')
.insert(tenThousandRows);Update with Filters
await dypai.db.direct
.from('products')
.eq('category', 'deprecated')
.update({ active: false });Delete with Filters
await dypai.db.direct
.from('products')
.eq('id', 'abc123')
.delete();Upsert
await dypai.db.direct
.from('products')
.upsert({ id: 'prod_1', name: 'Widget', price: 12.99 }, 'id');Raw SQL
// Migrations
await dypai.db.direct.sql('ALTER TABLE products ADD COLUMN sku TEXT');
// Queries with bind parameters ($1, $2, ...)
const { data } = await dypai.db.direct.sql(
'SELECT category, COUNT(*) as count FROM products WHERE price > $1 GROUP BY category',
[25]
);Available Filters
| Method | SQL |
|--------|-----|
| .eq(col, val) | col = val |
| .neq(col, val) | col != val |
| .gt(col, val) | col > val |
| .gte(col, val) | col >= val |
| .lt(col, val) | col < val |
| .lte(col, val) | col <= val |
| .like(col, val) | col ILIKE %val% |
| .in(col, [a,b]) | col IN (a, b) |
| .isNull(col) | col IS NULL |
| .notNull(col) | col IS NOT NULL |
User Management (Admin)
Manage application users (requires manage_users permission):
List Users
const { data, error } = await dypai.users.list({ page: 1, per_page: 50 });
console.log(data.users); // [{ id, email, role, ... }, ...]Create User
const { data, error } = await dypai.users.create({
email: '[email protected]',
password: 'securepassword',
role: 'editor',
user_metadata: { name: 'New User' },
});Update User Role
const { data, error } = await dypai.users.update('user_id', {
role: 'admin',
});Delete User
const { data, error } = await dypai.users.delete('user_id');React Integration
Setup
// src/lib/dypai.ts
import { createClient } from '@dypai-ai/client-sdk';
export const dypai = createClient(import.meta.env.VITE_DYPAI_URL);// src/App.tsx
import { DypaiProvider } from '@dypai-ai/client-sdk/react';
import { dypai } from './lib/dypai';
function App() {
return (
<DypaiProvider client={dypai}>
<Router />
</DypaiProvider>
);
}useAuth
import { useAuth } from '@dypai-ai/client-sdk/react';
function LoginPage() {
const { signIn, isLoading, isAuthenticated } = useAuth();
if (isAuthenticated) return <Navigate to="/dashboard" />;
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const { error } = await signIn(email, password);
if (error) alert(error.message);
};
return (
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={...} />
<input type="password" value={password} onChange={...} />
<button disabled={isLoading}>Sign In</button>
</form>
);
}function Navbar() {
const { user, isAuthenticated, signOut } = useAuth();
if (!isAuthenticated) return <Link to="/login">Sign In</Link>;
return (
<div>
<span>{user.email}</span>
<button onClick={signOut}>Sign Out</button>
</div>
);
}useEndpoint
import { useEndpoint } from '@dypai-ai/client-sdk/react';
function ProductList() {
const { data: products, isLoading, error, refetch } = useEndpoint('list_products');
if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;
return (
<div>
{products.map(p => <ProductCard key={p.id} product={p} />)}
<button onClick={refetch}>Refresh</button>
</div>
);
}// With params and auto-refetch
function Notifications() {
const { data } = useEndpoint('get_notifications', {
params: { unread: true },
refetchInterval: 30000, // every 30s
});
return <Badge count={data?.length ?? 0} />;
}// Conditional fetching
function UserProfile({ userId }: { userId?: string }) {
const { data: profile } = useEndpoint('get_user_profile', {
params: { id: userId },
enabled: !!userId, // only fetch when userId exists
});
return profile ? <Profile data={profile} /> : null;
}useAction
import { useAction } from '@dypai-ai/client-sdk/react';
function CreateProduct() {
const { mutate, isLoading } = useAction('create_product', {
onSuccess: (data) => toast.success(`Created: ${data.name}`),
onError: (err) => toast.error(err.message),
});
const handleSubmit = async (formData: ProductForm) => {
const { data, error } = await mutate(formData);
};
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
<button disabled={isLoading}>Create</button>
</form>
);
}// PUT, PATCH, DELETE
const { mutate: updateTask } = useAction('update_task', { method: 'PUT' });
const { mutate: patchSettings } = useAction('settings', { method: 'PATCH' });
const { mutate: deleteItem } = useAction('delete_item', { method: 'DELETE' });
await updateTask({ id: '123', done: true });
await patchSettings({ theme: 'dark' });
await deleteItem({ id: '123' });useChat
import { useChat } from '@dypai-ai/client-sdk/react';
function SupportChat() {
const { messages, input, setInput, sendMessage, isLoading, stop } = useChat('support_agent');
return (
<section>
{messages.map(message => (
<article key={message.id}>
<strong>{message.role}</strong>
<p>{message.content}</p>
{message.parts?.map((part, index) => {
if (part.type === 'tool-call') {
return <small key={index}>Calling {part.toolName}...</small>;
}
if (part.type === 'tool-result') {
return <small key={index}>{part.toolName} finished</small>;
}
return null;
})}
</article>
))}
<input value={input} onChange={event => setInput(event.target.value)} />
<button onClick={() => sendMessage()} disabled={isLoading}>Send</button>
{isLoading && <button onClick={stop}>Stop</button>}
</section>
);
}useChat supports DYPAI agent streams that use the Vercel AI SDK UI Message Stream protocol, plus legacy DYPAI text streams for older engines. Assistant text is accumulated in message.content; streamed tool calls and tool results are exposed in message.parts.
useUpload
import { useUpload } from '@dypai-ai/client-sdk/react';
function AvatarUpload() {
const { upload, progress, isUploading } = useUpload('storage_files', {
onSuccess: () => toast.success('Uploaded!'),
});
const handleFile = async (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
await upload(file, {
operation: 'upload',
file_path: `avatars/${userId}.jpg`,
});
};
return (
<div>
<input type="file" accept="image/*" onChange={handleFile} disabled={isUploading} />
{isUploading && <ProgressBar value={progress} />}
</div>
);
}ProtectedRoute
import { ProtectedRoute } from '@dypai-ai/client-sdk/react';
// Basic protection
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
// With redirect
<ProtectedRoute redirectTo="/login">
<Dashboard />
</ProtectedRoute>
// Role-based access
<ProtectedRoute
roles={['admin', 'editor']}
loadingComponent={<Spinner />}
unauthorizedComponent={<AccessDenied />}
unauthenticatedComponent={<LoginPrompt />}
>
<AdminPanel />
</ProtectedRoute>TypeScript Support
The SDK is fully typed. You can pass your database schema and endpoint map for complete type safety:
// Define your database schema
interface Database {
products: {
id: string;
name: string;
price: number;
active: boolean;
};
orders: {
id: string;
product_id: string;
quantity: number;
status: 'pending' | 'shipped' | 'delivered';
};
}
// Define your endpoint map
interface Api {
list_products: {
response: Database['products'][];
};
create_order: {
body: { product_id: string; quantity: number };
response: Database['orders'];
};
}
// Create a fully typed client
const dypai = createClient<Database, Api>(
'https://your-project.dypai.ai'
);
// Now everything has autocomplete and type checking
const { data } = await dypai.db.from('products').select(); // data: Product[]
const { data } = await dypai.api.post('create_order', { // body is typed
product_id: 'prod_123',
quantity: 2,
});API Reference
Full auto-generated API docs: dypai-solutions.github.io/client-sdk
Response Format
All SDK methods return a consistent { data, error } object:
const { data, error } = await dypai.auth.signInWithPassword({ ... });
if (error) {
console.error(error.message); // Human-readable message
console.error(error.status); // HTTP status code
console.error(error.code); // Error code (if available)
return;
}
// data is guaranteed to be non-null here
console.log(data);License
MIT
