@devvai/devv-code-backend
Version:
Backend SDK for Devv Code - Provides authentication, data management, email and AI capabilities
667 lines (539 loc) • 23.3 kB
Markdown
# Devv Code Backend SDK
Internal SDK providing authentication and data storage functionality for Devv Code frontend projects.
## Authentication Requirements
For data security, most operations require authentication via `auth.verifyOTP()`:
- **Authentication required**: table write operations (add/update/delete), file upload, email sending, AI chat
- **No authentication needed**: table read operations (`getItems`)
Always ensure users are logged in before performing write operations or using AI/email features.
## Installation
```bash
npm install @devvai/devv-code-backend
```
## Import
```typescript
import { auth, table, upload, email, imageGen, webSearch, webReader, replicate } from '@devvai/devv-code-backend';
import { DevvAI, OpenRouterAI, DevvImageGen, DevvWebSearch, DevvWebReader, DevvReplicate } from '@devvai/devv-code-backend';
// Import types for TypeScript users
import type { ReplicateTextToImageModel } from '@devvai/devv-code-backend';
```
## Authentication API
### auth.sendOTP(email: string): Promise<void>
Sends a verification code to the user's email.
### auth.verifyOTP(email: string, verificationCode: string): Promise<AuthResponse>
Verifies the email verification code and logs in, returning user information and session.
### auth.logout(): Promise<void>
Logs out and clears the session.
## Data Table API
### table.addItem(tableId: string, data: Record<string, any>): Promise<void>
Adds a data item to the specified table.
### table.getItems(tableId: string, options?: GetItemsOptions): Promise<GetItemsResponse>
Queries table data with support for filtering, sorting, and pagination. Returns all data using the default `_tid_id_idx` index when no parameters are provided.
### table.updateItem(tableId: string, data: ItemData): Promise<void>
Updates a data item. The data must include complete primary keys.
### table.deleteItem(tableId: string, keys: ItemData): Promise<void>
Deletes a data item. The keys must include complete primary keys.
## File Upload API
### upload.uploadFile(file: File): Promise<UploadFileResponse>
Uploads a file to the server. Returns the upload result including filename and link.
### upload.isErrorResponse(response: UploadFileResponse): response is ErrorResponse
Type guard to check if the response is an error response.
## Email API
### email.sendEmail(options: SendEmailOptions): Promise<EmailResponse>
Sends an email through the Resend service. Supports HTML/text content, attachments, scheduling, and more.
## Image Generation API
### imageGen.textToImage(options: TextToImageOptions): Promise<TextToImageResponse>
Generates images from text prompts using AI models. Supports both text-to-image and image-to-image transformations.
**Parameters:**
- `prompt` (required): Text description of the image to generate or transformation to apply
- `model` (optional): AI model to use (e.g., 'google/gemini-2.5-flash-image')
- `image_url` (optional): URL of existing image for image-to-image transformation (works with google/gemini-2.5-flash-image)
- `num_outputs` (optional): Number of images to generate (1-4, default: 1)
- `aspect_ratio` (optional): Image aspect ratio (e.g., '16:9', '1:1', '9:16')
- `output_format` (optional): Output format ('webp', 'jpg', 'png')
**Returns:**
- `images`: Array of generated image URLs
## Web Search API
### webSearch.search(options: WebSearchOptions): Promise<WebSearchResponse>
Search the web using Jina's SERP API to get search results in LLM-friendly format.
## Web Reader API
### webReader.read(options: WebReaderOptions): Promise<WebReaderResponse>
Extract clean, LLM-friendly content from any web page using Jina's Reader API.
## Replicate API
### replicate.textToImage(options: ReplicateImageOptions): Promise<ReplicateImageResponse>
Generates high-quality images using pre-approved Replicate models.
**Supported Models:**
- `ideogram-ai/ideogram-v3-turbo` - High-quality creative generation
- `black-forest-labs/flux-schnell` - Fast, efficient generation
- `google/imagen-4-fast` - Google's fast model
- `black-forest-labs/flux-kontext-pro` - Context-aware generation
- `prunaai/hidream-l1-fast` - Optimized fast model
- `luma/photon-flash` - Ultra-fast photorealistic
**Parameters:**
- `prompt` (required): Text description
- `model` (required): One of the supported models above
- `num_outputs`: Number of images (1-4, default: 1)
- `negative_prompt`: Things to avoid in generation
- `guidance_scale`: Prompt adherence strength
- Plus other model-specific parameters
## AI API
### DevvAI
Pre-configured OpenAI client (v5.10.2) with automatic authentication. Create an instance after user login to ensure proper authentication.
**Important**:
- No parameters needed when creating `new DevvAI()` - all authentication and configuration is handled internally for security
- The `model` parameter is required in API calls but will be overridden by Devv backend based on your project configuration
```typescript
// Create AI client after login - no parameters needed
const ai = new DevvAI();
// Example usage
const response = await ai.chat.completions.create({
model: 'gpt-3.5-turbo', // Required but overridden by backend
messages: [{ role: 'user', content: 'Hello!' }]
});
// Streaming
const stream = await ai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: 'Tell me a story' }],
stream: true
});
```
For complete usage documentation, refer to the official OpenAI SDK: https://github.com/openai/openai-node
## OpenRouter AI API
### OpenRouterAI
Alternative AI client that uses OpenRouter service for access to multiple model providers. Requires external OpenRouter API key configuration.
**Important**:
- Requires OpenRouter API key to be configured externally in your project settings
- Model parameter must use OpenRouter format: `provider/model-name`
- If you encounter API key errors, ensure you have configured your OpenRouter API key in the external API keys configuration
```typescript
// Create OpenRouter AI client after login
const ai = new OpenRouterAI();
// Using OpenAI models
const response = await ai.chat.completions.create({
model: 'openai/gpt-4-turbo', // Required: specify OpenRouter model format
messages: [{ role: 'user', content: 'Hello!' }]
});
// Using Claude models
const claudeResponse = await ai.chat.completions.create({
model: 'anthropic/claude-3-opus',
messages: [{ role: 'user', content: 'Tell me a joke' }]
});
// Using other providers
const geminiResponse = await ai.chat.completions.create({
model: 'google/gemini-pro',
messages: [{ role: 'user', content: 'Explain quantum computing' }]
});
// Streaming also supported
const stream = await ai.chat.completions.create({
model: 'meta-llama/llama-3-70b-instruct',
messages: [{ role: 'user', content: 'Write a poem' }],
stream: true
});
```
**Common OpenRouter Models**:
- OpenAI: `openai/gpt-4-turbo`, `openai/gpt-3.5-turbo`
- Anthropic: `anthropic/claude-3-opus`, `anthropic/claude-3-sonnet`
- Google: `google/gemini-pro`, `google/gemini-pro-vision`
- Meta: `meta-llama/llama-3-70b-instruct`, `meta-llama/llama-3-8b-instruct`
- And many more providers...
## Text-to-Speech API
### tts.convert(options: TextToSpeechOptions): Promise<TextToSpeechResponse>
Converts text to speech using ElevenLabs API service.
**Important**:
- Requires ElevenLabs API key to be configured externally in your project settings
- Uses ElevenLabs Text-to-Speech API v1
- For detailed parameter documentation, see: https://elevenlabs.io/docs/api-reference/text-to-speech
```typescript
// Simple usage - only text is required
const result = await tts.convert({
text: "Hello, world!"
});
console.log(result.audio_url); // URL to generated audio file
console.log(result.format); // e.g., "mp3_44100_128"
console.log(result.size); // File size in bytes
// Optional parameters available:
// - voice_id: ElevenLabs voice ID
// - model_id: ElevenLabs model ID
// - output_format: Audio format
// - stability: Voice stability (0-1)
// - similarity_boost: Voice similarity (0-1)
// - style: Voice style (0-1)
```
## Type Definitions
```typescript
interface User {
projectId: string;
uid: string;
name: string;
email: string;
createdTime: number;
lastLoginTime: number;
}
interface AuthResponse {
sid: string;
user: User;
}
interface GetItemsOptions {
limit?: number; // Items per page, max 100, default 20
cursor?: string; // Pagination cursor (from previous response's nextCursor)
sort?: string; // Sort field (must be range key of current index)
order?: 'asc' | 'desc'; // Sort order, default 'asc'
query?: Record<string, string | number | boolean | QueryCondition>; // Query conditions, fields must be indexed
}
interface QueryCondition {
operator: QueryOperator;
value: QueryValue;
}
type QueryOperator = 'EQ' | 'NE' | 'LT' | 'LE' | 'GT' | 'GE' | 'BEGINS_WITH' | 'BETWEEN';
type QueryValue = string | number | boolean | [string | number, string | number];
interface GetItemsResponse {
items: Record<string, any>[];
nextCursor?: string;
indexName?: string;
}
type ItemData = Record<string, any>;
interface UploadFileSuccessResponse {
[key: string]: string | undefined;
filename?: string;
link?: string;
}
interface ErrorResponse {
errCode: number;
errMsg: string;
}
type UploadFileResponse = UploadFileSuccessResponse | ErrorResponse;
interface SendEmailOptions {
from: string; // Sender email (must be verified)
to: string[]; // Array of recipient emails
subject: string; // Email subject
bcc?: string[]; // BCC recipients
cc?: string[]; // CC recipients
reply_to?: string; // Reply-to address
html?: string; // HTML content
text?: string; // Plain text content (at least one of html/text required)
tags?: EmailTag[]; // Metadata tags
attachments?: EmailAttachment[]; // File attachments
headers?: Record<string, string>; // Custom headers
scheduled_at?: string; // ISO 8601 timestamp for scheduled delivery
}
interface EmailTag {
name: string;
value: string;
}
interface EmailAttachment {
filename: string; // Filename with extension
content?: number[]; // Binary content as byte array (0-255)
path?: string; // URL to attachment (either content or path required)
content_type?: string; // MIME type
}
interface EmailResponse {
id: string; // Email ID for tracking
}
type ImageAspectRatio = '1:1' | '16:9' | '21:9' | '3:2' | '2:3' | '4:5' | '5:4' | '3:4' | '4:3' | '9:16' | '9:21';
type ImageOutputFormat = 'webp' | 'jpg' | 'png';
interface TextToImageOptions {
prompt: string; // Text description for image generation
model?: string; // Optional model (e.g., 'google/gemini-2.5-flash-image')
image_url?: string; // Optional: URL for image-to-image transformation
num_outputs?: number; // Number of images (1-4, default: 1)
aspect_ratio?: ImageAspectRatio; // Image aspect ratio
output_format?: ImageOutputFormat; // Output format
}
interface TextToImageResponse {
images: string[]; // Array of generated image URLs
}
// Web Search Types
interface WebSearchOptions {
query: string | string[]; // Search query
}
interface WebSearchResult {
title: string; // Page title
url: string; // Page URL
description: string; // Page description
content: string; // Extracted content
date?: string; // Published date
}
interface WebSearchResponse {
code: number; // Response code (200 for success)
status: number; // Status code (20000 for success)
data: WebSearchResult[]; // Array of search results
meta?: {
usage?: {
tokens: number; // Token usage
};
};
}
// Web Reader Types
interface WebReaderOptions {
url: string; // URL to read
}
interface WebReaderData {
title: string; // Page title
description: string; // Meta description
url: string; // Original URL
content: string; // Clean extracted content
publishedTime?: string; // Publication timestamp
metadata?: Record<string, any>; // Page metadata
external?: Record<string, any>; // External resources
warning?: string; // Cache warning
}
interface WebReaderResponse {
code: number; // Response code (200 for success)
status: number; // Status code (20000 for success)
data: WebReaderData; // Extracted page data
meta?: {
usage?: {
tokens: number; // Token usage
};
};
}
```
## Query Operators
| Operator | Description | Applies To |
|----------|-------------|------------|
| EQ | Equals (default) | Hash key (required), Range key |
| NE | Not equals | Range key only |
| LT | Less than | Range key only |
| LE | Less than or equal | Range key only |
| GT | Greater than | Range key only |
| GE | Greater than or equal | Range key only |
| BEGINS_WITH | Prefix match | Range key only |
| BETWEEN | Range (value must be two-element array) | Range key only |
## Internal Session Management
The SDK automatically handles device identification and session management, completely transparent to users:
### Device ID
- **Storage Location**: `localStorage.getItem('DEVV_CODE_DEVICE_ID')`
- **Generation Time**: Automatically generated during SDK initialization (if not exists)
- **Handling**:
- Automatically generates UUID v4 format device ID
- Sent after double SHA256 encryption
- Automatically included in all API request headers as `Device-Id` field
### Session ID
- **Storage Location**: `localStorage.getItem('DEVV_CODE_SID')`
- **Generation Time**: Automatically saved after successful `verifyOTP` call
- **Clear Time**: Automatically cleared when calling `logout`
- **Handling**:
- Automatically extracted and saved from response after successful login
- Automatically included in headers of all authenticated API requests as `sid` field
- No manual session state management needed
## File Upload Usage Example
```typescript
import { upload } from '@devvai/devv-code-backend';
// Upload a file
async function handleFileUpload(file: File) {
try {
const result = await upload.uploadFile(file);
if (upload.isErrorResponse(result)) {
console.error(`Upload failed: ${result.errMsg}`);
if (result.errCode === 429) {
console.error('Upload limit exceeded: max 200 uploads per 24 hours');
}
} else {
console.log('Upload successful:', result);
console.log('File link:', result.link);
}
} catch (error) {
console.error('Upload error:', error);
}
}
// Example with file input
const fileInput = document.querySelector<HTMLInputElement>('#file-input');
fileInput?.addEventListener('change', (event) => {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) {
handleFileUpload(file);
}
});
```
## Image Generation Usage Example
```typescript
import { imageGen } from '@devvai/devv-code-backend';
// Generate a single image
async function generateImage(prompt: string) {
try {
const result = await imageGen.textToImage({
prompt: prompt
});
console.log('Generated image:', result.images[0]);
} catch (error) {
console.error('Failed to generate image:', error);
}
}
// Generate multiple images with custom settings
async function generateMultipleImages(prompt: string) {
try {
const result = await imageGen.textToImage({
prompt: prompt,
num_outputs: 3, // Generate 3 images
aspect_ratio: '16:9', // Widescreen format
output_format: 'png' // PNG format
});
console.log('Generated images:', result.images);
} catch (error) {
console.error('Failed to generate images:', error);
}
}
// Generate portrait image
async function generatePortrait(description: string) {
try {
const result = await imageGen.textToImage({
prompt: `portrait of ${description}`,
aspect_ratio: '3:4', // Portrait orientation
output_format: 'jpg'
});
return result.images[0];
} catch (error) {
console.error('Image generation failed:', error);
throw error;
}
}
// Image-to-image transformation with Google Gemini model
async function transformImage(imageUrl: string, transformation: string) {
try {
const result = await imageGen.textToImage({
prompt: transformation, // e.g., "update the text to black"
model: 'google/gemini-2.5-flash-image',
image_url: imageUrl, // URL of the image to transform
output_format: 'png'
});
console.log('Transformed image:', result.images[0]);
return result.images[0];
} catch (error) {
console.error('Image transformation failed:', error);
throw error;
}
}
```
## Email Usage Example
```typescript
import { email } from '@devvai/devv-code-backend';
// Send a simple text email
async function sendWelcomeEmail(userEmail: string) {
try {
const result = await email.sendEmail({
from: 'noreply@yourdomain.com',
to: [userEmail],
subject: 'Welcome to Our Service',
text: 'Thank you for signing up!'
});
console.log('Email sent:', result.id);
} catch (error) {
console.error('Failed to send email:', error);
}
}
// Send HTML email with attachment
async function sendInvoiceEmail(customerEmail: string, invoiceUrl: string) {
try {
const result = await email.sendEmail({
from: 'billing@yourdomain.com',
to: [customerEmail],
subject: 'Your Invoice',
html: '<h1>Invoice</h1><p>Please find your invoice attached.</p>',
text: 'Invoice\n\nPlease find your invoice attached.',
attachments: [{
filename: 'invoice.pdf',
path: invoiceUrl,
content_type: 'application/pdf'
}],
tags: [
{ name: 'type', value: 'invoice' },
{ name: 'customer', value: customerEmail }
]
});
console.log('Invoice email sent:', result.id);
} catch (error) {
console.error('Failed to send invoice:', error);
}
}
// Schedule an email
async function scheduleReminder(userEmail: string, appointmentDate: Date) {
const reminderDate = new Date(appointmentDate);
reminderDate.setDate(reminderDate.getDate() - 1); // Day before appointment
try {
const result = await email.sendEmail({
from: 'reminders@yourdomain.com',
to: [userEmail],
subject: 'Appointment Reminder',
text: `Reminder: You have an appointment tomorrow at ${appointmentDate.toLocaleTimeString()}.`,
scheduled_at: reminderDate.toISOString()
});
console.log('Reminder scheduled:', result.id);
} catch (error) {
console.error('Failed to schedule reminder:', error);
}
}
```
## Web Search Usage Example
```typescript
import { webSearch } from '@devvai/devv-code-backend';
// Simple web search
const result = await webSearch.search({
query: "artificial intelligence news"
});
// Access results
result.data.forEach(item => {
console.log(item.title, item.url);
console.log(item.description);
});
```
## Web Reader Usage Example
```typescript
import { webReader } from '@devvai/devv-code-backend';
// Read a web page
const result = await webReader.read({
url: "https://example.com/article"
});
// Access content
console.log(result.data.title);
console.log(result.data.content);
```
## Usage Guidelines
1. **Authentication Flow**: First call `sendOTP` to send verification code, then call `verifyOTP` to complete login. It's recommended to use state management libraries (like Zustand) to persist user information.
2. **Query Constraints**:
- Returns all data using default `_tid_id_idx` index when no parameters provided
- Conditional queries must be based on indexed fields (primary or secondary index)
- Primary index is fixed as `_uid` (user ID) and `_id` (time-ordered ID)
- Hash keys can only use EQ operator, other operators will cause errors
- Range keys can use all operators
- Special case: When `_tid` is the hash key of an index, the system fills it automatically, no manual specification needed
3. **Sorting Rules**:
- `sort` field must be the range key of the currently used index
- Cannot sort by arbitrary fields
4. **Route Protection**: You need to implement authentication guards yourself to check user login status and handle route redirects.
5. **Error Handling**: All methods may throw exceptions, please use try-catch for handling.
6. **Pagination Best Practices**: Use cursor pagination, not offset. Cursors remain valid as long as the data structure doesn't change.
7. **File Upload Limits**: Maximum 200 file uploads per 24 hours per user. Files are automatically handled with proper multipart/form-data encoding.
8. **Email Requirements**:
- `from` address must be from a verified domain or email address
- At least one of `html` or `text` content is required
- For attachments, provide either `content` (byte array) or `path` (URL), not both
- Scheduled emails must have a future `scheduled_at` timestamp
9. **AI Chat Usage**:
- Requires active session (must login first with `auth.verifyOTP()`)
- **DevvAI**: The `model` parameter is required but ignored - Devv backend controls the actual model
- **OpenRouterAI**: The `model` parameter must use OpenRouter format (`provider/model-name`)
- Full OpenAI SDK compatibility - all features work identically
- No additional dependencies needed - OpenAI SDK is included
10. **OpenRouter AI Usage**:
- Requires active session (must login first with `auth.verifyOTP()`)
- Must have OpenRouter API key configured in external API keys settings
- Model parameter uses format: `provider/model-name` (e.g., `openai/gpt-4-turbo`, `anthropic/claude-3-opus`)
- If you see API key errors, check your OpenRouter API key configuration
- Supports all OpenRouter available models from various providers
11. **Image Generation Usage**:
- Requires active session (must login first with `auth.verifyOTP()`)
- Uses Flux Schnell model for high-quality text-to-image generation
- `num_outputs` must be between 1-4 (default is 1)
- Generated images are returned as URLs that can be directly used in your application
- Model documentation: https://replicate.com/black-forest-labs/flux-schnell
12. **Web Search and Reader Usage**:
- Requires active session (must login first with `auth.verifyOTP()`)
- Web Search returns up to 5 most relevant results
- Web Reader extracts clean, LLM-friendly content from any URL
- Search queries can be strings or arrays of strings
- URLs must be properly formatted (include https://)
## Important Notes
- Device ID and Session ID are stored in localStorage, clearing browser data will lose them
- Queries don't support non-indexed fields, attempting to query non-indexed fields will cause errors
- BETWEEN operator value must be a two-element array
- The system automatically selects the appropriate index based on your query parameters
- Performance consideration: Use indexed fields for queries to achieve optimal performance