Einen großartigen Nest.js-Blog erstellen: Kommentarsystem
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Im vorherigen Tutorial haben wir die Benutzerauthentifizierung mit express-session
implementiert. Nun können sich Benutzer im Blog registrieren und anmelden, und wir haben auch Routen geschützt, die einen Login zum Zugriff erfordern.
Da wir nun eindeutig zwischen Lesern und Autoren unterscheiden können, ist es nicht eine gute Zeit, eine Funktion für die Interaktion zwischen ihnen hinzuzufügen?
In diesem Artikel werden wir eine grundlegende, aber sehr wichtige Funktion zu unserem Blog hinzufügen: ein Kommentarsystem.
Spezifisch werden wir die folgenden Funktionalitäten implementieren:
- Eine Liste von Kommentaren unter jedem Artikel anzeigen.
- Eingeloggten Benutzern das Posten von Kommentaren ermöglichen.
Schritt 1: Erstellen Sie das Datenmodell für Kommentare
Genau wie bei unseren früheren Beiträgen und Benutzern benötigen unsere Kommentare eine eigene Datenbanktabelle und eine entsprechende Entitätsdatei.
Erstellen Sie die Datenbanktabelle
Führen Sie zunächst die folgende SQL-Anweisung in Ihrer PostgreSQL-Datenbank aus, um die Tabelle comment
zu erstellen. Diese Tabelle ist sowohl mit der Tabelle post
als auch mit der Tabelle user
verknüpft.
CREATE TABLE "comment" ( "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), "content" TEXT NOT NULL, "createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), "postId" UUID REFERENCES "post"("id") ON DELETE CASCADE, "userId" UUID REFERENCES "user"("id") ON DELETE CASCADE );
ON DELETE CASCADE
bedeutet, dass beim Löschen eines Beitrags oder eines Benutzers alle zugehörigen Kommentare automatisch gelöscht werden, um die Datenkonsistenz zu gewährleisten.
Wenn Ihre Datenbank auf Leapcell erstellt wurde,
können Sie SQL-Anweisungen einfach über die grafische Benutzeroberfläche ausführen. Gehen Sie dazu auf der Website zur Seite Datenbankverwaltung, fügen Sie die obige Anweisung in die SQL-Oberfläche ein und führen Sie sie aus.
Erstellen Sie die Comment-Entität
Erstellen Sie als Nächstes ein comments
-Modul, um die gesamte Logik im Zusammenhang mit Kommentaren zu verwalten.
nest generate module comments nest generate service comments nest generate controller comments
Erstellen Sie im Verzeichnis src/comments
eine Datei namens comment.entity.ts
:
// src/comments/comment.entity.ts import { Entity, Column, PrimaryColumn, CreateDateColumn, ManyToOne } from 'typeorm'; import { User } from '../users/user.entity'; import { Post } from '../posts/post.entity'; @Entity() export class Comment { @PrimaryColumn({ type: 'uuid', default: () => 'gen_random_uuid()' }) id: string; @Column('text') content: string; @CreateDateColumn() createdAt: Date; @ManyToOne(() => User, (user) => user.comments) user: User; @ManyToOne(() => Post, (post) => post.comments) post: Post; }
Aktualisieren Sie die zugehörigen Entitäten
Wir haben die ManyToOne
-Beziehung von Kommentaren zu Benutzern und Beiträgen definiert. Nun müssen wir die umgekehrte OneToMany
-Beziehung in den Entitäten User
und Post
definieren.
Aktualisieren Sie src/users/user.entity.ts
:
// src/users/user.entity.ts import { Entity, Column, PrimaryColumn, OneToMany } from 'typeorm'; import { Comment } from '../comments/comment.entity'; // Import Comment @Entity() export class User { @PrimaryColumn({ type: 'uuid', default: () => 'gen_random_uuid()' }) id: string; @Column({ unique: true }) username: string; @Column() password: string; @OneToMany(() => Comment, (comment) => comment.user) // Add relationship comments: Comment[]; }
Aktualisieren Sie src/posts/post.entity.ts
:
// src/posts/post.entity.ts import { Entity, Column, PrimaryColumn, CreateDateColumn, OneToMany } from 'typeorm'; import { Comment } from '../comments/comment.entity'; // Import Comment @Entity() export class Post { @PrimaryColumn({ type: 'uuid', default: () => 'gen_random_uuid()' }) id: string; @Column() title: string; @Column('text') content: string; @CreateDateColumn() createdAt: Date; @OneToMany(() => Comment, (comment) => comment.post) // Add relationship comments: Comment[]; }
Schritt 2: Implementieren Sie den Comment-Service
Als Nächstes schreiben wir den CommentsService
, der die Logik für die Erstellung und Abfrage von Kommentaren übernimmt.
Registrieren Sie die Comment-Entität
Öffnen Sie src/comments/comments.module.ts
, registrieren Sie das TypeOrmModule
und rufen Sie den CommentsService
ab, damit andere Module ihn verwenden können.
// src/comments/comments.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { CommentsService } from './comments.service'; import { Comment } from './comment.entity'; import { CommentsController } from './comments.controller'; @Module({ imports: [TypeOrmModule.forFeature([Comment])], providers: [CommentsService], controllers: [CommentsController], exports: [CommentsService], // Export the Service }) export class CommentsModule {}
Schreiben Sie die Service-Logik
Ändern Sie die Datei src/comments/comments.service.ts
, um Methoden zum Erstellen und Abfragen von Kommentaren hinzuzufügen.
// src/comments/comments.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Comment } from './comment.entity'; import { Post } from '../posts/post.entity'; import { User } from '../users/user.entity'; @Injectable() export class CommentsService { constructor( @InjectRepository(Comment) private commentsRepository: Repository<Comment> ) {} // Alle Kommentare nach Post-ID finden und Benutzerinformationen einbeziehen findByPostId(postId: string): Promise<Comment[]> { return this.commentsRepository.find({ where: { post: { id: postId } }, relations: ['user'], // Schlüssel: Auch das zugehörige Benutzerobjekt laden order: { createdAt: 'ASC', // In aufsteigender Reihenfolge nach Zeit sortieren }, }); } // Einen Kommentar erstellen async create(content: string, user: User, post: Post): Promise<Comment> { const newComment = this.commentsRepository.create({ content, user, post, }); return this.commentsRepository.save(newComment); } }
relations: ['user']
ist ein sehr nützliches Feature von TypeORM. Es teilt TypeORM mit, das zugehörigeuser
-Objekt über den Fremdschlüssel beim Abfragen von Kommentaren automatisch abzurufen und zu befüllen. Dies erleichtert den Abruf des Benutzernamens des Autors des Kommentars.
Schritt 3: Kommentare einreichen und anzeigen
Nun müssen wir die Kommentarfunktionalität in die Artikelseite integrieren.
Dazu fügen wir der CommentsController
-Logik hinzu, um Kommentar-Einreichungen zu akzeptieren, und aktualisieren die PostsController
, um Kommentare auf der Artikelseite anzuzeigen.
Implementieren Sie CommentsController
Ändern Sie src/comments/comments.controller.ts
:
// src/comments/comments.controller.ts import { Controller, Post, Body, Param, Req, Res, UseGuards } from '@nestjs/common'; import { CommentsService } from './comments.service'; import { AuthenticatedGuard } from '../auth/authenticated.guard'; import type { Response, Request } from 'express'; @Controller('posts/:postId/comments') export class CommentsController { constructor(private readonly commentsService: CommentsService) {} @UseGuards(AuthenticatedGuard) // Sicherstellen, dass nur eingeloggte Benutzer kommentieren können @Post() async create( @Param('postId') postId: string, @Body('content') content: string, @Req() req: Request, @Res() res: Response ) { // req.session.user wird von express-session hinzugefügt const { user } = req.session as any; // Hinweis: In einer realen Anwendung müssen Sie überprüfen, ob postId existiert await this.commentsService.create(content, user, { id: postId } as any); res.redirect(`/posts/${postId}`); // Nach erfolgreichem Kommentieren zurück zur Artikelseite weiterleiten } }
Aktualisieren Sie PostsController
Ändern Sie die Methode findOne
des PostsController
, sodass beim Rendern der Artikelseite auch alle Kommentare für diesen Artikel abgerufen und übergeben werden.
Importieren Sie zunächst CommentsModule
in src/posts/posts.module.ts
, damit PostsModule
CommentsService
verwenden kann.
// src/posts/posts.module.ts // ... import { CommentsModule } from '../comments/comments.module'; @Module({ imports: [TypeOrmModule.forFeature([Post]), CommentsModule], // CommentsModule importieren controllers: [PostsController], providers: [PostsService], }) export class PostsModule {}
Aktualisieren Sie dann src/posts/posts.controller.ts
:
// src/posts/posts.controller.ts import { Controller, Get, Render, Param, Request /* ... */ } from '@nestjs/common'; import { PostsService } from './posts.service'; import { CommentsService } from '../comments/comments.service'; // CommentsService importieren // ... @Controller('posts') export class PostsController { constructor( private readonly postsService: PostsService, private readonly commentsService: CommentsService // CommentsService injizieren ) {} // ... andere Methoden bleiben unverändert @Get(':id') @Render('post') async post(@Param('id') id: string, @Request() req) { const post = await this.postsService.findOne(id); const comments = await this.commentsService.findByPostId(id); // Kommentare abrufen return { post, user: req.session.user, comments }; // Kommentare an die Vorlage übergeben } }
Schritt 4: Aktualisieren Sie die Frontend-Ansicht
Der letzte Schritt besteht darin, die EJS-Vorlage zu ändern, um die Kommentarliste und das Kommentarformular anzuzeigen.
Öffnen Sie views/post.ejs
und fügen Sie den folgenden Code unter dem Artikelinhalt ein:
<a href="/" class="back-link">← Zurück zur Startseite</a> <section class="comments-section"> <h3>Kommentare</h3> <div class="comment-list"> <% if (comments.length > 0) { %> <% comments.forEach(comment => { %> <div class="comment-item"> <p class="comment-content"><%= comment.content %></p> <small> Von <strong><%= comment.user.username %></strong> am <%= new Date(comment.createdAt).toLocaleDateString() %> </small> </div> <% }) %> <% } else { %> <p>Noch keine Kommentare. Seien Sie der Erste, der kommentiert!</p> <% } %> </div> <% if (user) { %> <form action="/posts/<%= post.id %>/comments" method="POST" class="comment-form"> <h4>Hinterlassen Sie einen Kommentar</h4> <div class="form-group"> <textarea name="content" rows="4" placeholder="Schreiben Sie hier Ihren Kommentar..." required></textarea> </div> <button type="submit">Kommentar senden</button> </form> <% } else { %> <p><a href="/auth/login">Anmelden</a>, um einen Kommentar zu hinterlassen.</p> <% } %> </section> <%- include('_footer') %>
Damit die Seite besser aussieht, können Sie einige Stile zu public/css/style.css
hinzufügen:
/* ... andere Stile ... */ .comments-section { margin-top: 3rem; border-top: 1px solid #eee; padding-top: 2rem; } .comment-list .comment-item { background: #f9f9f9; border: 1px solid #ddd; padding: 1rem; border-radius: 5px; margin-bottom: 1rem; } .comment-content { margin-top: 0; } .comment-item small { color: #666; } .comment-form { margin-top: 2rem; } .comment-form textarea { width: 100%; padding: 0.5rem; margin-bottom: 1rem; }
Ausführen und Testen
Starten Sie Ihre Anwendung neu:
npm run start:dev
Öffnen Sie Ihren Browser und besuchen Sie die Webseite: http://localhost:3000/
Navigieren Sie zu einem beliebigen Artikel, und Sie werden nun den Kommentarbereich sehen.
Geben Sie Inhalt in das Kommentarfeld ein und senden Sie ihn ab. Nachdem die Seite aktualisiert wurde, sehen Sie den gerade geposteten Kommentar in der Kommentarliste.
Herzlichen Glückwunsch, Sie haben erfolgreich ein Kommentarsystem zu Ihrem Blog hinzugefügt!
Natürlich ist die aktuelle Kommentarfunktionalität noch recht einfach. Im nächsten Artikel werden wir diese Funktion weiter verbessern, indem wir die Logik für Autoren implementieren, um auf Kommentare zu antworten, und so die Interaktivität des Blogs auf das nächste Level heben.
Frühere Tutorials: