優れたNest.jsブログを構築する:タグでフィルタリング
Min-jun Kim
Dev Intern · Leapcell

前のチュートリアルでは、ブログのタグを作成および表示する機能を追加しました。
次に、タグ機能の残りの部分、つまりタグによる投稿のフィルタリングを完了します。
ユーザーが投稿詳細ページでタグリンクをクリックしたときに、そのタグの投稿のみを表示するリストページにユーザーをリダイレクトする必要があります。これを行うには、バックエンドに新しいルートと処理ロジックを作成し、フロントエンドに対応するビューを作成する必要があります。
ステップ1:サービスロジックの拡張
まず、タグIDで投稿を検索するための新しいメソッドをサービスに追加します。
src/posts/posts.service.ts
ファイルを開き、次のコンテンツを追加します:
// src/posts/posts.service.ts // ... (その他のインポートとコンストラクター) async findByTag(tagId: string): Promise<Post[]> { return this.postsRepository .createQueryBuilder('post') .leftJoinAndSelect('post.tags', 'tag') .where('tag.id = :tagId', { tagId }) .orderBy('post.createdAt', 'DESC') .getMany(); } // ... (ファイルの残りの部分)
findByTag
は多対多の関係を伴うため、createQueryBuilder
を使用して投稿とタグの中間テーブルをJOINし、tagId
でフィルタリングします。
フィルタリングページにタグ名を表示するには、TagsService
に簡単なfindOne
メソッドを追加する必要があります。
// src/tags/tags.service.ts async findOne(id: string): Promise<Tag | null> { return this.tagsRepository.findOneBy({ id }); }
ステップ2:コントローラーとルートの作成
次に、/tags/:id
リクエストを処理するコントローラーを実装します。
コントローラーロジックの記述
対応するコントローラーファイルを作成します:
nest generate controller tags
src/tags/tags.controller.ts
を開き、次のコードを追加します:
// src/tags/tags.controller.ts import { Controller, Get, Param, Render, Request } from '@nestjs/common'; import { TagsService } from './tags.service'; import { PostsService } from '../posts/posts.service'; @Controller('tags') export class TagsController { constructor(private readonly tagsService: TagsService, private readonly postsService: PostsService) {} @Get(':id') @Render('posts-by-filter') async findPostsByTag(@Param('id') id: string, @Request() req) { const posts = await this.postsService.findByTag(id); const tag = await this.tagsService.findOne(id); return { posts, user: req.session.user, filterType: 'Tag', filterName: tag ? tag.name : 'Unknown', }; } }
モジュール依存関係の更新
上記のコードが正しく機能するには、サービス間の依存関係を処理するためにモジュールファイルを更新する必要があります。
PostsService
とTagsService
は互いに依存しているため、循環依存の問題を解決するためにforwardRef
を使用する必要があります。
まず、PostsService
をエクスポートして、他のモジュールが使用できるようにします。同時に、PostsModule
がTagsModule
を参照する方法を変更します。
// src/posts/posts.module.ts import { Module, forwardRef } from '@nestjs/common'; // ... @Module({ imports: [ TypeOrmModule.forFeature([Post]), CommentsModule, TrackingModule, forwardRef(() => TagsModule), // 循環依存を回避するためにforwardRefを使用 ], // ..., exports: [PostsService], // PostsServiceをエクスポート }) export class PostsModule {}
次に、TagsModule
にPostsModule
をインポートします。
// src/tags/tags.module.ts import { Module, forwardRef } from '@nestjs/common'; import { PostsModule } from '../posts/posts.module'; // インポート //... @Module({ imports: [TypeOrmModule.forFeature([Tag]), forwardRef(() => PostsModule)], // importsに追加 controllers: [TagsController], providers: [TagsService], }) export class TagsModule {}
ステップ3:フロントエンドビューの作成
最後に、views
フォルダにposts-by-filter.ejs
ビューファイルを作成します。このファイルは、タグでフィルタリングされた投稿のリストを表示するために使用されます。その内容はindex.ejs
と非常によく似ています。
views/posts-by-filter.ejs
ファイルを作成します:
<%- include('_header', { title: `Posts in ${filterName}` }) %> <div class="filter-header"> <h2>Posts in Tag: <strong><%= filterName %></strong></h2> </div> <% if (posts.length > 0) { %> <div class="post-list"> <% posts.forEach(post => { %> <article class="post-item"> <h2><a href="/posts/<%= post.id %>"><%= post.title %></a></h2> <p><%= post.content.substring(0, 150) %>...</p> <small><%= new Date(post.createdAt).toLocaleDateString() %></small> </article> <% }) %> </div> <% } else { %> <p>No posts found in this tag.</p> <% } %> <a href="/" class="back-link" style="margin-top: 2rem;">← Back to Home</a> <%- include('_footer') %>
このテンプレートは、タイトル(例:「Posts in Tag: Tutorial」)と投稿のリストを動的に表示します。タグの下に投稿がない場合は、メッセージが表示されます。
これで、フィルタリングプロセス全体が完了しました。アプリケーションを再起動して、投稿の詳細ページにあるタグリンクをクリックすると、対応するフィルタリングページにリダイレクトされます。
実行とテスト
アプリケーションを再起動します:
npm run start:dev
ブラウザを開いて、http://localhost:3000/にアクセスします。
タグが含まれている投稿にアクセスし、いずれかのタグをクリックします。
対応するタグのフィルタリングページにリダイレクトされ、そのタグの下にあるすべての投稿のリストが表示されます。
これらの2つのチュートリアルを通じて、ブログに完全なタギングシステムを追加しました。
この時点で、ブログプロジェクトは基本的なアーキテクチャからコア機能、コンテンツ編成、データ分析まで、すべてをカバーしています。
ブログの詳細機能は無限です。現在のフレームワークに基づいて、さらに機能を追加し続けることができます。残りはあなたの想像力次第です!
過去のチュートリアル: