Add QueryBuilder documentation and video tutorials
- Created a new documentation file for the QueryBuilder class, detailing its methods and usage examples. - Added a comprehensive video tutorials index, covering various aspects of the Debros Network framework. - Updated the sidebar configuration to include the new video tutorials section and reorganized the API documentation categories.
This commit is contained in:
parent
8cd23b433c
commit
c7babf9aea
@ -2,9 +2,15 @@
|
|||||||
"src/**/*.{js,ts}": [
|
"src/**/*.{js,ts}": [
|
||||||
"prettier --write",
|
"prettier --write",
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
|
"pnpm run test:unit",
|
||||||
"npm run build"
|
"npm run build"
|
||||||
],
|
],
|
||||||
|
"tests/**/*.{js,ts}": [
|
||||||
|
"prettier --write",
|
||||||
|
"eslint --fix",
|
||||||
|
"pnpm run test:unit"
|
||||||
|
],
|
||||||
"*.{json,md}": [
|
"*.{json,md}": [
|
||||||
"prettier --write"
|
"prettier --write"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
810
docs/docs/api/base-model.md
Normal file
810
docs/docs/api/base-model.md
Normal file
@ -0,0 +1,810 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# BaseModel Class
|
||||||
|
|
||||||
|
The `BaseModel` class is the abstract base class for all data models in Debros Network. It provides ORM-like functionality with automatic database management, validation, relationships, and lifecycle hooks.
|
||||||
|
|
||||||
|
## Class Definition
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
abstract class BaseModel {
|
||||||
|
// Instance properties
|
||||||
|
id: string;
|
||||||
|
createdAt?: number;
|
||||||
|
updatedAt?: number;
|
||||||
|
|
||||||
|
// Static methods
|
||||||
|
static async create<T extends BaseModel>(
|
||||||
|
this: ModelConstructor<T>,
|
||||||
|
data: Partial<T>,
|
||||||
|
options?: CreateOptions,
|
||||||
|
): Promise<T>;
|
||||||
|
|
||||||
|
static async findById<T extends BaseModel>(
|
||||||
|
this: ModelConstructor<T>,
|
||||||
|
id: string,
|
||||||
|
options?: FindOptions,
|
||||||
|
): Promise<T | null>;
|
||||||
|
|
||||||
|
static async findOne<T extends BaseModel>(
|
||||||
|
this: ModelConstructor<T>,
|
||||||
|
criteria: Partial<T>,
|
||||||
|
options?: FindOptions,
|
||||||
|
): Promise<T | null>;
|
||||||
|
|
||||||
|
static query<T extends BaseModel>(this: ModelConstructor<T>): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Instance methods
|
||||||
|
save(options?: SaveOptions): Promise<this>;
|
||||||
|
delete(options?: DeleteOptions): Promise<boolean>;
|
||||||
|
reload(options?: ReloadOptions): Promise<this>;
|
||||||
|
validate(): Promise<ValidationResult>;
|
||||||
|
toJSON(): Record<string, any>;
|
||||||
|
clone(): this;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static Methods
|
||||||
|
|
||||||
|
### create(data, options?)
|
||||||
|
|
||||||
|
Creates a new model instance and saves it to the database.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `data`: Partial model data
|
||||||
|
- `options` (optional): Creation options
|
||||||
|
|
||||||
|
**Returns:** `Promise<T>` - The created model instance
|
||||||
|
|
||||||
|
**Throws:**
|
||||||
|
|
||||||
|
- `ValidationError` - If validation fails
|
||||||
|
- `DatabaseError` - If database operation fails
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { BaseModel, Model, Field } from '@debros/network';
|
||||||
|
|
||||||
|
@Model({
|
||||||
|
scope: 'global',
|
||||||
|
type: 'docstore',
|
||||||
|
})
|
||||||
|
class User extends BaseModel {
|
||||||
|
@Field({ type: 'string', required: true, unique: true })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true, unique: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Field({ type: 'number', required: false, default: 0 })
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new user
|
||||||
|
const user = await User.create({
|
||||||
|
username: 'alice',
|
||||||
|
email: 'alice@example.com',
|
||||||
|
score: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(user.id); // Generated ID
|
||||||
|
console.log(user.username); // 'alice'
|
||||||
|
console.log(user.createdAt); // Timestamp
|
||||||
|
```
|
||||||
|
|
||||||
|
**With validation:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const user = await User.create({
|
||||||
|
username: 'ab', // Too short
|
||||||
|
email: 'invalid-email', // Invalid format
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
console.log('Validation failed:', error.field, error.constraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With options:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.create(
|
||||||
|
{
|
||||||
|
username: 'bob',
|
||||||
|
email: 'bob@example.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validate: true,
|
||||||
|
skipHooks: false,
|
||||||
|
userId: 'user123', // For user-scoped models
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### findById(id, options?)
|
||||||
|
|
||||||
|
Finds a model instance by its ID.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `id`: The model ID to search for
|
||||||
|
- `options` (optional): Find options
|
||||||
|
|
||||||
|
**Returns:** `Promise<T | null>` - The found model or null
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Basic find
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
if (user) {
|
||||||
|
console.log('Found user:', user.username);
|
||||||
|
} else {
|
||||||
|
console.log('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// With relationships
|
||||||
|
const user = await User.findById('user123', {
|
||||||
|
with: ['posts', 'profile'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// With specific user context
|
||||||
|
const user = await User.findById('user123', {
|
||||||
|
userId: 'current-user-id',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### findOne(criteria, options?)
|
||||||
|
|
||||||
|
Finds the first model instance matching the criteria.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `criteria`: Partial model data to match
|
||||||
|
- `options` (optional): Find options
|
||||||
|
|
||||||
|
**Returns:** `Promise<T | null>` - The found model or null
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find by field
|
||||||
|
const user = await User.findOne({
|
||||||
|
username: 'alice',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find with multiple criteria
|
||||||
|
const user = await User.findOne({
|
||||||
|
email: 'alice@example.com',
|
||||||
|
isActive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// With options
|
||||||
|
const user = await User.findOne(
|
||||||
|
{
|
||||||
|
username: 'alice',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
with: ['posts'],
|
||||||
|
cache: 300, // Cache for 5 minutes
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### query()
|
||||||
|
|
||||||
|
Returns a query builder for complex queries.
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>` - Query builder instance
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Basic query
|
||||||
|
const users = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.orderBy('createdAt', 'desc')
|
||||||
|
.limit(10)
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// Complex query with relationships
|
||||||
|
const activeUsers = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.where('score', '>', 100)
|
||||||
|
.where('registeredAt', '>', Date.now() - 30 * 24 * 60 * 60 * 1000)
|
||||||
|
.with(['posts.comments', 'profile'])
|
||||||
|
.orderBy('score', 'desc')
|
||||||
|
.paginate(1, 20);
|
||||||
|
|
||||||
|
// Query with caching
|
||||||
|
const cachedUsers = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.cache(600) // Cache for 10 minutes
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Instance Methods
|
||||||
|
|
||||||
|
### save(options?)
|
||||||
|
|
||||||
|
Saves the current model instance to the database.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `options` (optional): Save options
|
||||||
|
|
||||||
|
**Returns:** `Promise<this>` - The saved model instance
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
user.email = 'newemail@example.com';
|
||||||
|
user.score += 10;
|
||||||
|
|
||||||
|
await user.save();
|
||||||
|
console.log('User updated');
|
||||||
|
|
||||||
|
// With options
|
||||||
|
await user.save({
|
||||||
|
validate: true,
|
||||||
|
skipHooks: false,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### delete(options?)
|
||||||
|
|
||||||
|
Deletes the model instance from the database.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `options` (optional): Delete options
|
||||||
|
|
||||||
|
**Returns:** `Promise<boolean>` - True if deletion was successful
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
if (user) {
|
||||||
|
const deleted = await user.delete();
|
||||||
|
console.log('User deleted:', deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// With options
|
||||||
|
await user.delete({
|
||||||
|
skipHooks: false,
|
||||||
|
cascade: true, // Delete related records
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### reload(options?)
|
||||||
|
|
||||||
|
Reloads the model instance from the database.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `options` (optional): Reload options
|
||||||
|
|
||||||
|
**Returns:** `Promise<this>` - The reloaded model instance
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
|
||||||
|
// Model might be updated elsewhere
|
||||||
|
await user.reload();
|
||||||
|
|
||||||
|
// With relationships
|
||||||
|
await user.reload({
|
||||||
|
with: ['posts', 'profile'],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### validate()
|
||||||
|
|
||||||
|
Validates the current model instance.
|
||||||
|
|
||||||
|
**Returns:** `Promise<ValidationResult>` - Validation result
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = new User();
|
||||||
|
user.username = 'alice';
|
||||||
|
user.email = 'invalid-email';
|
||||||
|
|
||||||
|
const result = await user.validate();
|
||||||
|
if (!result.valid) {
|
||||||
|
console.log('Validation errors:', result.errors);
|
||||||
|
// [{ field: 'email', constraint: 'must be valid email', value: 'invalid-email' }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### toJSON()
|
||||||
|
|
||||||
|
Converts the model instance to a plain JavaScript object.
|
||||||
|
|
||||||
|
**Returns:** `Record<string, any>` - Plain object representation
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
const userObj = user.toJSON();
|
||||||
|
|
||||||
|
console.log(userObj);
|
||||||
|
// {
|
||||||
|
// id: 'user123',
|
||||||
|
// username: 'alice',
|
||||||
|
// email: 'alice@example.com',
|
||||||
|
// score: 100,
|
||||||
|
// createdAt: 1234567890,
|
||||||
|
// updatedAt: 1234567890
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Useful for JSON serialization
|
||||||
|
const jsonString = JSON.stringify(user); // Calls toJSON() automatically
|
||||||
|
```
|
||||||
|
|
||||||
|
### clone()
|
||||||
|
|
||||||
|
Creates a deep copy of the model instance (without ID).
|
||||||
|
|
||||||
|
**Returns:** `this` - Cloned model instance
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const user = await User.findById('user123');
|
||||||
|
const userCopy = user.clone();
|
||||||
|
|
||||||
|
userCopy.username = 'alice_copy';
|
||||||
|
await userCopy.save(); // Creates new record
|
||||||
|
|
||||||
|
console.log(user.id !== userCopy.id); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle Hooks
|
||||||
|
|
||||||
|
### Hook Decorators
|
||||||
|
|
||||||
|
Models can define lifecycle hooks using decorators:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@Model({ scope: 'global', type: 'docstore' })
|
||||||
|
class User extends BaseModel {
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Field({ type: 'number', required: false })
|
||||||
|
loginCount: number = 0;
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
async beforeCreateHook() {
|
||||||
|
this.createdAt = Date.now();
|
||||||
|
this.updatedAt = Date.now();
|
||||||
|
|
||||||
|
// Validate unique username
|
||||||
|
const existing = await User.findOne({ username: this.username });
|
||||||
|
if (existing) {
|
||||||
|
throw new ValidationError('username', this.username, 'must be unique');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterCreate()
|
||||||
|
async afterCreateHook() {
|
||||||
|
console.log(`New user created: ${this.username}`);
|
||||||
|
|
||||||
|
// Send welcome email
|
||||||
|
await this.sendWelcomeEmail();
|
||||||
|
|
||||||
|
// Create default settings
|
||||||
|
await this.createDefaultSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeUpdate()
|
||||||
|
async beforeUpdateHook() {
|
||||||
|
this.updatedAt = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterUpdate()
|
||||||
|
async afterUpdateHook() {
|
||||||
|
console.log(`User updated: ${this.username}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeDelete()
|
||||||
|
async beforeDeleteHook() {
|
||||||
|
// Clean up related data
|
||||||
|
await this.deleteRelatedPosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterDelete()
|
||||||
|
async afterDeleteHook() {
|
||||||
|
console.log(`User deleted: ${this.username}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom methods
|
||||||
|
private async sendWelcomeEmail() {
|
||||||
|
// Implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createDefaultSettings() {
|
||||||
|
// Implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
private async deleteRelatedPosts() {
|
||||||
|
// Implementation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Hook Types
|
||||||
|
|
||||||
|
| Hook | Trigger | Usage |
|
||||||
|
| ----------------- | ------------------------------- | --------------------------------------- |
|
||||||
|
| `@BeforeCreate()` | Before creating new record | Validation, default values, preparation |
|
||||||
|
| `@AfterCreate()` | After creating new record | Notifications, related record creation |
|
||||||
|
| `@BeforeUpdate()` | Before updating existing record | Validation, timestamp updates, logging |
|
||||||
|
| `@AfterUpdate()` | After updating existing record | Cache invalidation, notifications |
|
||||||
|
| `@BeforeDelete()` | Before deleting record | Cleanup, validation, logging |
|
||||||
|
| `@AfterDelete()` | After deleting record | Cleanup, notifications, cascade deletes |
|
||||||
|
| `@BeforeSave()` | Before save (create or update) | Common validation, timestamps |
|
||||||
|
| `@AfterSave()` | After save (create or update) | Common post-processing |
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### Field Validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@Model({ scope: 'global', type: 'docstore' })
|
||||||
|
class User extends BaseModel {
|
||||||
|
@Field({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
minLength: 3,
|
||||||
|
maxLength: 20,
|
||||||
|
validate: (username: string) => /^[a-zA-Z0-9_]+$/.test(username),
|
||||||
|
transform: (username: string) => username.toLowerCase(),
|
||||||
|
})
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
validate: (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email),
|
||||||
|
transform: (email: string) => email.toLowerCase(),
|
||||||
|
})
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
validate: (score: number) => score >= 0 && score <= 1000,
|
||||||
|
})
|
||||||
|
score: number;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
type: 'array',
|
||||||
|
required: false,
|
||||||
|
default: [],
|
||||||
|
validate: (tags: string[]) => tags.length <= 10,
|
||||||
|
})
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Validation Methods
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@Model({ scope: 'global', type: 'docstore' })
|
||||||
|
class User extends BaseModel {
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
// Custom validation method
|
||||||
|
async customValidation(): Promise<ValidationResult> {
|
||||||
|
const errors: ValidationError[] = [];
|
||||||
|
|
||||||
|
// Check username availability
|
||||||
|
if (this.username) {
|
||||||
|
const existing = await User.findOne({ username: this.username });
|
||||||
|
if (existing && existing.id !== this.id) {
|
||||||
|
errors.push(new ValidationError('username', this.username, 'already taken'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check email format and availability
|
||||||
|
if (this.email) {
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(this.email)) {
|
||||||
|
errors.push(new ValidationError('email', this.email, 'invalid format'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await User.findOne({ email: this.email });
|
||||||
|
if (existing && existing.id !== this.id) {
|
||||||
|
errors.push(new ValidationError('email', this.email, 'already registered'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check password strength
|
||||||
|
if (this.password && this.password.length < 8) {
|
||||||
|
errors.push(new ValidationError('password', this.password, 'must be at least 8 characters'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: errors.length === 0,
|
||||||
|
errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
@BeforeUpdate()
|
||||||
|
async validateBeforeSave() {
|
||||||
|
const result = await this.customValidation();
|
||||||
|
if (!result.valid) {
|
||||||
|
throw new ValidationError('model', this, 'custom validation failed', result.errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Interfaces
|
||||||
|
|
||||||
|
### ModelConstructor Type
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type ModelConstructor<T extends BaseModel> = new () => T;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options Interfaces
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface CreateOptions {
|
||||||
|
validate?: boolean;
|
||||||
|
skipHooks?: boolean;
|
||||||
|
userId?: string; // For user-scoped models
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FindOptions {
|
||||||
|
with?: string[]; // Relationship names to eager load
|
||||||
|
cache?: boolean | number; // Cache result
|
||||||
|
userId?: string; // For user-scoped models
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SaveOptions {
|
||||||
|
validate?: boolean;
|
||||||
|
skipHooks?: boolean;
|
||||||
|
upsert?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteOptions {
|
||||||
|
skipHooks?: boolean;
|
||||||
|
cascade?: boolean; // Delete related records
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReloadOptions {
|
||||||
|
with?: string[]; // Relationship names to eager load
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation Types
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ValidationResult {
|
||||||
|
valid: boolean;
|
||||||
|
errors: ValidationError[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValidationError extends Error {
|
||||||
|
constructor(
|
||||||
|
public field: string,
|
||||||
|
public value: any,
|
||||||
|
public constraint: string,
|
||||||
|
public details?: any,
|
||||||
|
) {
|
||||||
|
super(`Validation failed for field '${field}': ${constraint}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Common Errors
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Validation errors
|
||||||
|
try {
|
||||||
|
const user = await User.create({
|
||||||
|
username: 'a', // Too short
|
||||||
|
email: 'invalid',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
console.log(`Field ${error.field} failed: ${error.constraint}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found errors
|
||||||
|
const user = await User.findById('non-existent-id');
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError('User', 'non-existent-id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database errors
|
||||||
|
try {
|
||||||
|
await user.save();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DatabaseError) {
|
||||||
|
console.log('Database operation failed:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Example
|
||||||
|
|
||||||
|
### Blog Post Model
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
BaseModel,
|
||||||
|
Model,
|
||||||
|
Field,
|
||||||
|
BelongsTo,
|
||||||
|
HasMany,
|
||||||
|
BeforeCreate,
|
||||||
|
AfterCreate,
|
||||||
|
} from '@debros/network';
|
||||||
|
import { User } from './User';
|
||||||
|
import { Comment } from './Comment';
|
||||||
|
|
||||||
|
@Model({
|
||||||
|
scope: 'user',
|
||||||
|
type: 'docstore',
|
||||||
|
sharding: {
|
||||||
|
strategy: 'user',
|
||||||
|
count: 2,
|
||||||
|
key: 'authorId',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class Post extends BaseModel {
|
||||||
|
@Field({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
minLength: 1,
|
||||||
|
maxLength: 200,
|
||||||
|
})
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
minLength: 1,
|
||||||
|
maxLength: 10000,
|
||||||
|
})
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
authorId: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
type: 'array',
|
||||||
|
required: false,
|
||||||
|
default: [],
|
||||||
|
transform: (tags: string[]) => tags.map((tag) => tag.toLowerCase()),
|
||||||
|
})
|
||||||
|
tags: string[];
|
||||||
|
|
||||||
|
@Field({ type: 'boolean', required: false, default: false })
|
||||||
|
isPublished: boolean;
|
||||||
|
|
||||||
|
@Field({ type: 'number', required: false, default: 0 })
|
||||||
|
viewCount: number;
|
||||||
|
|
||||||
|
@Field({ type: 'number', required: false })
|
||||||
|
publishedAt?: number;
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
@BelongsTo(() => User, 'authorId')
|
||||||
|
author: User;
|
||||||
|
|
||||||
|
@HasMany(() => Comment, 'postId')
|
||||||
|
comments: Comment[];
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
setupNewPost() {
|
||||||
|
this.createdAt = Date.now();
|
||||||
|
this.updatedAt = Date.now();
|
||||||
|
this.viewCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterCreate()
|
||||||
|
async afterPostCreated() {
|
||||||
|
console.log(`New post created: ${this.title}`);
|
||||||
|
|
||||||
|
// Update author's post count
|
||||||
|
const author = await User.findById(this.authorId);
|
||||||
|
if (author) {
|
||||||
|
author.postCount = (author.postCount || 0) + 1;
|
||||||
|
await author.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom methods
|
||||||
|
async publish(): Promise<void> {
|
||||||
|
this.isPublished = true;
|
||||||
|
this.publishedAt = Date.now();
|
||||||
|
await this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async incrementViews(): Promise<void> {
|
||||||
|
this.viewCount += 1;
|
||||||
|
await this.save({ skipHooks: true }); // Skip hooks for performance
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCommentCount(): Promise<number> {
|
||||||
|
return await Comment.query().where('postId', this.id).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTopComments(limit: number = 5): Promise<Comment[]> {
|
||||||
|
return await Comment.query()
|
||||||
|
.where('postId', this.id)
|
||||||
|
.orderBy('likeCount', 'desc')
|
||||||
|
.limit(limit)
|
||||||
|
.with(['author'])
|
||||||
|
.find();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage examples
|
||||||
|
async function blogExamples() {
|
||||||
|
// Create a post
|
||||||
|
const post = await Post.create({
|
||||||
|
title: 'My First Post',
|
||||||
|
content: 'This is the content of my first post...',
|
||||||
|
authorId: 'user123',
|
||||||
|
tags: ['JavaScript', 'Web Development'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find posts by author
|
||||||
|
const userPosts = await Post.query()
|
||||||
|
.where('authorId', 'user123')
|
||||||
|
.where('isPublished', true)
|
||||||
|
.orderBy('publishedAt', 'desc')
|
||||||
|
.with(['author', 'comments.author'])
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// Publish a post
|
||||||
|
await post.publish();
|
||||||
|
|
||||||
|
// Get post with comments
|
||||||
|
const postWithComments = await Post.findById(post.id, {
|
||||||
|
with: ['author', 'comments.author'],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Post:', postWithComments?.title);
|
||||||
|
console.log('Author:', postWithComments?.author.username);
|
||||||
|
console.log('Comments:', postWithComments?.comments.length);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This comprehensive BaseModel documentation covers all the essential functionality for working with models in Debros Network, including CRUD operations, validation, relationships, hooks, and real-world usage examples.
|
828
docs/docs/api/query-builder.md
Normal file
828
docs/docs/api/query-builder.md
Normal file
@ -0,0 +1,828 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
# QueryBuilder Class
|
||||||
|
|
||||||
|
The `QueryBuilder` class provides a fluent API for constructing complex database queries. It supports filtering, sorting, relationships, pagination, and caching with type safety throughout.
|
||||||
|
|
||||||
|
## Class Definition
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class QueryBuilder<T extends BaseModel> {
|
||||||
|
// Filtering methods
|
||||||
|
where(field: string, value: any): QueryBuilder<T>;
|
||||||
|
where(field: string, operator: QueryOperator, value: any): QueryBuilder<T>;
|
||||||
|
whereIn(field: string, values: any[]): QueryBuilder<T>;
|
||||||
|
whereNotIn(field: string, values: any[]): QueryBuilder<T>;
|
||||||
|
whereNull(field: string): QueryBuilder<T>;
|
||||||
|
whereNotNull(field: string): QueryBuilder<T>;
|
||||||
|
whereBetween(field: string, min: any, max: any): QueryBuilder<T>;
|
||||||
|
whereRaw(condition: string, parameters?: any[]): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Logical operators
|
||||||
|
and(): QueryBuilder<T>;
|
||||||
|
or(): QueryBuilder<T>;
|
||||||
|
not(): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Sorting
|
||||||
|
orderBy(field: string, direction?: 'asc' | 'desc'): QueryBuilder<T>;
|
||||||
|
orderByRaw(orderClause: string): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Limiting and pagination
|
||||||
|
limit(count: number): QueryBuilder<T>;
|
||||||
|
offset(count: number): QueryBuilder<T>;
|
||||||
|
paginate(page: number, perPage: number): Promise<PaginatedResult<T>>;
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
with(relationships: string[]): QueryBuilder<T>;
|
||||||
|
withCount(relationships: string[]): QueryBuilder<T>;
|
||||||
|
whereHas(relationship: string, callback?: (query: QueryBuilder<any>) => void): QueryBuilder<T>;
|
||||||
|
whereDoesntHave(relationship: string): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Aggregation
|
||||||
|
count(): Promise<number>;
|
||||||
|
sum(field: string): Promise<number>;
|
||||||
|
avg(field: string): Promise<number>;
|
||||||
|
min(field: string): Promise<any>;
|
||||||
|
max(field: string): Promise<any>;
|
||||||
|
|
||||||
|
// Caching
|
||||||
|
cache(ttl?: number): QueryBuilder<T>;
|
||||||
|
fresh(): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Execution
|
||||||
|
find(): Promise<T[]>;
|
||||||
|
findOne(): Promise<T | null>;
|
||||||
|
first(): Promise<T | null>;
|
||||||
|
get(): Promise<T[]>;
|
||||||
|
|
||||||
|
// Advanced features
|
||||||
|
distinct(field?: string): QueryBuilder<T>;
|
||||||
|
groupBy(field: string): QueryBuilder<T>;
|
||||||
|
having(field: string, operator: QueryOperator, value: any): QueryBuilder<T>;
|
||||||
|
|
||||||
|
// Query info
|
||||||
|
toSQL(): string;
|
||||||
|
getParameters(): any[];
|
||||||
|
explain(): Promise<QueryPlan>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filtering Methods
|
||||||
|
|
||||||
|
### where(field, value) / where(field, operator, value)
|
||||||
|
|
||||||
|
Adds a WHERE condition to the query.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name to filter on
|
||||||
|
- `value`: Value to compare (when using equals operator)
|
||||||
|
- `operator`: Comparison operator
|
||||||
|
- `value`: Value to compare against
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>` - Builder instance for chaining
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Basic equality
|
||||||
|
const activeUsers = await User.query().where('isActive', true).find();
|
||||||
|
|
||||||
|
// With operators
|
||||||
|
const highScoreUsers = await User.query()
|
||||||
|
.where('score', '>', 1000)
|
||||||
|
.where('registeredAt', '>=', Date.now() - 30 * 24 * 60 * 60 * 1000)
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// String operations
|
||||||
|
const usersWithAlice = await User.query().where('username', 'like', '%alice%').find();
|
||||||
|
|
||||||
|
// Array operations
|
||||||
|
const adminUsers = await User.query().where('roles', 'includes', 'admin').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereIn(field, values)
|
||||||
|
|
||||||
|
Filters records where field value is in the provided array.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name
|
||||||
|
- `values`: Array of values to match
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find users with specific IDs
|
||||||
|
const users = await User.query().whereIn('id', ['user1', 'user2', 'user3']).find();
|
||||||
|
|
||||||
|
// Find posts with specific tags
|
||||||
|
const posts = await Post.query().whereIn('category', ['tech', 'programming', 'tutorial']).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereNotIn(field, values)
|
||||||
|
|
||||||
|
Filters records where field value is NOT in the provided array.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name
|
||||||
|
- `values`: Array of values to exclude
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Exclude specific users
|
||||||
|
const users = await User.query().whereNotIn('status', ['banned', 'suspended']).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereNull(field) / whereNotNull(field)
|
||||||
|
|
||||||
|
Filters records based on null values.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name to check
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find users without profile pictures
|
||||||
|
const usersWithoutAvatars = await User.query().whereNull('avatarUrl').find();
|
||||||
|
|
||||||
|
// Find users with profile pictures
|
||||||
|
const usersWithAvatars = await User.query().whereNotNull('avatarUrl').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereBetween(field, min, max)
|
||||||
|
|
||||||
|
Filters records where field value is between min and max.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name
|
||||||
|
- `min`: Minimum value (inclusive)
|
||||||
|
- `max`: Maximum value (inclusive)
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find users with scores between 100 and 500
|
||||||
|
const moderateUsers = await User.query().whereBetween('score', 100, 500).find();
|
||||||
|
|
||||||
|
// Find posts from last week
|
||||||
|
const lastWeek = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
||||||
|
const recentPosts = await Post.query().whereBetween('createdAt', lastWeek, Date.now()).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereRaw(condition, parameters?)
|
||||||
|
|
||||||
|
Adds a raw WHERE condition.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `condition`: Raw SQL-like condition string
|
||||||
|
- `parameters`: Optional parameters for the condition
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Complex condition
|
||||||
|
const users = await User.query()
|
||||||
|
.whereRaw('score > ? AND (registeredAt > ? OR isPremium = ?)', [100, lastWeek, true])
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logical Operators
|
||||||
|
|
||||||
|
### and() / or() / not()
|
||||||
|
|
||||||
|
Combines conditions with logical operators.
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// AND (default behavior)
|
||||||
|
const premiumActiveUsers = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.and()
|
||||||
|
.where('isPremium', true)
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// OR condition
|
||||||
|
const eligibleUsers = await User.query()
|
||||||
|
.where('isPremium', true)
|
||||||
|
.or()
|
||||||
|
.where('score', '>', 1000)
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// NOT condition
|
||||||
|
const nonAdminUsers = await User.query().not().where('role', 'admin').find();
|
||||||
|
|
||||||
|
// Complex combinations
|
||||||
|
const complexQuery = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.and()
|
||||||
|
.group((query) => query.where('isPremium', true).or().where('score', '>', 500))
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sorting Methods
|
||||||
|
|
||||||
|
### orderBy(field, direction?)
|
||||||
|
|
||||||
|
Orders results by specified field.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name to sort by
|
||||||
|
- `direction`: Sort direction ('asc' or 'desc', defaults to 'asc')
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Sort by score descending
|
||||||
|
const topUsers = await User.query().orderBy('score', 'desc').limit(10).find();
|
||||||
|
|
||||||
|
// Multiple sort criteria
|
||||||
|
const users = await User.query().orderBy('score', 'desc').orderBy('username', 'asc').find();
|
||||||
|
|
||||||
|
// Sort by date
|
||||||
|
const recentPosts = await Post.query().orderBy('createdAt', 'desc').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### orderByRaw(orderClause)
|
||||||
|
|
||||||
|
Orders results using a raw ORDER BY clause.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `orderClause`: Raw order clause
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const users = await User.query().orderByRaw('score DESC, RANDOM()').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limiting and Pagination
|
||||||
|
|
||||||
|
### limit(count)
|
||||||
|
|
||||||
|
Limits the number of results.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `count`: Maximum number of records to return
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get top 10 users
|
||||||
|
const topUsers = await User.query().orderBy('score', 'desc').limit(10).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### offset(count)
|
||||||
|
|
||||||
|
Skips a number of records.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `count`: Number of records to skip
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get users 11-20 (skip first 10)
|
||||||
|
const nextBatch = await User.query().orderBy('score', 'desc').offset(10).limit(10).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### paginate(page, perPage)
|
||||||
|
|
||||||
|
Paginates results and returns pagination info.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `page`: Page number (1-based)
|
||||||
|
- `perPage`: Number of records per page
|
||||||
|
|
||||||
|
**Returns:** `Promise<PaginatedResult<T>>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get page 2 with 20 users per page
|
||||||
|
const result = await User.query().where('isActive', true).orderBy('score', 'desc').paginate(2, 20);
|
||||||
|
|
||||||
|
console.log('Users:', result.data);
|
||||||
|
console.log('Total:', result.total);
|
||||||
|
console.log('Page:', result.page);
|
||||||
|
console.log('Per page:', result.perPage);
|
||||||
|
console.log('Total pages:', result.totalPages);
|
||||||
|
console.log('Has next:', result.hasNext);
|
||||||
|
console.log('Has previous:', result.hasPrev);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Relationship Methods
|
||||||
|
|
||||||
|
### with(relationships)
|
||||||
|
|
||||||
|
Eager loads relationships.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `relationships`: Array of relationship names or dot-notation for nested relationships
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Load single relationship
|
||||||
|
const usersWithPosts = await User.query().with(['posts']).find();
|
||||||
|
|
||||||
|
// Load multiple relationships
|
||||||
|
const usersWithData = await User.query().with(['posts', 'profile', 'followers']).find();
|
||||||
|
|
||||||
|
// Load nested relationships
|
||||||
|
const usersWithCommentsAuthors = await User.query().with(['posts.comments.author']).find();
|
||||||
|
|
||||||
|
// Mixed relationships
|
||||||
|
const complexLoad = await User.query()
|
||||||
|
.with(['posts.comments.author', 'profile', 'followers.profile'])
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### withCount(relationships)
|
||||||
|
|
||||||
|
Loads relationship counts without loading the actual relationships.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `relationships`: Array of relationship names
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const usersWithCounts = await User.query().withCount(['posts', 'followers', 'following']).find();
|
||||||
|
|
||||||
|
// Access counts
|
||||||
|
usersWithCounts.forEach((user) => {
|
||||||
|
console.log(`${user.username}: ${user.postsCount} posts, ${user.followersCount} followers`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereHas(relationship, callback?)
|
||||||
|
|
||||||
|
Filters records that have related records matching criteria.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `relationship`: Relationship name
|
||||||
|
- `callback`: Optional callback to add conditions to the relationship query
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Users who have posts
|
||||||
|
const usersWithPosts = await User.query().whereHas('posts').find();
|
||||||
|
|
||||||
|
// Users who have published posts
|
||||||
|
const usersWithPublishedPosts = await User.query()
|
||||||
|
.whereHas('posts', (query) => {
|
||||||
|
query.where('isPublished', true);
|
||||||
|
})
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// Users who have recent posts
|
||||||
|
const usersWithRecentPosts = await User.query()
|
||||||
|
.whereHas('posts', (query) => {
|
||||||
|
query.where('createdAt', '>', Date.now() - 7 * 24 * 60 * 60 * 1000);
|
||||||
|
})
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### whereDoesntHave(relationship)
|
||||||
|
|
||||||
|
Filters records that don't have related records.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `relationship`: Relationship name
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Users without posts
|
||||||
|
const usersWithoutPosts = await User.query().whereDoesntHave('posts').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Aggregation Methods
|
||||||
|
|
||||||
|
### count()
|
||||||
|
|
||||||
|
Returns the count of matching records.
|
||||||
|
|
||||||
|
**Returns:** `Promise<number>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Count all active users
|
||||||
|
const activeUserCount = await User.query().where('isActive', true).count();
|
||||||
|
|
||||||
|
console.log(`Active users: ${activeUserCount}`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### sum(field) / avg(field) / min(field) / max(field)
|
||||||
|
|
||||||
|
Performs aggregation operations on a field.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name to aggregate
|
||||||
|
|
||||||
|
**Returns:** `Promise<number>` or `Promise<any>` for min/max
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Calculate statistics
|
||||||
|
const totalScore = await User.query().sum('score');
|
||||||
|
const averageScore = await User.query().avg('score');
|
||||||
|
const highestScore = await User.query().max('score');
|
||||||
|
const lowestScore = await User.query().min('score');
|
||||||
|
|
||||||
|
console.log(`Total: ${totalScore}, Average: ${averageScore}`);
|
||||||
|
console.log(`Range: ${lowestScore} - ${highestScore}`);
|
||||||
|
|
||||||
|
// With conditions
|
||||||
|
const premiumAverage = await User.query().where('isPremium', true).avg('score');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caching Methods
|
||||||
|
|
||||||
|
### cache(ttl?)
|
||||||
|
|
||||||
|
Caches query results.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `ttl`: Time to live in seconds (optional, uses default if not provided)
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Cache for default duration
|
||||||
|
const cachedUsers = await User.query().where('isActive', true).cache().find();
|
||||||
|
|
||||||
|
// Cache for 10 minutes
|
||||||
|
const cachedPosts = await Post.query().where('isPublished', true).cache(600).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### fresh()
|
||||||
|
|
||||||
|
Forces a fresh query, bypassing cache.
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Always fetch fresh data
|
||||||
|
const freshUsers = await User.query().where('isActive', true).fresh().find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Execution Methods
|
||||||
|
|
||||||
|
### find()
|
||||||
|
|
||||||
|
Executes the query and returns all matching records.
|
||||||
|
|
||||||
|
**Returns:** `Promise<T[]>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const users = await User.query().where('isActive', true).orderBy('score', 'desc').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### findOne() / first()
|
||||||
|
|
||||||
|
Executes the query and returns the first matching record.
|
||||||
|
|
||||||
|
**Returns:** `Promise<T | null>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find the highest scoring user
|
||||||
|
const topUser = await User.query().orderBy('score', 'desc').findOne();
|
||||||
|
|
||||||
|
// Find specific user
|
||||||
|
const user = await User.query().where('username', 'alice').first();
|
||||||
|
```
|
||||||
|
|
||||||
|
### get()
|
||||||
|
|
||||||
|
Alias for `find()`.
|
||||||
|
|
||||||
|
**Returns:** `Promise<T[]>`
|
||||||
|
|
||||||
|
## Advanced Methods
|
||||||
|
|
||||||
|
### distinct(field?)
|
||||||
|
|
||||||
|
Returns distinct values.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Optional field to get distinct values for
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get users with distinct emails
|
||||||
|
const uniqueUsers = await User.query().distinct('email').find();
|
||||||
|
|
||||||
|
// Get all distinct tags from posts
|
||||||
|
const uniqueTags = await Post.query().distinct('tags').find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### groupBy(field)
|
||||||
|
|
||||||
|
Groups results by field.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field to group by
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Group users by registration date
|
||||||
|
const usersByDate = await User.query().groupBy('registeredAt').withCount(['posts']).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
### having(field, operator, value)
|
||||||
|
|
||||||
|
Adds HAVING condition (used with groupBy).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `field`: Field name
|
||||||
|
- `operator`: Comparison operator
|
||||||
|
- `value`: Value to compare
|
||||||
|
|
||||||
|
**Returns:** `QueryBuilder<T>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Find dates with more than 10 user registrations
|
||||||
|
const busyDays = await User.query().groupBy('registeredAt').having('COUNT(*)', '>', 10).find();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query Inspection
|
||||||
|
|
||||||
|
### toSQL()
|
||||||
|
|
||||||
|
Returns the SQL representation of the query.
|
||||||
|
|
||||||
|
**Returns:** `string`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const query = User.query().where('isActive', true).orderBy('score', 'desc').limit(10);
|
||||||
|
|
||||||
|
console.log('SQL:', query.toSQL());
|
||||||
|
```
|
||||||
|
|
||||||
|
### getParameters()
|
||||||
|
|
||||||
|
Returns the parameters for the query.
|
||||||
|
|
||||||
|
**Returns:** `any[]`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const query = User.query().where('score', '>', 100);
|
||||||
|
|
||||||
|
console.log('Parameters:', query.getParameters());
|
||||||
|
```
|
||||||
|
|
||||||
|
### explain()
|
||||||
|
|
||||||
|
Returns the query execution plan.
|
||||||
|
|
||||||
|
**Returns:** `Promise<QueryPlan>`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const plan = await User.query().where('isActive', true).explain();
|
||||||
|
|
||||||
|
console.log('Query plan:', plan);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Definitions
|
||||||
|
|
||||||
|
### QueryOperator
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type QueryOperator =
|
||||||
|
| 'eq'
|
||||||
|
| '='
|
||||||
|
| 'ne'
|
||||||
|
| '!='
|
||||||
|
| '<>'
|
||||||
|
| 'gt'
|
||||||
|
| '>'
|
||||||
|
| 'gte'
|
||||||
|
| '>='
|
||||||
|
| 'lt'
|
||||||
|
| '<'
|
||||||
|
| 'lte'
|
||||||
|
| '<='
|
||||||
|
| 'in'
|
||||||
|
| 'not in'
|
||||||
|
| 'like'
|
||||||
|
| 'not like'
|
||||||
|
| 'regex'
|
||||||
|
| 'is null'
|
||||||
|
| 'is not null'
|
||||||
|
| 'includes'
|
||||||
|
| 'includes any'
|
||||||
|
| 'includes all';
|
||||||
|
```
|
||||||
|
|
||||||
|
### PaginatedResult
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface PaginatedResult<T> {
|
||||||
|
data: T[];
|
||||||
|
total: number;
|
||||||
|
page: number;
|
||||||
|
perPage: number;
|
||||||
|
totalPages: number;
|
||||||
|
hasNext: boolean;
|
||||||
|
hasPrev: boolean;
|
||||||
|
firstPage: number;
|
||||||
|
lastPage: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### QueryPlan
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface QueryPlan {
|
||||||
|
estimatedCost: number;
|
||||||
|
estimatedRows: number;
|
||||||
|
indexesUsed: string[];
|
||||||
|
operations: QueryOperation[];
|
||||||
|
warnings: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueryOperation {
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
cost: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complex Query Examples
|
||||||
|
|
||||||
|
### Advanced Filtering
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Complex user search
|
||||||
|
const searchResults = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.and()
|
||||||
|
.group((query) =>
|
||||||
|
query
|
||||||
|
.where('username', 'like', `%${searchTerm}%`)
|
||||||
|
.or()
|
||||||
|
.where('email', 'like', `%${searchTerm}%`)
|
||||||
|
.or()
|
||||||
|
.where('bio', 'like', `%${searchTerm}%`),
|
||||||
|
)
|
||||||
|
.and()
|
||||||
|
.group((query) => query.where('isPremium', true).or().where('score', '>', 500))
|
||||||
|
.orderBy('score', 'desc')
|
||||||
|
.with(['profile', 'posts.comments'])
|
||||||
|
.paginate(page, 20);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Analytics Query
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// User activity analytics
|
||||||
|
const analytics = await User.query()
|
||||||
|
.where('registeredAt', '>', startDate)
|
||||||
|
.where('registeredAt', '<', endDate)
|
||||||
|
.whereHas('posts', (query) => {
|
||||||
|
query.where('isPublished', true);
|
||||||
|
})
|
||||||
|
.withCount(['posts', 'comments', 'followers'])
|
||||||
|
.orderBy('score', 'desc')
|
||||||
|
.cache(300) // Cache for 5 minutes
|
||||||
|
.find();
|
||||||
|
|
||||||
|
// Process analytics data
|
||||||
|
const stats = {
|
||||||
|
totalUsers: analytics.length,
|
||||||
|
averageScore: analytics.reduce((sum, user) => sum + user.score, 0) / analytics.length,
|
||||||
|
totalPosts: analytics.reduce((sum, user) => sum + user.postsCount, 0),
|
||||||
|
totalComments: analytics.reduce((sum, user) => sum + user.commentsCount, 0),
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dashboard Query
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function getDashboardData(userId: string) {
|
||||||
|
// Multiple optimized queries
|
||||||
|
const [userStats, recentPosts, topComments, followingActivity] = await Promise.all([
|
||||||
|
// User statistics
|
||||||
|
User.query()
|
||||||
|
.where('id', userId)
|
||||||
|
.withCount(['posts', 'comments', 'followers', 'following'])
|
||||||
|
.cache(60) // Cache for 1 minute
|
||||||
|
.first(),
|
||||||
|
|
||||||
|
// Recent posts
|
||||||
|
Post.query()
|
||||||
|
.where('authorId', userId)
|
||||||
|
.orderBy('createdAt', 'desc')
|
||||||
|
.limit(5)
|
||||||
|
.with(['comments.author'])
|
||||||
|
.find(),
|
||||||
|
|
||||||
|
// Top comments by user
|
||||||
|
Comment.query()
|
||||||
|
.where('authorId', userId)
|
||||||
|
.orderBy('likeCount', 'desc')
|
||||||
|
.limit(10)
|
||||||
|
.with(['post.author'])
|
||||||
|
.find(),
|
||||||
|
|
||||||
|
// Activity from people user follows
|
||||||
|
Post.query()
|
||||||
|
.whereHas('author', (query) => {
|
||||||
|
query.whereHas('followers', (followQuery) => {
|
||||||
|
followQuery.where('followerId', userId);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.orderBy('createdAt', 'desc')
|
||||||
|
.limit(20)
|
||||||
|
.with(['author', 'comments'])
|
||||||
|
.find(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
userStats,
|
||||||
|
recentPosts,
|
||||||
|
topComments,
|
||||||
|
followingActivity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The QueryBuilder provides a powerful, type-safe way to construct complex database queries with support for relationships, caching, pagination, and advanced filtering options.
|
691
docs/docs/videos/index.md
Normal file
691
docs/docs/videos/index.md
Normal file
@ -0,0 +1,691 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# Video Tutorials
|
||||||
|
|
||||||
|
Learn Debros Network through comprehensive video tutorials. These step-by-step guides will help you master the framework from basic concepts to advanced implementations.
|
||||||
|
|
||||||
|
## 🎬 **Getting Started Series**
|
||||||
|
|
||||||
|
### 1. Introduction to Debros Network
|
||||||
|
|
||||||
|
**Duration: 15 minutes**
|
||||||
|
|
||||||
|
A complete overview of Debros Network, its architecture, and core concepts. Perfect for developers new to decentralized applications.
|
||||||
|
|
||||||
|
**What You'll Learn:**
|
||||||
|
|
||||||
|
- Framework architecture and components
|
||||||
|
- Key differences from traditional frameworks
|
||||||
|
- When to use Debros Network
|
||||||
|
- Development environment overview
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Code examples from this video
|
||||||
|
import { DebrosFramework, BaseModel, Model, Field } from '@debros/network';
|
||||||
|
|
||||||
|
@Model({
|
||||||
|
scope: 'global',
|
||||||
|
type: 'docstore',
|
||||||
|
})
|
||||||
|
class User extends BaseModel {
|
||||||
|
@Field({ type: 'string', required: true })
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Introduction Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Setting Up Your Development Environment
|
||||||
|
|
||||||
|
**Duration: 20 minutes**
|
||||||
|
|
||||||
|
Step-by-step guide to setting up Debros Network in your development environment.
|
||||||
|
|
||||||
|
**What You'll Learn:**
|
||||||
|
|
||||||
|
- Installing dependencies
|
||||||
|
- Project structure setup
|
||||||
|
- IDE configuration
|
||||||
|
- Development tools
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
|
||||||
|
- Node.js 18+
|
||||||
|
- npm or pnpm
|
||||||
|
- TypeScript knowledge
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Commands from this video
|
||||||
|
npm create debros-app my-app
|
||||||
|
cd my-app
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Setup Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Your First Debros Application
|
||||||
|
|
||||||
|
**Duration: 25 minutes**
|
||||||
|
|
||||||
|
Build your first decentralized application with user management and basic CRUD operations.
|
||||||
|
|
||||||
|
**What You'll Learn:**
|
||||||
|
|
||||||
|
- Creating models with decorators
|
||||||
|
- Database initialization
|
||||||
|
- Basic CRUD operations
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
**Project Files:**
|
||||||
|
|
||||||
|
- `models/User.ts`
|
||||||
|
- `models/Post.ts`
|
||||||
|
- `app.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Final code from this tutorial
|
||||||
|
@Model({
|
||||||
|
scope: 'global',
|
||||||
|
type: 'docstore',
|
||||||
|
sharding: { strategy: 'hash', count: 4, key: 'id' },
|
||||||
|
})
|
||||||
|
export class User extends BaseModel {
|
||||||
|
@Field({ type: 'string', required: true, unique: true })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Field({ type: 'string', required: true, unique: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@HasMany(() => Post, 'authorId')
|
||||||
|
posts: Post[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch First App Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ **Core Concepts Series**
|
||||||
|
|
||||||
|
### 4. Understanding Models and Decorators
|
||||||
|
|
||||||
|
**Duration: 30 minutes**
|
||||||
|
|
||||||
|
Deep dive into the model system, decorators, and data validation.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Model configuration options
|
||||||
|
- Field types and validation
|
||||||
|
- Custom validators
|
||||||
|
- Lifecycle hooks
|
||||||
|
- Best practices
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Advanced model example from video
|
||||||
|
@Model({
|
||||||
|
scope: 'user',
|
||||||
|
type: 'docstore',
|
||||||
|
sharding: { strategy: 'user', count: 2, key: 'userId' },
|
||||||
|
})
|
||||||
|
export class BlogPost extends BaseModel {
|
||||||
|
@Field({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
minLength: 5,
|
||||||
|
maxLength: 200,
|
||||||
|
validate: (title: string) => !title.includes('spam'),
|
||||||
|
})
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@BeforeCreate()
|
||||||
|
async validatePost() {
|
||||||
|
// Custom validation logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Models Deep Dive**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Mastering the Query System
|
||||||
|
|
||||||
|
**Duration: 35 minutes**
|
||||||
|
|
||||||
|
Complete guide to building complex queries with relationships and optimization.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Query builder API
|
||||||
|
- Filtering and sorting
|
||||||
|
- Relationship loading
|
||||||
|
- Pagination
|
||||||
|
- Query optimization
|
||||||
|
- Caching strategies
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Complex query example from video
|
||||||
|
const results = await User.query()
|
||||||
|
.where('isActive', true)
|
||||||
|
.where('score', '>', 100)
|
||||||
|
.whereHas('posts', (query) => {
|
||||||
|
query.where('isPublished', true).where('createdAt', '>', Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||||
|
})
|
||||||
|
.with(['posts.comments.author', 'profile'])
|
||||||
|
.orderBy('score', 'desc')
|
||||||
|
.cache(300)
|
||||||
|
.paginate(1, 20);
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Query Mastery**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Working with Relationships
|
||||||
|
|
||||||
|
**Duration: 25 minutes**
|
||||||
|
|
||||||
|
Understanding and implementing relationships between models.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Relationship types (HasMany, BelongsTo, etc.)
|
||||||
|
- Eager vs lazy loading
|
||||||
|
- Nested relationships
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Relationship examples from video
|
||||||
|
@Model({ scope: 'global', type: 'docstore' })
|
||||||
|
export class User extends BaseModel {
|
||||||
|
@HasMany(() => Post, 'authorId')
|
||||||
|
posts: Post[];
|
||||||
|
|
||||||
|
@ManyToMany(() => User, 'followers', 'following')
|
||||||
|
followers: User[];
|
||||||
|
|
||||||
|
@HasOne(() => UserProfile, 'userId')
|
||||||
|
profile: UserProfile;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Relationships Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 **Advanced Features Series**
|
||||||
|
|
||||||
|
### 7. Database Sharding and Scaling
|
||||||
|
|
||||||
|
**Duration: 40 minutes**
|
||||||
|
|
||||||
|
Learn how to scale your application with automatic sharding strategies.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Sharding strategies
|
||||||
|
- User-scoped vs global databases
|
||||||
|
- Performance optimization
|
||||||
|
- Monitoring and metrics
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Sharding configuration examples
|
||||||
|
@Model({
|
||||||
|
scope: 'user',
|
||||||
|
type: 'docstore',
|
||||||
|
sharding: {
|
||||||
|
strategy: 'hash',
|
||||||
|
count: 8,
|
||||||
|
key: 'userId',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class UserData extends BaseModel {
|
||||||
|
// Model implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Sharding Guide**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Migrations and Schema Evolution
|
||||||
|
|
||||||
|
**Duration: 30 minutes**
|
||||||
|
|
||||||
|
Managing database schema changes and data migrations.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Creating migrations
|
||||||
|
- Data transformations
|
||||||
|
- Rollback strategies
|
||||||
|
- Production deployment
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Migration example from video
|
||||||
|
const migration = createMigration('add_user_profiles', '1.1.0')
|
||||||
|
.addField('User', 'profilePicture', {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.addField('User', 'bio', {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.transformData('User', (user) => ({
|
||||||
|
...user,
|
||||||
|
displayName: user.username || 'Anonymous',
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Migrations Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Real-time Features and PubSub
|
||||||
|
|
||||||
|
**Duration: 25 minutes**
|
||||||
|
|
||||||
|
Implementing real-time functionality with the built-in PubSub system.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Event publishing
|
||||||
|
- Real-time subscriptions
|
||||||
|
- WebSocket integration
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Real-time examples from video
|
||||||
|
@Model({ scope: 'global', type: 'docstore' })
|
||||||
|
export class ChatMessage extends BaseModel {
|
||||||
|
@AfterCreate()
|
||||||
|
async publishMessage() {
|
||||||
|
await this.publish('message:created', {
|
||||||
|
roomId: this.roomId,
|
||||||
|
message: this,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Real-time Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ **Project Tutorials**
|
||||||
|
|
||||||
|
### 10. Building a Complete Blog Application
|
||||||
|
|
||||||
|
**Duration: 60 minutes**
|
||||||
|
|
||||||
|
Build a full-featured blog application with authentication, posts, and comments.
|
||||||
|
|
||||||
|
**Features Built:**
|
||||||
|
|
||||||
|
- User authentication
|
||||||
|
- Post creation and editing
|
||||||
|
- Comment system
|
||||||
|
- User profiles
|
||||||
|
- Admin dashboard
|
||||||
|
|
||||||
|
**Final Project Structure:**
|
||||||
|
|
||||||
|
```
|
||||||
|
blog-app/
|
||||||
|
├── models/
|
||||||
|
│ ├── User.ts
|
||||||
|
│ ├── Post.ts
|
||||||
|
│ ├── Comment.ts
|
||||||
|
│ └── Category.ts
|
||||||
|
├── services/
|
||||||
|
│ ├── AuthService.ts
|
||||||
|
│ └── BlogService.ts
|
||||||
|
└── app.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Blog Tutorial**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. Creating a Social Media Platform
|
||||||
|
|
||||||
|
**Duration: 90 minutes**
|
||||||
|
|
||||||
|
Build a decentralized social media platform with advanced features.
|
||||||
|
|
||||||
|
**Features Built:**
|
||||||
|
|
||||||
|
- User profiles and following
|
||||||
|
- Feed generation
|
||||||
|
- Real-time messaging
|
||||||
|
- Content moderation
|
||||||
|
- Analytics dashboard
|
||||||
|
|
||||||
|
**Part 1: User System and Profiles** (30 min)
|
||||||
|
[**▶️ Watch Part 1**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
**Part 2: Posts and Feed** (30 min)
|
||||||
|
[**▶️ Watch Part 2**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
**Part 3: Real-time Features** (30 min)
|
||||||
|
[**▶️ Watch Part 3**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 12. E-commerce Platform with Debros Network
|
||||||
|
|
||||||
|
**Duration: 75 minutes**
|
||||||
|
|
||||||
|
Build a decentralized e-commerce platform with product management and orders.
|
||||||
|
|
||||||
|
**Features Built:**
|
||||||
|
|
||||||
|
- Product catalog
|
||||||
|
- Shopping cart
|
||||||
|
- Order processing
|
||||||
|
- Inventory management
|
||||||
|
- Payment integration
|
||||||
|
|
||||||
|
**Part 1: Product Management** (25 min)
|
||||||
|
[**▶️ Watch Part 1**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
**Part 2: Shopping and Orders** (25 min)
|
||||||
|
[**▶️ Watch Part 2**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
**Part 3: Advanced Features** (25 min)
|
||||||
|
[**▶️ Watch Part 3**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 **Development Workflow Series**
|
||||||
|
|
||||||
|
### 13. Testing Strategies for Debros Applications
|
||||||
|
|
||||||
|
**Duration: 35 minutes**
|
||||||
|
|
||||||
|
Comprehensive testing approaches for decentralized applications.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Unit testing models
|
||||||
|
- Integration testing
|
||||||
|
- Mocking strategies
|
||||||
|
- Performance testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Testing example from video
|
||||||
|
describe('User Model', () => {
|
||||||
|
it('should create user with valid data', async () => {
|
||||||
|
const user = await User.create({
|
||||||
|
username: 'testuser',
|
||||||
|
email: 'test@example.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.id).toBeDefined();
|
||||||
|
expect(user.username).toBe('testuser');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Testing Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 14. Deployment and Production Best Practices
|
||||||
|
|
||||||
|
**Duration: 45 minutes**
|
||||||
|
|
||||||
|
Deploy your Debros Network applications to production environments.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Production configuration
|
||||||
|
- Docker containers
|
||||||
|
- Monitoring and logging
|
||||||
|
- Performance optimization
|
||||||
|
- Security considerations
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Docker example from video
|
||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --only=production
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Deployment Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 15. Performance Optimization Techniques
|
||||||
|
|
||||||
|
**Duration: 40 minutes**
|
||||||
|
|
||||||
|
Advanced techniques for optimizing Debros Network applications.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Query optimization
|
||||||
|
- Caching strategies
|
||||||
|
- Database indexing
|
||||||
|
- Memory management
|
||||||
|
- Profiling tools
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Optimization examples from video
|
||||||
|
// Efficient query with proper indexing
|
||||||
|
const optimizedQuery = await User.query()
|
||||||
|
.where('isActive', true) // Indexed field
|
||||||
|
.with(['posts']) // Eager load to avoid N+1
|
||||||
|
.cache(300) // Cache frequently accessed data
|
||||||
|
.limit(50) // Reasonable limits
|
||||||
|
.find();
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Optimization Video**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📱 **Integration Series**
|
||||||
|
|
||||||
|
### 16. Frontend Integration with React
|
||||||
|
|
||||||
|
**Duration: 50 minutes**
|
||||||
|
|
||||||
|
Integrate Debros Network with React applications for full-stack development.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- React hooks for Debros
|
||||||
|
- State management
|
||||||
|
- Real-time updates
|
||||||
|
- Error boundaries
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// React integration example from video
|
||||||
|
import { useDebrosQuery, useDebrosModel } from '@debros/react';
|
||||||
|
|
||||||
|
function UserProfile({ userId }: { userId: string }) {
|
||||||
|
const { user, loading, error } = useDebrosModel(User, userId, {
|
||||||
|
with: ['posts', 'profile']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <div>Loading...</div>;
|
||||||
|
if (error) return <div>Error: {error.message}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{user.username}</h1>
|
||||||
|
<p>Posts: {user.posts.length}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch React Integration**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 17. Building APIs with Express and Debros
|
||||||
|
|
||||||
|
**Duration: 35 minutes**
|
||||||
|
|
||||||
|
Create REST APIs using Express.js with Debros Network as the backend.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Express middleware
|
||||||
|
- API route design
|
||||||
|
- Authentication
|
||||||
|
- Error handling
|
||||||
|
- API documentation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Express API example from video
|
||||||
|
app.get('/api/users/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findById(req.params.id, {
|
||||||
|
with: ['posts'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'User not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(user);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[**▶️ Watch Express Integration**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 18. Mobile Development with React Native
|
||||||
|
|
||||||
|
**Duration: 45 minutes**
|
||||||
|
|
||||||
|
Build mobile applications using React Native and Debros Network.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- React Native setup
|
||||||
|
- Offline synchronization
|
||||||
|
- Mobile-specific optimizations
|
||||||
|
- Push notifications
|
||||||
|
|
||||||
|
[**▶️ Watch Mobile Development**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 **Masterclass Series**
|
||||||
|
|
||||||
|
### 19. Architecture Patterns and Best Practices
|
||||||
|
|
||||||
|
**Duration: 60 minutes**
|
||||||
|
|
||||||
|
Advanced architectural patterns for large-scale Debros Network applications.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Domain-driven design
|
||||||
|
- CQRS patterns
|
||||||
|
- Event sourcing
|
||||||
|
- Microservices architecture
|
||||||
|
|
||||||
|
[**▶️ Watch Architecture Masterclass**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 20. Contributing to Debros Network
|
||||||
|
|
||||||
|
**Duration: 30 minutes**
|
||||||
|
|
||||||
|
Learn how to contribute to the Debros Network open-source project.
|
||||||
|
|
||||||
|
**Topics Covered:**
|
||||||
|
|
||||||
|
- Development setup
|
||||||
|
- Code standards
|
||||||
|
- Testing requirements
|
||||||
|
- Pull request process
|
||||||
|
|
||||||
|
[**▶️ Watch Contributing Guide**](https://youtube.com/watch?v=VIDEO_ID_HERE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 **Additional Resources**
|
||||||
|
|
||||||
|
### Video Playlists
|
||||||
|
|
||||||
|
**🎬 [Complete Beginner Series](https://youtube.com/playlist?list=PLAYLIST_ID)**
|
||||||
|
Videos 1-6: Everything you need to get started
|
||||||
|
|
||||||
|
**🏗️ [Advanced Development](https://youtube.com/playlist?list=PLAYLIST_ID)**
|
||||||
|
Videos 7-12: Advanced features and patterns
|
||||||
|
|
||||||
|
**🛠️ [Project Tutorials](https://youtube.com/playlist?list=PLAYLIST_ID)**
|
||||||
|
Videos 10-12: Complete project walkthroughs
|
||||||
|
|
||||||
|
**🔧 [Production Ready](https://youtube.com/playlist?list=PLAYLIST_ID)**
|
||||||
|
Videos 13-15: Testing, deployment, and optimization
|
||||||
|
|
||||||
|
### Community Videos
|
||||||
|
|
||||||
|
**Community Showcase**
|
||||||
|
|
||||||
|
- [Building a Decentralized Chat App](https://youtube.com/watch?v=COMMUNITY_VIDEO_1)
|
||||||
|
- [E-learning Platform with Debros](https://youtube.com/watch?v=COMMUNITY_VIDEO_2)
|
||||||
|
- [IoT Data Management](https://youtube.com/watch?v=COMMUNITY_VIDEO_3)
|
||||||
|
|
||||||
|
### Interactive Learning
|
||||||
|
|
||||||
|
**🎮 [Interactive Tutorials](https://learn.debros.io)**
|
||||||
|
Hands-on coding exercises with instant feedback
|
||||||
|
|
||||||
|
**💬 [Discord Community](https://discord.gg/debros)**
|
||||||
|
Get help and discuss videos with other developers
|
||||||
|
|
||||||
|
**📖 [Workshop Materials](https://github.com/debros/workshops)**
|
||||||
|
Download code samples and workshop materials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📅 **Release Schedule**
|
||||||
|
|
||||||
|
New video tutorials are released every **Tuesday and Friday**:
|
||||||
|
|
||||||
|
- **Tuesdays**: Core concepts and feature deep-dives
|
||||||
|
- **Fridays**: Project tutorials and real-world applications
|
||||||
|
|
||||||
|
**Subscribe to our [YouTube channel](https://youtube.com/@debrosnetwork)** and enable notifications to stay updated with the latest tutorials.
|
||||||
|
|
||||||
|
## 🤝 **Request a Tutorial**
|
||||||
|
|
||||||
|
Have a specific topic you'd like covered? Request a tutorial:
|
||||||
|
|
||||||
|
- **[GitHub Discussions](https://github.com/debros/network/discussions)** - Community requests
|
||||||
|
- **[Email Us](mailto:tutorials@debros.io)** - Direct requests
|
||||||
|
- **[Discord](https://discord.gg/debros)** - Community suggestions
|
||||||
|
|
||||||
|
We prioritize tutorials based on community demand and framework updates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_All videos include closed captions, downloadable code samples, and companion blog posts for reference._
|
@ -40,6 +40,13 @@ const sidebars: SidebarsConfig = {
|
|||||||
'examples/basic-usage',
|
'examples/basic-usage',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Video Tutorials',
|
||||||
|
items: [
|
||||||
|
'videos/index',
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'Contributing',
|
label: 'Contributing',
|
||||||
@ -56,9 +63,18 @@ const sidebars: SidebarsConfig = {
|
|||||||
'api/overview',
|
'api/overview',
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'Framework Classes',
|
label: 'Core Classes',
|
||||||
items: [
|
items: [
|
||||||
'api/debros-framework',
|
'api/debros-framework',
|
||||||
|
'api/base-model',
|
||||||
|
'api/query-builder',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Network API',
|
||||||
|
items: [
|
||||||
|
'api/network-api',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user