This repository has been archived on 2025-08-03. You can view files and clone it, but cannot push or open issues or pull requests.
network-orbit/docs/docs/examples/basic-usage.md
anonpenguin 067e462339 Add SVG illustration and TypeScript configuration file
- Added a new SVG file `undraw_docusaurus_tree.svg` to the static images directory for enhanced visual representation.
- Created a `tsconfig.json` file to improve TypeScript support and editor experience, extending from the Docusaurus base configuration.
2025-06-19 07:02:58 +03:00

22 KiB
Raw Blame History

sidebar_position
sidebar_position
1

Basic Usage Examples

This guide provides practical examples of using DebrosFramework for common development tasks. These examples will help you understand how to implement typical application features using the framework.

Setting Up Your First Application

1. Project Setup

mkdir my-debros-app
cd my-debros-app
npm init -y
npm install debros-framework @orbitdb/core @helia/helia
npm install --save-dev typescript @types/node ts-node

2. Basic Configuration

Create src/config.ts:

export const config = {
  ipfs: {
    // IPFS configuration
    addresses: {
      swarm: ['/ip4/0.0.0.0/tcp/4001'],
      api: '/ip4/127.0.0.1/tcp/5001',
      gateway: '/ip4/127.0.0.1/tcp/8080',
    },
  },
  orbitdb: {
    // OrbitDB configuration
    directory: './orbitdb',
  },
  framework: {
    // Framework configuration
    cacheSize: 1000,
    enableQueryOptimization: true,
    enableAutomaticPinning: true,
  },
};

Simple Blog Application

Let's build a simple blog application to demonstrate basic DebrosFramework usage.

1. Define Models

Create src/models/User.ts:

import { BaseModel, Model, Field, HasMany, BeforeCreate, AfterCreate } from 'debros-framework';
import { Post } from './Post';

@Model({
  scope: 'global',
  type: 'docstore',
  sharding: {
    strategy: 'hash',
    count: 4,
    key: 'id',
  },
})
export class User extends BaseModel {
  @Field({
    type: 'string',
    required: true,
    unique: true,
    minLength: 3,
    maxLength: 20,
    validate: (username: string) => /^[a-zA-Z0-9_]+$/.test(username),
  })
  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: 'string', required: false, maxLength: 500 })
  bio?: string;

  @Field({ type: 'string', required: false })
  avatarUrl?: string;

  @Field({ type: 'boolean', required: false, default: true })
  isActive: boolean;

  @Field({ type: 'number', required: false, default: () => Date.now() })
  registeredAt: number;

  @Field({ type: 'number', required: false, default: () => Date.now() })
  lastLoginAt: number;

  // Relationships
  @HasMany(() => Post, 'authorId')
  posts: Post[];

  @BeforeCreate()
  setupNewUser() {
    this.registeredAt = Date.now();
    this.lastLoginAt = Date.now();
    this.isActive = true;
  }

  @AfterCreate()
  async afterUserCreated() {
    console.log(`New user created: ${this.username}`);
    // Here you could send a welcome email, create default settings, etc.
  }

  // Helper methods
  updateLastLogin() {
    this.lastLoginAt = Date.now();
    return this.save();
  }

  async getPostCount(): Promise<number> {
    return await Post.query().where('authorId', this.id).count();
  }

  async getRecentPosts(limit: number = 5): Promise<Post[]> {
    return await Post.query()
      .where('authorId', this.id)
      .orderBy('createdAt', 'desc')
      .limit(limit)
      .find();
  }
}

Create src/models/Post.ts:

import {
  BaseModel,
  Model,
  Field,
  BelongsTo,
  HasMany,
  BeforeCreate,
  BeforeUpdate,
} from 'debros-framework';
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: [] })
  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, default: 0 })
  likeCount: number;

  @Field({ type: 'number', required: false, default: () => Date.now() })
  createdAt: number;

  @Field({ type: 'number', required: false, default: () => Date.now() })
  updatedAt: 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;
    this.likeCount = 0;
  }

  @BeforeUpdate()
  updateTimestamp() {
    this.updatedAt = Date.now();
  }

  // Helper methods
  async publish(): Promise<void> {
    this.isPublished = true;
    this.publishedAt = Date.now();
    await this.save();
  }

  async unpublish(): Promise<void> {
    this.isPublished = false;
    this.publishedAt = undefined;
    await this.save();
  }

  async incrementViews(): Promise<void> {
    this.viewCount += 1;
    await this.save();
  }

  async like(): Promise<void> {
    this.likeCount += 1;
    await this.save();
  }

  async unlike(): Promise<void> {
    if (this.likeCount > 0) {
      this.likeCount -= 1;
      await this.save();
    }
  }

  addTag(tag: string): void {
    const normalizedTag = tag.toLowerCase().trim();
    if (normalizedTag && !this.tags.includes(normalizedTag)) {
      this.tags.push(normalizedTag);
    }
  }

  removeTag(tag: string): void {
    this.tags = this.tags.filter((t) => t !== tag.toLowerCase().trim());
  }

  getExcerpt(length: number = 150): string {
    if (this.content.length <= length) {
      return this.content;
    }
    return this.content.substring(0, length).trim() + '...';
  }

  getReadingTime(): number {
    const wordsPerMinute = 200;
    const wordCount = this.content.split(/\s+/).length;
    return Math.ceil(wordCount / wordsPerMinute);
  }
}

Create src/models/Comment.ts:

import { BaseModel, Model, Field, BelongsTo, BeforeCreate } from 'debros-framework';
import { User } from './User';
import { Post } from './Post';

@Model({
  scope: 'user',
  type: 'docstore',
  sharding: {
    strategy: 'user',
    count: 2,
    key: 'authorId',
  },
})
export class Comment extends BaseModel {
  @Field({ type: 'string', required: true, minLength: 1, maxLength: 1000 })
  content: string;

  @Field({ type: 'string', required: true })
  postId: string;

  @Field({ type: 'string', required: true })
  authorId: string;

  @Field({ type: 'boolean', required: false, default: true })
  isApproved: boolean;

  @Field({ type: 'number', required: false, default: 0 })
  likeCount: number;

  @Field({ type: 'number', required: false, default: () => Date.now() })
  createdAt: number;

  // Relationships
  @BelongsTo(() => Post, 'postId')
  post: Post;

  @BelongsTo(() => User, 'authorId')
  author: User;

  @BeforeCreate()
  setupNewComment() {
    this.createdAt = Date.now();
    this.isApproved = true; // Auto-approve for now
  }

  // Helper methods
  async approve(): Promise<void> {
    this.isApproved = true;
    await this.save();
  }

  async reject(): Promise<void> {
    this.isApproved = false;
    await this.save();
  }

  async like(): Promise<void> {
    this.likeCount += 1;
    await this.save();
  }

  async unlike(): Promise<void> {
    if (this.likeCount > 0) {
      this.likeCount -= 1;
      await this.save();
    }
  }
}

Create src/models/index.ts:

export { User } from './User';
export { Post } from './Post';
export { Comment } from './Comment';

2. Application Service

Create src/BlogService.ts:

import { DebrosFramework } from 'debros-framework';
import { User, Post, Comment } from './models';

export class BlogService {
  private framework: DebrosFramework;

  constructor(framework: DebrosFramework) {
    this.framework = framework;
  }

  // User operations
  async createUser(userData: {
    username: string;
    email: string;
    bio?: string;
    avatarUrl?: string;
  }): Promise<User> {
    return await User.create(userData);
  }

  async getUserByUsername(username: string): Promise<User | null> {
    return await User.query().where('username', username).findOne();
  }

  async getUserById(id: string): Promise<User | null> {
    return await User.findById(id);
  }

  async updateUserProfile(
    userId: string,
    updates: {
      bio?: string;
      avatarUrl?: string;
    },
  ): Promise<User> {
    const user = await User.findById(userId);
    if (!user) {
      throw new Error('User not found');
    }

    if (updates.bio !== undefined) user.bio = updates.bio;
    if (updates.avatarUrl !== undefined) user.avatarUrl = updates.avatarUrl;

    await user.save();
    return user;
  }

  // Post operations
  async createPost(
    authorId: string,
    postData: {
      title: string;
      content: string;
      tags?: string[];
    },
  ): Promise<Post> {
    const post = await Post.create({
      title: postData.title,
      content: postData.content,
      authorId,
      tags: postData.tags || [],
    });

    return post;
  }

  async getPostById(id: string): Promise<Post | null> {
    const post = await Post.findById(id);
    if (post) {
      await post.incrementViews(); // Track view
    }
    return post;
  }

  async getPostWithDetails(id: string): Promise<Post | null> {
    return await Post.query().where('id', id).with(['author', 'comments.author']).findOne();
  }

  async getPublishedPosts(
    options: {
      page?: number;
      limit?: number;
      tag?: string;
      authorId?: string;
    } = {},
  ): Promise<{ posts: Post[]; total: number }> {
    const { page = 1, limit = 10, tag, authorId } = options;

    let query = Post.query().where('isPublished', true).with(['author']);

    if (tag) {
      query = query.where('tags', 'includes', tag);
    }

    if (authorId) {
      query = query.where('authorId', authorId);
    }

    const result = await query.orderBy('publishedAt', 'desc').paginate(page, limit);

    return {
      posts: result.data,
      total: result.total,
    };
  }

  async getUserPosts(userId: string, includeUnpublished: boolean = false): Promise<Post[]> {
    let query = Post.query().where('authorId', userId);

    if (!includeUnpublished) {
      query = query.where('isPublished', true);
    }

    return await query.orderBy('createdAt', 'desc').find();
  }

  async updatePost(
    postId: string,
    updates: {
      title?: string;
      content?: string;
      tags?: string[];
    },
  ): Promise<Post> {
    const post = await Post.findById(postId);
    if (!post) {
      throw new Error('Post not found');
    }

    if (updates.title !== undefined) post.title = updates.title;
    if (updates.content !== undefined) post.content = updates.content;
    if (updates.tags !== undefined) post.tags = updates.tags;

    await post.save();
    return post;
  }

  async deletePost(postId: string): Promise<void> {
    const post = await Post.findById(postId);
    if (!post) {
      throw new Error('Post not found');
    }

    // Delete associated comments first
    const comments = await Comment.query().where('postId', postId).find();

    for (const comment of comments) {
      await comment.delete();
    }

    await post.delete();
  }

  // Comment operations
  async createComment(authorId: string, postId: string, content: string): Promise<Comment> {
    const comment = await Comment.create({
      content,
      postId,
      authorId,
    });

    return comment;
  }

  async getPostComments(postId: string): Promise<Comment[]> {
    return await Comment.query()
      .where('postId', postId)
      .where('isApproved', true)
      .with(['author'])
      .orderBy('createdAt', 'asc')
      .find();
  }

  async deleteComment(commentId: string): Promise<void> {
    const comment = await Comment.findById(commentId);
    if (!comment) {
      throw new Error('Comment not found');
    }

    await comment.delete();
  }

  // Search and discovery
  async searchPosts(
    query: string,
    options: {
      limit?: number;
      tags?: string[];
    } = {},
  ): Promise<Post[]> {
    const { limit = 20, tags } = options;

    let searchQuery = Post.query()
      .where('isPublished', true)
      .where((q) => {
        q.where('title', 'like', `%${query}%`).orWhere('content', 'like', `%${query}%`);
      });

    if (tags && tags.length > 0) {
      searchQuery = searchQuery.where('tags', 'includes any', tags);
    }

    return await searchQuery
      .with(['author'])
      .orderBy('likeCount', 'desc')
      .orderBy('createdAt', 'desc')
      .limit(limit)
      .find();
  }

  async getPopularPosts(timeframe: 'day' | 'week' | 'month' | 'all' = 'week'): Promise<Post[]> {
    let sinceTime: number;

    switch (timeframe) {
      case 'day':
        sinceTime = Date.now() - 24 * 60 * 60 * 1000;
        break;
      case 'week':
        sinceTime = Date.now() - 7 * 24 * 60 * 60 * 1000;
        break;
      case 'month':
        sinceTime = Date.now() - 30 * 24 * 60 * 60 * 1000;
        break;
      default:
        sinceTime = 0;
    }

    let query = Post.query().where('isPublished', true);

    if (sinceTime > 0) {
      query = query.where('publishedAt', '>', sinceTime);
    }

    return await query
      .with(['author'])
      .orderBy('likeCount', 'desc')
      .orderBy('viewCount', 'desc')
      .limit(10)
      .find();
  }

  async getTags(): Promise<Array<{ tag: string; count: number }>> {
    // Get all published posts
    const posts = await Post.query().where('isPublished', true).select(['tags']).find();

    // Count tags
    const tagCounts = new Map<string, number>();

    posts.forEach((post) => {
      post.tags.forEach((tag) => {
        tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
      });
    });

    // Convert to array and sort by count
    return Array.from(tagCounts.entries())
      .map(([tag, count]) => ({ tag, count }))
      .sort((a, b) => b.count - a.count);
  }

  // Analytics
  async getUserStats(userId: string): Promise<{
    postCount: number;
    totalViews: number;
    totalLikes: number;
    commentCount: number;
  }> {
    const [posts, comments] = await Promise.all([
      Post.query().where('authorId', userId).find(),
      Comment.query().where('authorId', userId).count(),
    ]);

    const totalViews = posts.reduce((sum, post) => sum + post.viewCount, 0);
    const totalLikes = posts.reduce((sum, post) => sum + post.likeCount, 0);

    return {
      postCount: posts.length,
      totalViews,
      totalLikes,
      commentCount: comments,
    };
  }

  async getSystemStats(): Promise<{
    userCount: number;
    postCount: number;
    commentCount: number;
    publishedPostCount: number;
  }> {
    const [userCount, postCount, commentCount, publishedPostCount] = await Promise.all([
      User.query().count(),
      Post.query().count(),
      Comment.query().count(),
      Post.query().where('isPublished', true).count(),
    ]);

    return {
      userCount,
      postCount,
      commentCount,
      publishedPostCount,
    };
  }
}

3. Main Application

Create src/index.ts:

import { DebrosFramework } from 'debros-framework';
import { BlogService } from './BlogService';
import { User, Post, Comment } from './models';
import { setupServices } from './setup';

async function main() {
  try {
    console.log('🚀 Starting DebrosFramework Blog Application...');

    // Initialize services
    const { orbitDBService, ipfsService } = await setupServices();

    // Initialize framework
    const framework = new DebrosFramework();
    await framework.initialize(orbitDBService, ipfsService);

    console.log('✅ Framework initialized successfully');

    // Create blog service
    const blogService = new BlogService(framework);

    // Demo: Create sample data
    await createSampleData(blogService);

    // Demo: Query data
    await demonstrateQueries(blogService);

    // Keep running for demo
    console.log('📝 Blog application is running...');
    console.log('Press Ctrl+C to stop');

    // Graceful shutdown
    process.on('SIGINT', async () => {
      console.log('\n🛑 Shutting down...');
      await framework.stop();
      process.exit(0);
    });
  } catch (error) {
    console.error('❌ Application failed to start:', error);
    process.exit(1);
  }
}

async function createSampleData(blogService: BlogService) {
  console.log('\n📝 Creating sample data...');

  // Create users
  const alice = await blogService.createUser({
    username: 'alice',
    email: 'alice@example.com',
    bio: 'Tech enthusiast and blogger',
    avatarUrl: 'https://example.com/avatars/alice.jpg',
  });

  const bob = await blogService.createUser({
    username: 'bob',
    email: 'bob@example.com',
    bio: 'Developer and writer',
  });

  console.log(`✅ Created users: ${alice.username}, ${bob.username}`);

  // Create posts
  const post1 = await blogService.createPost(alice.id, {
    title: 'Getting Started with DebrosFramework',
    content: 'DebrosFramework makes it easy to build decentralized applications...',
    tags: ['debros', 'tutorial', 'decentralized'],
  });

  const post2 = await blogService.createPost(bob.id, {
    title: 'Building Scalable dApps',
    content: 'Scalability is crucial for decentralized applications...',
    tags: ['scaling', 'dapps', 'blockchain'],
  });

  // Publish posts
  await post1.publish();
  await post2.publish();

  console.log(`✅ Created and published posts: "${post1.title}", "${post2.title}"`);

  // Create comments
  const comment1 = await blogService.createComment(
    bob.id,
    post1.id,
    'Great introduction to DebrosFramework!',
  );

  const comment2 = await blogService.createComment(
    alice.id,
    post2.id,
    'Very insightful article about scaling.',
  );

  console.log(`✅ Created ${[comment1, comment2].length} comments`);

  // Add some interactions
  await post1.like();
  await post1.like();
  await post2.like();
  await comment1.like();

  console.log('✅ Added sample interactions (likes)');
}

async function demonstrateQueries(blogService: BlogService) {
  console.log('\n🔍 Demonstrating queries...');

  // Get all published posts
  const { posts, total } = await blogService.getPublishedPosts({ limit: 10 });
  console.log(`📚 Found ${total} published posts:`);
  posts.forEach((post) => {
    console.log(
      `  - "${post.title}" by ${post.author.username} (${post.likeCount} likes, ${post.viewCount} views)`,
    );
  });

  // Search posts
  const searchResults = await blogService.searchPosts('DebrosFramework');
  console.log(`\n🔍 Search results for "DebrosFramework": ${searchResults.length} posts`);
  searchResults.forEach((post) => {
    console.log(`  - "${post.title}" by ${post.author.username}`);
  });

  // Get popular posts
  const popularPosts = await blogService.getPopularPosts('week');
  console.log(`\n⭐ Popular posts this week: ${popularPosts.length}`);
  popularPosts.forEach((post) => {
    console.log(`  - "${post.title}" (${post.likeCount} likes)`);
  });

  // Get tags
  const tags = await blogService.getTags();
  console.log(`\n🏷  Popular tags:`);
  tags.slice(0, 5).forEach(({ tag, count }) => {
    console.log(`  - ${tag}: ${count} posts`);
  });

  // Get user stats
  const users = await User.query().find();
  for (const user of users) {
    const stats = await blogService.getUserStats(user.id);
    console.log(`\n📊 Stats for ${user.username}:`);
    console.log(`  - Posts: ${stats.postCount}`);
    console.log(`  - Total views: ${stats.totalViews}`);
    console.log(`  - Total likes: ${stats.totalLikes}`);
    console.log(`  - Comments: ${stats.commentCount}`);
  }

  // System stats
  const systemStats = await blogService.getSystemStats();
  console.log(`\n🌐 System stats:`);
  console.log(`  - Users: ${systemStats.userCount}`);
  console.log(`  - Posts: ${systemStats.postCount} (${systemStats.publishedPostCount} published)`);
  console.log(`  - Comments: ${systemStats.commentCount}`);
}

// Run the application
main().catch(console.error);

4. Service Setup

Create src/setup.ts:

import { createHelia } from '@helia/helia';
import { createOrbitDB } from '@orbitdb/core';

export async function setupServices() {
  console.log('🔧 Setting up IPFS and OrbitDB services...');

  // Create IPFS instance
  const ipfs = await createHelia({
    // Configure as needed for your environment
  });

  // Create OrbitDB instance
  const orbitdb = await createOrbitDB({ ipfs });

  // Wrap services for DebrosFramework
  const ipfsService = {
    async init() {
      /* Already initialized */
    },
    getHelia: () => ipfs,
    getLibp2pInstance: () => ipfs.libp2p,
    async stop() {
      await ipfs.stop();
    },
  };

  const orbitDBService = {
    async init() {
      /* Already initialized */
    },
    async openDB(name: string, type: string) {
      return await orbitdb.open(name, { type });
    },
    getOrbitDB: () => orbitdb,
    async stop() {
      await orbitdb.stop();
    },
  };

  console.log('✅ Services setup complete');

  return { ipfsService, orbitDBService };
}

5. Running the Application

Add to your package.json:

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "ts-node src/index.ts"
  }
}

Run the application:

npm run dev

Key Concepts Demonstrated

1. Model Definition

  • Using decorators to define models and fields
  • Implementing validation and transformation
  • Setting up relationships between models
  • Using hooks for lifecycle management

2. Database Scoping

  • Global models for shared data (User)
  • User-scoped models for private data (Post, Comment)
  • Automatic sharding based on model configuration

3. Query Operations

  • Basic CRUD operations
  • Complex queries with filtering and sorting
  • Relationship loading (eager and lazy)
  • Pagination and search functionality

4. Business Logic

  • Service layer for application logic
  • Model methods for domain-specific operations
  • Data validation and transformation
  • Analytics and reporting

5. Error Handling

  • Graceful error handling in service methods
  • Validation error handling
  • Application lifecycle management

This example provides a solid foundation for building more complex applications with DebrosFramework. You can extend it by adding features like user authentication, real-time notifications, file uploads, and more advanced search capabilities.