» Node.js: Building Event-Driven Microservices with Kafka » 3. Consumer: Trend Service » 3.4 Service Integration

Service Integration

Let‘s call the Trend Service to show trends from the bookstore Web Service.

Add config items in src/web/infrastructure/config/config.ts:

@@ -15,10 +15,15 @@ interface MQConfig {
   topic: string;
 }
 
+export interface RemoteServiceConfig {
+  trend_url: string;
+}
+
 export interface Config {
   app: ApplicationConfig;
   db: DBConfig;
   mq: MQConfig;
+  remote: RemoteServiceConfig;
 }
 
 export function parseConfig(filename: string): Config {

Put in config values in src/web/config.json:

@@ -10,5 +10,8 @@
   "mq": {
     "brokers": ["localhost:9094"],
     "topic": "lr-book-searches"
+  },
+  "remote": {
+    "trend_url": "http://localhost:3001/trends"
   }
 }

Call the trend service in src/web/application/executor/book_operator.ts:

@@ -1,5 +1,5 @@
 import { BookManager } from "../../domain/gateway";
-import { Book } from "../../../domain/model";
+import { Book, Trend } from "../../../domain/model";
 import { MQHelper } from "../../infrastructure/mq";
 
 export class BookOperator {
@@ -25,4 +25,10 @@ export class BookOperator {
     }
     return books;
   }
+
+  async getTrends(trendURL: string): Promise<Trend[]> {
+    const resp = await fetch(trendURL);
+    const trends: Trend[] = await resp.json();
+    return trends;
+  }
 }

Integrate the trend service results in src/web/adapter/router.ts:

@@ -1,15 +1,18 @@
 import express, { Request, Response } from "express";
 import { engine } from "express-handlebars";
 
-import { Book } from "../../domain/model";
+import { Book, Trend } from "../../domain/model";
 import { BookOperator } from "../application/executor";
 import { WireHelper } from "../application";
+import { RemoteServiceConfig } from "../infrastructure/config";
 
 class RestHandler {
   private bookOperator: BookOperator;
+  private remote: RemoteServiceConfig;
 
-  constructor(bookOperator: BookOperator) {
+  constructor(bookOperator: BookOperator, r: RemoteServiceConfig) {
     this.bookOperator = bookOperator;
+    this.remote = r;
   }
 
   public async indexPage(req: Request, res: Response): Promise<void> {
@@ -21,11 +24,19 @@ class RestHandler {
       console.warn(`Failed to get books: ${err}`);
       books = [];
     }
+    let trends: Trend[];
+    try {
+      trends = await this.bookOperator.getTrends(this.remote.trend_url);
+    } catch (err) {
+      console.warn(`Failed to get trends: ${err}`);
+      trends = [];
+    }
     // Render the 'index.handlebars' template, passing data to it
     res.render("index", {
       layout: false,
       title: "LiteRank Book Store",
       books,
+      trends,
       q,
     });
   }
@@ -61,9 +72,13 @@ class RestHandler {
 }
 
 // Create router
-function MakeRouter(wireHelper: WireHelper): express.Router {
+function MakeRouter(
+  remote: RemoteServiceConfig,
+  wireHelper: WireHelper
+): express.Router {
   const restHandler = new RestHandler(
-    new BookOperator(wireHelper.bookManager(), wireHelper.messageQueueHelper())
+    new BookOperator(wireHelper.bookManager(), wireHelper.messageQueueHelper()),
+    remote
   );
 
   const router = express.Router();
@@ -75,6 +90,7 @@ function MakeRouter(wireHelper: WireHelper): express.Router {
 
 export function InitApp(
   templates_dir: string,
+  remote: RemoteServiceConfig,
   wireHelper: WireHelper
 ): express.Express {
   const app = express();
@@ -88,7 +104,7 @@ export function InitApp(
   // Set the directory for template files
   app.set("views", templates_dir);
 
-  const r = MakeRouter(wireHelper);
+  const r = MakeRouter(remote, wireHelper);
   app.use("", r);
   return app;
 }

Pass in config items in src/web/app.ts:

@@ -6,7 +6,7 @@ const config_filename = "src/web/config.json";
 
 const c = parseConfig(config_filename);
 const wireHelper = new WireHelper(c);
-const app = InitApp(c.app.templates_dir, wireHelper);
+const app = InitApp(c.app.templates_dir, c.remote, wireHelper);
 
 app.listen(c.app.port, () => {
   console.log(`Running on port ${c.app.port}`);

Render the trends in src/web/adapter/templates/index.handlebars:

@@ -41,17 +41,19 @@
         <!-- Trends Section -->
         <div class="mb-8">
             <h2 class="text-2xl font-bold mb-4">Trends</h2>
-            <div class="grid grid-cols-3 gap-4">
-                <!-- Trend items can be dynamically generated here -->
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 1
-                </div>
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 2
-                </div>
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 3
+            <div class="grid grid-cols-5 gap-2">
+                {{#each trends}}
+                <div class="bg-white p-4 rounded-md border-gray-300 shadow mt-2">
+                    <div>#<b><a href="/?q={{this.query}}">{{this.query}}</a></b></div>
+                    {{#each this.books }}
+                    <div class="font-serif border-t py-1 mt-1">
+                        {{this.title}}
+                        <span class="italic text-sm text-gray-500">by</span>
+                        <span class="font-mono italic text-sm">{{this.author}}</span>
+                    </div>
+                    {{/each}}
                 </div>
+                {{/each}}
             </div>
         </div>

Restart your web and trend services, you should see something like this on the index page.

Trends

First service is integrated successfully! 🎉!