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,8 +2,14 @@
|
||||
"src/**/*.{js,ts}": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"pnpm run test:unit",
|
||||
"npm run build"
|
||||
],
|
||||
"tests/**/*.{js,ts}": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"pnpm run test:unit"
|
||||
],
|
||||
"*.{json,md}": [
|
||||
"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',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Video Tutorials',
|
||||
items: [
|
||||
'videos/index',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Contributing',
|
||||
@ -56,9 +63,18 @@ const sidebars: SidebarsConfig = {
|
||||
'api/overview',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Framework Classes',
|
||||
label: 'Core Classes',
|
||||
items: [
|
||||
'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