UNPKG

snake-query

Version:
509 lines (434 loc) 12.8 kB
# Schema Building Guide Learn how to create powerful response schemas using SnakeQuery's SchemaBuilder for consistent, type-safe results. ## 🎯 Why Use Schemas? Schemas ensure your query responses are: - **Consistent**: Same structure every time - **Type-safe**: Guaranteed data types - **Validated**: Required fields are present - **Documented**: Clear data contracts ## 🏗️ SchemaBuilder Basics ### Import SchemaBuilder ```javascript const SnakeQuery = require('snake-query'); const { SchemaBuilder } = SnakeQuery; ``` ### Basic Pattern ```javascript const schema = SchemaBuilder.create() .type() // Define base type .constraints() // Add constraints .build(); // Generate final schema ``` ## 📊 Basic Types ### String ```javascript const nameSchema = SchemaBuilder.create() .string({ minLength: 1, maxLength: 100 }) .build(); // Usage in query const result = await client.query({ query: 'Extract user names', data: users, responseSchema: SchemaBuilder.create() .array(nameSchema) .build() }); ``` ### Number ```javascript const priceSchema = SchemaBuilder.create() .number({ minimum: 0, maximum: 10000 }) .build(); // Integer (treated as number internally) const quantitySchema = SchemaBuilder.create() .integer({ minimum: 1 }) .build(); ``` ### Boolean ```javascript const availableSchema = SchemaBuilder.create() .boolean() .build(); ``` ## 🏢 Object Schemas ### Basic Object ```javascript const userSchema = SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('age', { minimum: 0 }) .addStringProperty('email') .required(['name', 'age']) .build(); ``` ### Complex Object ```javascript const productSchema = SchemaBuilder.create() .object() .addStringProperty('title') .addNumberProperty('price', { minimum: 0 }) .addStringProperty('description', { maxLength: 500 }) .addBooleanProperty('inStock') .addArrayProperty('tags', { type: 'string' }) .required(['title', 'price']) .description('Product information') .build(); ``` ### Nested Objects ```javascript const orderSchema = SchemaBuilder.create() .object() .addStringProperty('orderId') .addObjectProperty('customer', { name: { type: 'string' }, email: { type: 'string' }, phone: { type: 'string' } }) .addObjectProperty('shipping', { address: { type: 'string' }, city: { type: 'string' }, zipCode: { type: 'string' } }) .required(['orderId', 'customer']) .build(); ``` ## 📋 Array Schemas ### Simple Arrays ```javascript // Array of strings const tagsSchema = SchemaBuilder.create() .array({ type: 'string' }) .build(); // Array of numbers const ratingsSchema = SchemaBuilder.create() .array({ type: 'number', minimum: 1, maximum: 5 }) .build(); ``` ### Array of Objects ```javascript const productsSchema = SchemaBuilder.create() .array( SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('price', { minimum: 0 }) .addStringProperty('category') .required(['name', 'price']) .build() ) .build(); // Usage const result = await client.query({ query: 'Show all products with name, price and category', fetchUrl: 'https://api.store.com/products', responseSchema: productsSchema }); ``` ### Array Constraints ```javascript const limitedProductsSchema = SchemaBuilder.create() .array(productSchema) .minItems(1) // At least 1 item .maxItems(10) // At most 10 items .build(); ``` ## 🎯 Advanced Patterns ### Conditional Fields ```javascript const userProfileSchema = SchemaBuilder.create() .object() .addStringProperty('name') .addStringProperty('email') .addStringProperty('role') // Optional field .addStringProperty('department') // Optional field .required(['name', 'email']) // Only name and email required .build(); ``` ### Multiple Types in Arrays ```javascript const mixedDataSchema = SchemaBuilder.create() .array( SchemaBuilder.create() .object() .addStringProperty('type') // 'user' or 'order' .addStringProperty('id') .addObjectProperty('data', {}) // Flexible data field .required(['type', 'id']) .build() ) .build(); ``` ### Reusable Schema Components ```javascript // Define reusable schemas const addressSchema = SchemaBuilder.create() .object() .addStringProperty('street') .addStringProperty('city') .addStringProperty('zipCode') .addStringProperty('country') .required(['street', 'city']) .build(); const personSchema = SchemaBuilder.create() .object() .addStringProperty('name') .addStringProperty('email') .addProperty('address', addressSchema) .required(['name', 'email']) .build(); // Use in larger schemas const employeeSchema = SchemaBuilder.create() .object() .addProperty('personalInfo', personSchema) .addStringProperty('employeeId') .addStringProperty('department') .required(['personalInfo', 'employeeId']) .build(); ``` ## 📈 Real-World Examples ### E-commerce Product Catalog ```javascript const catalogSchema = SchemaBuilder.create() .object() .addArrayProperty('products', SchemaBuilder.create() .object() .addStringProperty('id') .addStringProperty('name') .addStringProperty('description') .addNumberProperty('price', { minimum: 0 }) .addNumberProperty('originalPrice', { minimum: 0 }) .addStringProperty('category') .addStringProperty('brand') .addBooleanProperty('inStock') .addNumberProperty('rating', { minimum: 0, maximum: 5 }) .addNumberProperty('reviewCount', { minimum: 0 }) .addArrayProperty('images', { type: 'string' }) .addArrayProperty('tags', { type: 'string' }) .required(['id', 'name', 'price', 'category']) .build() ) .addObjectProperty('pagination', { total: { type: 'number' }, page: { type: 'number' }, limit: { type: 'number' } }) .required(['products']) .build(); const result = await client.query({ query: 'Show first 20 products with all details, include pagination info', fetchUrl: 'https://api.store.com/products', responseSchema: catalogSchema }); ``` ### Sales Analytics Dashboard ```javascript const salesAnalyticsSchema = SchemaBuilder.create() .object() .addObjectProperty('summary', { totalRevenue: { type: 'number', minimum: 0 }, totalOrders: { type: 'number', minimum: 0 }, averageOrderValue: { type: 'number', minimum: 0 }, conversionRate: { type: 'number', minimum: 0, maximum: 100 } }) .addArrayProperty('dailyStats', SchemaBuilder.create() .object() .addStringProperty('date') .addNumberProperty('revenue', { minimum: 0 }) .addNumberProperty('orders', { minimum: 0 }) .addNumberProperty('visitors', { minimum: 0 }) .required(['date', 'revenue', 'orders']) .build() ) .addArrayProperty('topProducts', SchemaBuilder.create() .object() .addStringProperty('productName') .addNumberProperty('sales', { minimum: 0 }) .addNumberProperty('revenue', { minimum: 0 }) .required(['productName', 'sales', 'revenue']) .build() ) .addArrayProperty('insights', { type: 'string' }) .required(['summary', 'dailyStats']) .build(); const result = await client.query({ query: 'Create comprehensive sales dashboard with daily stats, top products, and key insights', fetchUrl: 'https://api.company.com/sales', responseSchema: salesAnalyticsSchema }); ``` ### User Management System ```javascript const userManagementSchema = SchemaBuilder.create() .array( SchemaBuilder.create() .object() .addStringProperty('userId') .addStringProperty('username') .addStringProperty('email') .addStringProperty('firstName') .addStringProperty('lastName') .addStringProperty('role') .addStringProperty('department') .addBooleanProperty('isActive') .addStringProperty('lastLoginDate') .addStringProperty('createdAt') .addObjectProperty('permissions', { canRead: { type: 'boolean' }, canWrite: { type: 'boolean' }, canDelete: { type: 'boolean' }, canAdmin: { type: 'boolean' } }) .required(['userId', 'username', 'email', 'role', 'isActive']) .build() ) .build(); const result = await client.query({ query: 'List all active users with their roles, departments, and permissions', fetchUrl: 'https://api.company.com/users', responseSchema: userManagementSchema }); ``` ## ⚡ Schema Performance Tips ### 1. Keep Schemas Focused ```javascript // ✅ Good: Specific schema for specific use case const productListSchema = SchemaBuilder.create() .array( SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('price') .required(['name', 'price']) .build() ) .build(); // ❌ Avoid: Overly complex schemas with unused fields const overComplexSchema = SchemaBuilder.create() .array( SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('price') .addStringProperty('description') .addArrayProperty('reviews', { type: 'object' }) .addObjectProperty('metadata', {}) // ... many more fields that won't be used .build() ) .build(); ``` ### 2. Use Appropriate Constraints ```javascript // ✅ Good: Reasonable constraints const userSchema = SchemaBuilder.create() .object() .addStringProperty('name', { minLength: 1, maxLength: 100 }) .addNumberProperty('age', { minimum: 0, maximum: 150 }) .required(['name']) .build(); // ❌ Avoid: Overly restrictive constraints const restrictiveSchema = SchemaBuilder.create() .object() .addStringProperty('name', { minLength: 5, maxLength: 10 }) // Too restrictive .addNumberProperty('age', { minimum: 18, maximum: 65 }) // Excludes valid cases .build(); ``` ### 3. Reuse Schema Components ```javascript // ✅ Good: Define once, use multiple times const addressSchema = SchemaBuilder.create() .object() .addStringProperty('street') .addStringProperty('city') .build(); const userSchema = SchemaBuilder.create() .object() .addProperty('homeAddress', addressSchema) .addProperty('workAddress', addressSchema) .build(); ``` ## 🚨 Common Mistakes ### 1. Missing Required Fields ```javascript // ❌ Bad: Forgetting to mark essential fields as required const productSchema = SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('price') // Missing: .required(['name', 'price']) .build(); // ✅ Good: Always specify required fields const productSchema = SchemaBuilder.create() .object() .addStringProperty('name') .addNumberProperty('price') .required(['name', 'price']) .build(); ``` ### 2. Inconsistent Property Names ```javascript // ❌ Bad: Inconsistent naming const userSchema = SchemaBuilder.create() .object() .addStringProperty('user_name') // Snake case .addStringProperty('firstName') // Camel case .addStringProperty('last-name') // Kebab case .build(); // ✅ Good: Consistent naming convention const userSchema = SchemaBuilder.create() .object() .addStringProperty('userName') .addStringProperty('firstName') .addStringProperty('lastName') .build(); ``` ### 3. Overly Complex Nested Structures ```javascript // ❌ Avoid: Too many levels of nesting const complexSchema = SchemaBuilder.create() .object() .addObjectProperty('level1', { level2: { type: 'object', properties: { level3: { type: 'object', properties: { level4: { type: 'string' } } } } } }) .build(); // ✅ Better: Flatten structure or use separate schemas const nestedSchema = SchemaBuilder.create() .object() .addStringProperty('id') .addObjectProperty('details', { name: { type: 'string' }, value: { type: 'string' } }) .build(); ``` ## 🎯 Best Practices Summary 1. **Start Simple**: Begin with basic schemas and add complexity as needed 2. **Be Specific**: Define exact field names and types you expect 3. **Use Constraints**: Set appropriate minimum/maximum values 4. **Mark Required Fields**: Always specify which fields are essential 5. **Keep It Focused**: Create schemas for specific use cases 6. **Reuse Components**: Define common patterns once 7. **Test Your Schemas**: Verify they work with real data 8. **Document Complex Schemas**: Add descriptions for clarity With these patterns and practices, you can create robust, maintainable schemas that ensure consistent, type-safe responses from your SnakeQuery operations.