Rytass Utils - CMS Base NestJS Module
Powerful content management system base module designed specifically for NestJS applications. Provides complete multi-language article management, version control, approval workflows, and category management functionality, making it the ideal choice for building enterprise-grade CMS systems.
Features
Installation
npm install @rytass/cms-base-nestjs-module @nestjs/typeorm typeorm
yarn add @rytass/cms-base-nestjs-module @nestjs/typeorm typeorm
Basic Usage
Module Configuration
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CmsBaseModule } from '@rytass/cms-base-nestjs-module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'username',
password: 'password',
database: 'cms_database',
entities: [],
synchronize: true,
}),
CmsBaseModule.forRoot({
multipleLanguageMode: true,
draftMode: true,
signatureLevels: [
{ id: 1, name: 'Editor', level: 1 },
{ id: 2, name: 'Senior Editor', level: 2 },
{ id: 3, name: 'Chief Editor', level: 3 }
],
fullTextSearchMode: true,
autoReleaseAfterApproved: false
})
],
})
export class AppModule {}
Async Configuration
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CmsBaseModule } from '@rytass/cms-base-nestjs-module';
@Module({
imports: [
ConfigModule.forRoot(),
CmsBaseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
multipleLanguageMode: configService.get('CMS_MULTI_LANGUAGE') === 'true',
draftMode: configService.get('CMS_DRAFT_MODE') === 'true',
signatureLevels: JSON.parse(configService.get('CMS_SIGNATURE_LEVELS')),
fullTextSearchMode: configService.get('CMS_FULL_TEXT_SEARCH') === 'true'
})
})
],
})
export class AppModule {}
Core Services
Article Management
import { Injectable } from '@nestjs/common';
import { ArticleBaseService } from '@rytass/cms-base-nestjs-module';
@Injectable()
export class ArticleService {
constructor(
private readonly articleBaseService: ArticleBaseService
) {}
async createArticle(data: CreateArticleDto) {
return await this.articleBaseService.create({
title: data.title,
content: data.content,
categoryIds: data.categoryIds,
language: data.language || 'zh-TW',
authorId: data.authorId,
customFields: data.customFields
});
}
async publishArticle(articleId: string, userId: string) {
return await this.articleBaseService.release(articleId, { userId });
}
async getArticleWithVersions(articleId: string) {
return await this.articleBaseService.findById(articleId);
}
}
Category Management
import { Injectable } from '@nestjs/common';
import { CategoryBaseService } from '@rytass/cms-base-nestjs-module';
@Injectable()
export class CategoryService {
constructor(
private readonly categoryBaseService: CategoryBaseService
) {}
async createCategory(data: CreateCategoryDto) {
return await this.categoryBaseService.create({
names: data.names,
parentId: data.parentId,
description: data.description,
sortOrder: data.sortOrder
});
}
async getCategoryTree() {
return await this.categoryBaseService.findAll();
}
async getArticlesByCategory(categoryId: string, options = {}) {
return await this.categoryBaseService.findArticles(
categoryId,
{
page: options.page || 1,
limit: options.limit || 10,
includeSubCategories: true
}
);
}
}
Data Models
Article Entity
import { BaseArticleEntity } from '@rytass/cms-base-nestjs-module';
import { Entity, Column } from 'typeorm';
@Entity('articles')
export class Article extends BaseArticleEntity {
@Column({ type: 'jsonb', nullable: true })
customFields?: Record<string, any>;
@Column({ nullable: true })
featuredImage?: string;
@Column({ type: 'text', array: true, default: [] })
tags: string[];
}
Category Entity
import { BaseCategoryEntity } from '@rytass/cms-base-nestjs-module';
import { Entity, Column } from 'typeorm';
@Entity('categories')
export class Category extends BaseCategoryEntity {
@Column({ nullable: true })
icon?: string;
@Column({ type: 'jsonb', nullable: true })
metadata?: Record<string, any>;
@Column({ default: true })
isActive: boolean;
}
Multi-Language Support
const article = await articleService.createArticle({
title: {
'zh-TW': '中文標題',
'en-US': 'English Title',
'ja-JP': '日本語タイトル'
},
content: {
'zh-TW': '中文內容...',
'en-US': 'English content...',
'ja-JP': '日本語の内容...'
},
categoryIds: ['category-1', 'category-2'],
language: 'zh-TW'
});
const articles = await articleService.findArticles({
language: 'en-US',
status: 'published'
});
Version Control
const version = await articleService.createVersion(articleId, {
title: 'Updated Title',
content: 'Updated content',
changeReason: 'Fixed typos',
authorId: userId
});
const versions = await articleService.getVersionHistory(articleId);
await articleService.revertToVersion(articleId, versionId);
const diff = await articleService.compareVersions(articleId, version1Id, version2Id);
Approval Workflow
const signatureRequest = await articleService.submitForApproval(articleId, {
submitterId: userId,
comments: 'Please review this article'
});
await articleService.approveArticle(articleId, {
approverId: managerId,
level: 2,
comments: 'Content looks good, approved'
});
await articleService.rejectArticle(articleId, {
reviewerId: managerId,
reason: 'Content needs revision',
suggestions: 'Please add more details'
});
Full-Text Search
const searchResults = await articleService.searchArticles({
query: 'search keywords',
language: 'zh-TW',
categories: ['tech', 'news'],
dateRange: {
from: new Date('2024-01-01'),
to: new Date('2024-12-31')
},
page: 1,
limit: 20
});
const suggestions = await articleService.getSuggestions('keywords');
DataLoader Integration
import { ArticleDataloader, CategoryDataloader } from '@rytass/cms-base-nestjs-module';
@Injectable()
export class ArticleResolver {
constructor(
private readonly articleDataloader: ArticleDataloader,
private readonly categoryDataloader: CategoryDataloader
) {}
@ResolveField()
async categories(@Parent() article: Article) {
return this.categoryDataloader.loadMany(article.categoryIds);
}
@ResolveField()
async relatedArticles(@Parent() article: Article) {
return this.articleDataloader.loadRelated(article.id);
}
}
Configuration Options
CmsBaseModuleOptions
multipleLanguageMode | boolean | false | Enable multi-language support |
draftMode | boolean | true | Enable draft mode |
signatureLevels | SignatureLevel[] | [] | Approval level settings |
fullTextSearchMode | boolean | false | Enable full-text search |
autoReleaseAfterApproved | boolean | false | Auto-publish after approval |
circularCategoryMode | boolean | false | Allow circular category references |
multipleCategoryParentMode | boolean | false | Allow multiple parent categories |
Error Handling
import {
ArticleNotFoundError,
CategoryNotFoundError,
InsufficientPermissionError
} from '@rytass/cms-base-nestjs-module';
try {
await articleService.publishArticle(articleId, userId);
} catch (error) {
if (error instanceof ArticleNotFoundError) {
throw new NotFoundException('Article not found');
} else if (error instanceof InsufficientPermissionError) {
throw new ForbiddenException('Insufficient permissions');
}
throw error;
}
Best Practices
Performance
- Use DataLoader to avoid N+1 query problems
- Set up appropriate database indexes
- Use pagination for large data queries
- Enable query caching mechanisms
Security
- Implement proper permission controls
- Validate user inputs
- Use parameterized queries to prevent SQL injection
- Log sensitive operation activities
Scalability
- Separate read and write operations
- Use Redis caching for hot content
- Implement content delivery networks (CDN)
- Consider database sharding strategies
Migration
npm run typeorm:migration:generate -- -n CreateCmsBaseTables
npm run typeorm:migration:run
npm run typeorm:migration:revert
License
MIT