Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/devenock/nest_blog

NestJS Blog Backend API
https://github.com/devenock/nest_blog

Last synced: 6 days ago
JSON representation

NestJS Blog Backend API

Awesome Lists containing this project

README

        

# NestJS Blog Backend Documentation

## Table of Contents

1. Introduction
2. Architecture Overview
3. Setup and Installation
4. Module Breakdown
5. API Endpoints
6. Database Schema
7. Authentication and Authorization
8. Performance Optimizations
9. Scalability Considerations
10. Deployment

## 1. Introduction

This documentation covers a sophisticated blog backend built using NestJS and MongoDB. The system is designed to be scalable, performant, and feature-rich, suitable for handling a complex blogging platform with multiple users, posts, comments, categories, and tags.

## 2. Architecture Overview

The blog backend is built on a modular architecture, leveraging NestJS's powerful dependency injection system and MongoDB's flexible document model. The main components include:

- API Gateway
- Authentication Module
- User Module
- Post Module
- Comment Module
- Category Module
- Tag Module
- Search Module
- Analytics Module
- Caching Layer (Redis)
- Task Queue (RabbitMQ)
- MongoDB Database

## 3. Setup and Installation

To set up the project, follow these steps:

1. Clone the repository:
```bash
git clone https://github.com/your-repo/nestjs-blog-backend.git
cd nestjs-blog-backend
```

2. Install dependencies:
```bash
npm install
```

3. Set up environment variables:
Create a `.env` file in the root directory with the following content:
```
MONGODB_URI=mongodb://localhost:27017/blog
JWT_SECRET=your_jwt_secret
REDIS_URL=redis://localhost:6379
```

4. Start the development server:
```bash
npm run start:dev
```

## 4. Module Breakdown

### User Module
- Handles user registration, authentication, and profile management
- Manages user roles and permissions

### Post Module
- Manages blog post CRUD operations
- Handles post versioning and drafts

### Comment Module
- Manages comments on blog posts
- Implements nested comments and moderation features

### Category Module
- Manages blog categories
- Handles hierarchical category structures

### Tag Module
- Manages tags for blog posts

### Search Module
- Implements full-text search functionality for posts, comments, and users
- Integrates with MongoDB's text search capabilities

### Analytics Module
- Tracks post views, user engagement, and other metrics
- Generates reports and insights

### Notification Module
- Manages email notifications for new posts, comments, and user activities
- Implements real-time notifications using WebSockets

### Media Module
- Handles file uploads for images and other media
- Integrates with cloud storage solutions (e.g., AWS S3)

## 5. API Endpoints

Here's an overview of the main API endpoints:

```typescript
@Controller('api')
export class AppController {
@Post('auth/login')
login() {}

@Post('auth/register')
register() {}

@Get('users')
getUsers() {}

@Get('users/:id')
getUser() {}

@Put('users/:id')
updateUser() {}

@Delete('users/:id')
deleteUser() {}

@Get('posts')
getPosts() {}

@Post('posts')
createPost() {}

@Get('posts/:id')
getPost() {}

@Put('posts/:id')
updatePost() {}

@Delete('posts/:id')
deletePost() {}

@Get('posts/:id/comments')
getComments() {}

@Post('posts/:id/comments')
createComment() {}

@Put('comments/:id')
updateComment() {}

@Delete('comments/:id')
deleteComment() {}

@Get('categories')
getCategories() {}

@Post('categories')
createCategory() {}

@Put('categories/:id')
updateCategory() {}

@Delete('categories/:id')
deleteCategory() {}

@Get('tags')
getTags() {}

@Post('tags')
createTag() {}

@Put('tags/:id')
updateTag() {}

@Delete('tags/:id')
deleteTag() {}

@Get('search')
search() {}

@Get('analytics')
getAnalytics() {}
}
```

For detailed information on request/response formats and authentication requirements, refer to the API documentation generated using Swagger.

## 6. Database Schema

The MongoDB schema for the blog backend is designed as follows:

```typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Schema as MongooseSchema } from 'mongoose';

@Schema()
export class User extends Document {
@Prop({ required: true, unique: true })
username: string;

@Prop({ required: true, unique: true })
email: string;

@Prop({ required: true })
password: string;

@Prop({ enum: ['admin', 'author', 'reader'], default: 'reader' })
role: string;

@Prop({ default: Date.now })
createdAt: Date;

@Prop({ default: Date.now })
updatedAt: Date;
}

@Schema()
export class Post extends Document {
@Prop({ required: true })
title: string;

@Prop({ required: true })
content: string;

@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'User' })
author: User;

@Prop({ type: [{ type: MongooseSchema.Types.ObjectId, ref: 'Category' }] })
categories: Category[];

@Prop({ type: [{ type: MongooseSchema.Types.ObjectId, ref: 'Tag' }] })
tags: Tag[];

@Prop({ enum: ['draft', 'published'], default: 'draft' })
status: string;

@Prop()
publishedAt: Date;

@Prop({ default: Date.now })
createdAt: Date;

@Prop({ default: Date.now })
updatedAt: Date;
}

@Schema()
export class Comment extends Document {
@Prop({ required: true })
content: string;

@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'User' })
author: User;

@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Post' })
post: Post;

@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Comment' })
parent: Comment;

@Prop({ default: Date.now })
createdAt: Date;

@Prop({ default: Date.now })
updatedAt: Date;
}

@Schema()
export class Category extends Document {
@Prop({ required: true })
name: string;

@Prop({ required: true, unique: true })
slug: string;

@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Category' })
parent: Category;

@Prop({ default: Date.now })
createdAt: Date;

@Prop({ default: Date.now })
updatedAt: Date;
}

@Schema()
export class Tag extends Document {
@Prop({ required: true })
name: string;

@Prop({ required: true, unique: true })
slug: string;

@Prop({ default: Date.now })
createdAt: Date;

@Prop({ default: Date.now })
updatedAt: Date;
}

@Schema()
export class Analytics extends Document {
@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Post' })
post: Post;

@Prop({ default: 0 })
views: number;

@Prop({ default: 0 })
uniqueVisitors: number;

@Prop({ default: Date.now })
date: Date;
}

export const UserSchema = SchemaFactory.createForClass(User);
export const PostSchema = SchemaFactory.createForClass(Post);
export const CommentSchema = SchemaFactory.createForClass(Comment);
export const CategorySchema = SchemaFactory.createForClass(Category);
export const TagSchema = SchemaFactory.createForClass(Tag);
export const AnalyticsSchema = SchemaFactory.createForClass(Analytics);
```

## 7. Authentication and Authorization

The blog backend uses JWT-based authentication and role-based access control. Here's an example of the authentication guard:

```typescript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private reflector: Reflector,
private jwtService: JwtService,
) {}

async canActivate(context: ExecutionContext): Promise {
const requiredRoles = this.reflector.get('roles', context.getHandler());
if (!requiredRoles) {
return true;
}

const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
return false;
}

try {
const payload = await this.jwtService.verifyAsync(token);
request['user'] = payload;
} catch {
return false;
}

return requiredRoles.some((role) => request.user.roles?.includes(role));
}

private extractTokenFromHeader(request: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
```

To use this guard, apply it to your controllers or routes:

```typescript
@UseGuards(AuthGuard)
@Get('protected')
@Roles('admin')
getProtectedResource() {
return 'This is a protected resource';
}
```

## 8. Performance Optimizations

To optimize performance, the blog backend implements several strategies:

1. **Caching**: Use Redis to cache frequently accessed data.

```typescript
import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';

@Injectable()
export class CacheService {
private readonly redis: Redis;

constructor() {
this.redis = new Redis(process.env.REDIS_URL);
}

async get(key: string): Promise {
return this.redis.get(key);
}

async set(key: string, value: string, ttl?: number): Promise {
if (ttl) {
await this.redis.set(key, value, 'EX', ttl);
} else {
await this.redis.set(key, value);
}
}

async del(key: string): Promise {
await this.redis.del(key);
}
}
```

2. **Pagination**: Implement pagination for list endpoints to limit the amount of data transferred.

```typescript
@Injectable()
export class PostService {
constructor(
@InjectModel(Post.name) private postModel: Model,
) {}

async findAll(page: number = 1, limit: number = 10): Promise {
const skip = (page - 1) * limit;
return this.postModel.find().skip(skip).limit(limit).exec();
}
}
```

3. **Database Indexing**: Create indexes for frequently queried fields in MongoDB.

```typescript
PostSchema.index({ title: 'text', content: 'text' });
PostSchema.index({ author: 1, createdAt: -1 });
```

4. **Aggregation Pipelines**: Use MongoDB aggregation pipelines for complex queries.

```typescript
@Injectable()
export class AnalyticsService {
constructor(
@InjectModel(Analytics.name) private analyticsModel: Model,
) {}

async getTopPosts(limit: number = 10): Promise {
return this.analyticsModel.aggregate([
{ $group: { _id: '$post', totalViews: { $sum: '$views' } } },
{ $sort: { totalViews: -1 } },
{ $limit: limit },
{
$lookup: {
from: 'posts',
localField: '_id',
foreignField: '_id',
as: 'postDetails',
},
},
{ $unwind: '$postDetails' },
{
$project: {
_id: 0,
postId: '$_id',
title: '$postDetails.title',
totalViews: 1,
},
},
]);
}
}
```

## 9. Scalability Considerations

To ensure the blog backend can scale effectively:

1. **Horizontal Scaling**: Deploy multiple NestJS instances behind a load balancer.
2. **Message Queue**: Use RabbitMQ for handling background tasks.
3. **Database Sharding**: Implement MongoDB sharding for large datasets.

The scalable architecture includes:
- Load Balancer
- Multiple NestJS Instances
- Redis Cache
- RabbitMQ
- MongoDB Cluster
- Worker Instances
- CDN

## 10. Deployment

To deploy the blog backend:

1. Set up a MongoDB cluster (e.g., MongoDB Atlas).
2. Set up a Redis instance for caching.
3. Set up a RabbitMQ instance for the message queue.
4. Deploy NestJS instances to a cloud provider (e.g., AWS, Google Cloud, or Heroku).
5. Set up a load balancer to distribute traffic among NestJS instances.
6. Configure environment variables for production.

Example Docker Compose file for local development:

```yaml
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- MONGODB_URI=mongodb://mongo:27017/blog
- JWT_SECRET=your_jwt_secret
- REDIS_URL=redis://redis:6379
- RABBITMQ_URL=amqp://rabbitmq:5672
depends_on:
- mongo
- redis
- rabbitmq

mongo:
image: mongo:latest
ports:
- "27017:27017"

redis:
image: redis:latest
ports:
- "6379:6379"

rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
```

This comprehensive documentation provides a solid foundation for understanding, developing, and deploying the sophisticated blog backend using NestJS and MongoDB. As the project evolves, be sure to keep this documentation updated to reflect any changes in the architecture or implementation details.