This guide helps you upgrade between major versions of the AppSheet library.
Version 3.0.0 introduces breaking changes for dependency injection and testing support. The main changes are:
- Factory injection pattern for SchemaManager and ConnectionManager
runAsUserEmailis now required (not optional)- New constructor signatures for all main classes
- New schema introspection methods
// ❌ Old (v2.x)
import { SchemaLoader, SchemaManager, AppSheetClient } from '@techdivision/appsheet';
const client = new AppSheetClient({
appId: 'app-id',
applicationAccessKey: 'key',
runAsUserEmail: 'user@example.com' // optional
});
const schema = SchemaLoader.fromYaml('./schema.yaml');
const db = new SchemaManager(schema);
const table = db.table('conn', 'tableName'); // userEmail optional
// ✅ New (v3.0.0)
import {
SchemaLoader,
SchemaManager,
AppSheetClient,
AppSheetClientFactory,
ConnectionDefinition
} from '@techdivision/appsheet';
// Direct client usage
const connectionDef: ConnectionDefinition = {
appId: 'app-id',
applicationAccessKey: 'key',
tables: { /* table definitions */ }
};
const client = new AppSheetClient(connectionDef, 'user@example.com'); // required
// Schema-based usage with factory injection
const schema = SchemaLoader.fromYaml('./schema.yaml');
const clientFactory = new AppSheetClientFactory();
const db = new SchemaManager(clientFactory, schema);
const table = db.table('conn', 'tableName', 'user@example.com'); // required// ❌ Old (v2.x)
const client = new AppSheetClient({
appId: 'app-id',
applicationAccessKey: 'key',
runAsUserEmail: 'user@example.com' // optional
});
// ✅ New (v3.0.0)
const connectionDef: ConnectionDefinition = {
appId: 'app-id',
applicationAccessKey: 'key',
tables: {
users: { tableName: 'Users', keyField: 'id', fields: {...} }
}
};
const client = new AppSheetClient(connectionDef, 'user@example.com'); // required// ❌ Old (v2.x)
const db = new SchemaManager(schema);
// ✅ New (v3.0.0)
const clientFactory = new AppSheetClientFactory();
const db = new SchemaManager(clientFactory, schema);// ❌ Old (v2.x)
const table = db.table('conn', 'tableName'); // optional user
const table = db.table('conn', 'tableName', 'user@example.com');
// ✅ New (v3.0.0)
const table = db.table('conn', 'tableName', 'user@example.com'); // always required// ❌ Old (v2.x)
const connMgr = new ConnectionManager();
connMgr.register('name', client);
const client = connMgr.get('name', 'user@example.com');
// ✅ New (v3.0.0)
const clientFactory = new AppSheetClientFactory();
const connMgr = new ConnectionManager(clientFactory, schema);
const client = connMgr.get('name', 'user@example.com'); // both required| Removed | Alternative |
|---|---|
AppSheetClient.getConfig() |
Use getTable(tableName) |
ConnectionManager.register() |
Pass schema to constructor |
ConnectionManager.remove() |
Create new instance |
ConnectionManager.clear() |
Create new instance |
ConnectionManager.ping() |
Removed |
ConnectionManager.healthCheck() |
Removed |
SchemaManager.getConnectionManager() |
Internal only |
SchemaManager.reload() |
Create new instance |
Access schema metadata directly:
// Get table definition
const tableDef = db.getTableDefinition('default', 'users');
// → { tableName: 'Users', keyField: 'id', fields: {...} }
// Get field definition
const fieldDef = db.getFieldDefinition('default', 'users', 'status');
// → { type: 'Enum', required: true, allowedValues: [...] }
// Get allowed values for Enum fields
const values = db.getAllowedValues('default', 'users', 'status');
// → ['Active', 'Inactive', 'Pending']import { MockAppSheetClientFactory, SchemaManager } from '@techdivision/appsheet';
// Test setup - no real API calls
const mockFactory = new MockAppSheetClientFactory();
const db = new SchemaManager(mockFactory, schema);
// Test your code
const table = db.table('conn', 'users', 'test@example.com');
await table.add([{ id: '1', name: 'Test' }]);
const users = await table.findAll(); // Returns in-memory dataThis guide helps you upgrade from version 1.x to 2.0.0 of the AppSheet library.
Version 2.0.0 introduces breaking changes to provide better type safety and validation through AppSheet-specific field types. The old generic TypeScript types (string, number, etc.) have been replaced with 27 AppSheet field types that match the actual AppSheet column types.
Old (v1.x): Generic TypeScript types
type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object';New (v2.0.0): AppSheet-specific types
type AppSheetFieldType =
| 'Text' | 'Email' | 'URL' | 'Phone'
| 'Number' | 'Decimal' | 'Percent' | 'Price'
| 'Date' | 'DateTime' | 'Time' | 'Duration'
| 'YesNo' | 'Enum' | 'EnumList'
// ... and 12 more typesOld (v1.x): Mixed formats allowed
fields:
email: string # Shorthand string
age: number # Shorthand string
status: # Full object
type: string
enum: ["Active", "Inactive"]New (v2.0.0): Only full object format
fields:
email:
type: Email
required: true
age:
type: Number
required: false
status:
type: Enum
required: true
allowedValues: ["Active", "Inactive"]| Old Property | New Property | Notes |
|---|---|---|
enum |
allowedValues |
More descriptive name |
# Before (v1.x)
fields:
name: string
email: string
age: number
active: boolean
createdAt: date
tags: array
# After (v2.0.0)
fields:
name:
type: Text
required: false
email:
type: Email
required: true
age:
type: Number
required: false
active:
type: YesNo
required: false
createdAt:
type: DateTime
required: false
tags:
type: EnumList
required: false| Old Type | New Type(s) | Notes |
|---|---|---|
string |
Text, Email, URL, Phone, Address, Name |
Choose based on data |
number |
Number, Decimal, Price, Percent |
Choose based on usage |
boolean |
YesNo |
Accepts boolean or "Yes"/"No" strings |
date |
Date, DateTime, Time, Duration |
Choose based on precision |
array |
EnumList, RefList |
Choose based on usage |
object |
(various) | Depends on context |
# Before (v1.x)
status:
type: string
enum: ["Active", "Inactive", "Pending"]
# After (v2.0.0)
status:
type: Enum
required: true
allowedValues: ["Active", "Inactive", "Pending"]The easiest way to migrate is to use the CLI inspect command:
# Generate new schema from existing AppSheet app
npx appsheet inspect \
--app-id YOUR_APP_ID \
--key YOUR_ACCESS_KEY \
--output schema.yaml
# The CLI will auto-detect field types including:
# - Email addresses
# - URLs
# - Phone numbers
# - Enum fields (with extracted allowedValues)
# - EnumList fields
# - Dates, DateTimes, Percentages, etc.The CLI uses smart heuristics but may need manual adjustments:
# CLI might detect this as Text
website:
type: Text
# But you should change it to URL for validation
website:
type: URL
required: false# Validate schema structure
npx appsheet validate --schema schema.yaml
# Run your application tests
npm test# Before
name: string
# After - Choose appropriate type
name:
type: Text # Generic text
# or
email:
type: Email # Email with validation
# or
website:
type: URL # URL with validation# Before
priority:
type: string
enum: ["Low", "Medium", "High"]
# After
priority:
type: Enum
required: true
allowedValues: ["Low", "Medium", "High"]# Before
skills:
type: array
# After
skills:
type: EnumList
required: false
allowedValues: ["JavaScript", "TypeScript", "Python"]# Before
quantity: number
price: number
discount: number
# After - Choose appropriate numeric type
quantity:
type: Number # Integer or decimal
required: true
price:
type: Price # Currency value
required: true
discount:
type: Percent # 0.00 to 1.00
required: falseVersion 2.0.0 adds comprehensive validation:
// Now validates email format
await table.add([{ email: 'invalid' }]);
// ❌ Error: must be a valid email address// Now validates URL format
await table.add([{ website: 'not-a-url' }]);
// ❌ Error: must be a valid URL// Now validates against allowedValues
await table.add([{ status: 'Unknown' }]);
// ❌ Error: must be one of: Active, Inactive, Pending// Now validates range (0.00 to 1.00)
await table.add([{ discount: 1.5 }]);
// ❌ Error: must be between 0.00 and 1.00For large schemas, consider this migration script:
import * as fs from 'fs';
import * as yaml from 'yaml';
const oldSchema = yaml.parse(fs.readFileSync('old-schema.yaml', 'utf-8'));
// Type mapping
const typeMap = {
'string': 'Text',
'number': 'Number',
'boolean': 'YesNo',
'date': 'Date',
'array': 'EnumList',
'object': 'Text',
};
function migrateField(oldField: any) {
if (typeof oldField === 'string') {
// Shorthand format
return {
type: typeMap[oldField] || 'Text',
required: false,
};
}
// Full object format
const newField: any = {
type: typeMap[oldField.type] || 'Text',
required: oldField.required || false,
};
// Rename enum → allowedValues
if (oldField.enum) {
newField.type = 'Enum';
newField.allowedValues = oldField.enum;
}
if (oldField.description) {
newField.description = oldField.description;
}
return newField;
}
// Migrate all tables
for (const [connName, conn] of Object.entries(oldSchema.connections)) {
for (const [tableName, table] of Object.entries((conn as any).tables)) {
const newFields: any = {};
for (const [fieldName, fieldDef] of Object.entries((table as any).fields)) {
newFields[fieldName] = migrateField(fieldDef);
}
(table as any).fields = newFields;
}
}
fs.writeFileSync('migrated-schema.yaml', yaml.stringify(oldSchema));
console.log('✓ Schema migrated to v2.0.0 format');
console.log('⚠ Please review and adjust field types manually');Cause: Using old generic type in schema
Solution: Replace with AppSheet type:
# ❌ Old
email: string
# ✅ New
email:
type: EmailCause: Using old enum property
Solution: Rename to allowedValues:
# ❌ Old
status:
type: string
enum: ["Active"]
# ✅ New
status:
type: Enum
allowedValues: ["Active"]Cause: Using shorthand string format
Solution: Use full object format:
# ❌ Old
name: string
# ✅ New
name:
type: Text
required: false- Documentation: See CLAUDE.md for complete type reference
- CLI Help: Run
npx appsheet inspect --help - Examples: Check
examples/directory for v2.0.0 schemas - Issues: Report problems at https://github.com/techdivision/appsheet/issues
- ✅ Replace generic types with AppSheet types
- ✅ Convert all fields to full object format
- ✅ Rename
enum→allowedValues - ✅ Use CLI to auto-generate new schema
- ✅ Review and test thoroughly
Estimated Migration Time: 15-30 minutes per schema file (depending on size)