Einen großartigen Nest.js-Blog erstellen: Besucheranalysen
Min-jun Kim
Dev Intern · Leapcell

Im vorherigen Tutorial haben wir eine Volltextsuche in unseren Blog integriert, um das Auffinden guter Inhalte zu erleichtern.
Jetzt, da Ihr Blog funktionsreicher wird und Ihre Inhalte wachsen, stellt sich natürlich eine neue Frage: Welche Artikel sind bei den Lesern am beliebtesten?
Das Verständnis der Interessen Ihrer Leser kann Ihnen helfen, qualitativ hochwertigere Inhalte zu erstellen.
Daher werden wir in diesem Tutorial ein grundlegendes, aber sehr wichtiges Feature zu unserem Blog hinzufügen: Besucher-Tracking. Wir werden die Anzahl der Aufrufe jedes Artikels aufzeichnen und die Aufrufzahl auf der Seite anzeigen.
Sie könnten erwägen, einen Drittanbieterdienst wie Google Analytics zu verwenden. Wenn wir jedoch ein Backend-gesteuertes Tracking-System selbst erstellen, können wir mehr Daten in unseren eigenen Händen behalten und anpassen, welche Daten wir sammeln möchten.
Legen wir los:
Schritt 1: Erstellen Sie das Datenmodell für Aufrufdatensätze
Erstellen Sie die Datenbanktabelle
Führen Sie die folgende SQL-Anweisung in Ihrer PostgreSQL-Datenbank aus, um die Tabelle page_view
zu erstellen. Diese Tabelle zeichnet die Zeit jedes Aufrufs, den entsprechenden Beitrag und einige Besucherinformationen (wie IP-Adresse und User-Agent) für zukünftige detaillierte Analysen auf.
CREATE TABLE "page_view" ( "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), "createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), "postId" UUID REFERENCES "post"("id") ON DELETE CASCADE, "ipAddress" VARCHAR(45), "userAgent" TEXT );
ON DELETE CASCADE
stellt sicher, dass beim Löschen eines Beitrags alle zugehörigen Aufrufdatensätze automatisch gelöscht werden.
Wenn Ihre Datenbank auf Leapcell erstellt wurde,
können Sie SQL-Anweisungen einfach über die grafische Oberfläche ausführen. Gehen Sie einfach zur Seite Datenbankverwaltung auf der Website, fügen Sie die obige Anweisung in die SQL-Oberfläche ein und führen Sie sie aus.
Erstellen Sie die PageView-Entität
Erstellen Sie als Nächstes ein neues Modul für dieses Tracking-Feature.
nest generate module tracking nest generate service tracking
Erstellen Sie eine Datei page-view.entity.ts
im Verzeichnis src/tracking
:
// src/tracking/page-view.entity.ts import { Entity, PrimaryColumn, CreateDateColumn, ManyToOne, Column } from 'typeorm'; import { Post } from '../posts/post.entity'; @Entity() export class PageView { @PrimaryColumn({ type: 'uuid', default: () => 'gen_random_uuid()' }) id: string; @CreateDateColumn() createdAt: Date; @ManyToOne(() => Post, { onDelete: 'CASCADE' }) post: Post; @Column({ type: 'varchar', length: 45, nullable: true }) ipAddress: string; @Column({ type: 'text', nullable: true }) userAgent: string; }
Schritt 2: Implementieren Sie den Tracking-Dienst
DerTrackingService
ist für die gesamte Logik im Zusammenhang mit Aufrufdatensätzen zuständig, einschließlich der Aufzeichnung neuer Aufrufe und der Abfrage von Aufrufzahlen.
Registrieren Sie die PageView-Entität
Öffnen Sie src/tracking/tracking.module.ts
, registrieren Sie das TypeOrmModule
und exportieren Sie den TrackingService
.
// src/tracking/tracking.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TrackingService } from './tracking.service'; import { PageView } from './page-view.entity'; @Module({ imports: [TypeOrmModule.forFeature([PageView])], providers: [TrackingService], exports: [TrackingService], }) export class TrackingModule {}
Schreiben Sie die Service-Logik
Modifizieren Sie src/tracking/tracking.service.ts
, um Methoden für die Aufzeichnung und Abfrage hinzuzufügen.
// src/tracking/tracking.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { PageView } from './page-view.entity'; import { Post } from '../posts/post.entity'; @Injectable() export class TrackingService { constructor( @InjectRepository(PageView) private pageViewRepository: Repository<PageView> ) {} async recordView(post: Post, ipAddress: string, userAgent: string): Promise<void> { const newView = this.pageViewRepository.create({ post, ipAddress, userAgent, }); await this.pageViewRepository.save(newView); } async getCountByPostId(postId: string): Promise<number> { return this.pageViewRepository.count({ where: { post: { id: postId } }, }); } // For efficiency, fetch view counts for multiple posts at once async getCountsByPostIds(postIds: string[]): Promise<Record<string, number>> { if (postIds.length === 0) { return {}; } const counts = await this.pageViewRepository .createQueryBuilder('page_view') .select('page_view.postId', 'postId') .addSelect('COUNT(*)', 'count') .where('page_view.postId IN (:...postIds)', { postIds }) .groupBy('page_view.postId') .getRawMany(); const result: Record<string, number> = {}; counts.forEach((item) => { result[item.postId] = parseInt(item.count, 10); }); return result; } }
Die Methode
getCountsByPostIds
verwendet denQueryBuilder
von TypeORM, um SQL direkt zu schreiben, was eine effizientereGROUP BY
-Abfrage ermöglicht. Dies ist wesentlich schneller, als für jeden Beitrag eine separatecount
-Abfrage auszuführen, insbesondere wenn die Homepage die Aufrufzahlen für viele Artikel anzeigen muss.
Schritt 3: Integrieren Sie die Aufzeichnung von Aufrufen auf der Beitragsseite
Als Nächstes müssen wir die Methode recordView
des TrackingService
jedes Mal aufrufen, wenn ein Besucher einen Artikel aufruft. Der am besten geeignete Ort dafür ist die post
-Methode im PostsController
, die den Inhalt des Blogbeitrags abruft.
Importieren Sie zuerst TrackingModule
in src/posts/posts.module.ts
.
// src/posts/posts.module.ts // ... import { TrackingModule } from '../tracking/tracking.module'; @Module({ imports: [TypeOrmModule.forFeature([Post]), CommentsModule, TrackingModule], // Import TrackingModule controllers: [PostsController], providers: [PostsService], }) export class PostsModule {}
Injizieren und rufen Sie dann den TrackingService
im PostsController
auf.
// 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'; import { TrackingService } from '../tracking/tracking.service'; // Import import { marked } from 'marked'; @Controller('posts') export class PostsController { constructor( private readonly postsService: PostsService, private readonly commentsService: CommentsService, private readonly trackingService: TrackingService // Inject ) {} // ... other methods @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); const viewCount = await this.trackingService.getCountByPostId(id); if (post) { // Record a view this.trackingService.recordView(post, req.ip, req.headers['user-agent']); post.content = marked.parse(post.content) as string; } return { post, user: req.session.user, comments, viewCount }; } }
Hinweis: Wir haben await
für die Methode recordView
nicht verwendet. Dies ist ein "Fire-and-Forget"-Aufruf. Wir möchten die Antwort an den Leser nicht verzögern, nur um die sekundäre Operation des Aufzeichnens eines Aufrufs durchzuführen. Die Artikelseite sollte dem Leser so schnell wie möglich zurückgegeben werden, unabhängig davon, ob die Aufzeichnung erfolgreich ist.
Schritt 4: Zeigen Sie die Aufrufzahl im Frontend an
Beitragsdetailseite
Im vorherigen Schritt haben wir bereits viewCount
abgerufen und an die Vorlage post.ejs
übergeben. Jetzt müssen wir sie nur noch in der Vorlage anzeigen.
Öffnen Sie views/post.ejs
und fügen Sie die Aufrufzahl im Bereich der Metadaten des Beitrags hinzu:
<article class="post-detail"> <h1><%= post.title %></h1> <small> <%= new Date(post.createdAt).toLocaleDateString() %> | Views: <%= viewCount %> </small> <div class="post-content"><%- post.content %></div> </article>
Blog-Homepage
Um die Aufrufzahl auch in der Beitragsliste auf der Homepage anzuzeigen, müssen wir einige Anpassungen am PostsService
und PostsController
vornehmen.
Aktualisieren Sie src/posts/posts.service.ts
:
Wir erstellen eine neue Methode, um die Liste der Beiträge mit ihren Aufrufzahlen abzurufen.
// src/posts/posts.service.ts @Injectable() export class PostsService { // ... constructor // Interface to combine Post with its view count public async findAllWithViewCount(trackingService: TrackingService): Promise<any[]> { const posts = await this.findAll(); const postIds = posts.map((post) => post.id); const viewCounts = await trackingService.getCountsByPostIds(postIds); return posts.map((post) => ({ ...post, viewCount: viewCounts[post.id] || 0, })); } // ... other methods }
Aktualisieren Sie src/posts/posts.controller.ts
:
Modifizieren Sie die root
-Methode, um unsere neu erstellte Service-Methode zu verwenden.
// src/posts/posts.controller.ts // ... export class PostsController { // ... constructor @Get() @Render('index') async root(@Request() req) { const posts = await this.postsService.findAllWithViewCount(this.trackingService); return { posts, user: req.session.user }; } // ... }
Aktualisieren Sie schließlich die Vorlage views/index.ejs
, um die Aufrufzahl anzuzeigen.
<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() %> | Views: <%= post.viewCount %></small> </article> <% }) %> </div>
Ausführen und Testen
Starten Sie Ihre Anwendung neu:
npm run start:dev
Öffnen Sie Ihren Browser und besuchen Sie die Seite: http://localhost:3000/
In der Blog-Liste sehen Sie neben jedem Artikel "Views: 0".
Klicken Sie, um die Detailseite eines beliebigen Artikels aufzurufen und aktualisieren Sie die Seite ein paar Mal. Sie werden feststellen, dass die Aufrufzahl für diesen Artikel entsprechend gestiegen ist.
Fazit
Sie haben nun erfolgreich ein Backend-Aufrufzählsystem zu Ihrem Nest.js-Blog hinzugefügt. Benutzeraufrufdaten liegen nun in Ihrer Hand.
Mit diesen Rohdaten können Sie tiefergehende Datenoperationen und -analysen durchführen. Zum Beispiel:
- Deduplizierung: Zählen Sie mehrere Aufrufe derselben IP-Adresse innerhalb eines bestimmten Zeitfensters (z. B. eines Tages) als einen einzigen Aufruf.
- Bot-Filterung: Identifizieren und filtern Sie Aufrufe von Suchmaschinen-Crawlern heraus, indem Sie den
User-Agent
analysieren. - Daten-Dashboard: Erstellen Sie eine private Seite zur Visualisierung von Artikeltrends mit Diagrammen.
Die Daten gehören Ihnen, daher überlassen wir diese Erkundungen Ihnen.
Wenn Ihr Blog auf Leapcell bereitgestellt wird, hat Leapcell seine Web-Analytics-Funktion bereits automatisch dafür aktiviert (was völlig kostenlos ist).
Leapcells Web-Analytics umfasst viele praktische und leistungsstarke Funktionen zur Besucheranalyse. Sie können es einfach verwenden, um grundlegende Analysen des Besucherverhaltens durchzuführen, ohne es selbst entwickeln zu müssen.
Frühere Tutorials:
- Einen großartigen Nest.js-Blog erstellen: Volltextsuche für Beiträge
- Einen großartigen Nest.js-Blog erstellen: Bilder hochladen
- Einen großartigen Nest.js-Blog erstellen: Kommentare beantworten
- Einen großartigen Nest.js-Blog erstellen: Kommentarsystem
- Einen großartigen Nest.js-Blog erstellen: Autorisierung hinzufügen
- Einen großartigen Nest.js-Blog erstellen: Benutzerverwaltung hinzufügen
- 10 Minuten vom ersten Code bis zum Live-Deployment: Ein super-schneller Nest.js Blog-Kurs