Build Your Own Chatbot with AWS Bedrock and NestJS: A Step-by-Step Guide

Wiljeder FilhoWiljeder Filho
4 min read

Integrating a Generative AI model from AWS Bedrock into a NestJS-based chatbot API offers a powerful approach to building interactive applications. This guide simplifies the process, guiding you step-by-step from setting up your environment to creating a fully functional chatbot API in NestJS using TypeScript.

Step-by-Step Guide: Using a base model from AWS Bedrock with NestJS and AWS SDK

Prerequisites:

  • AWS Account with access to AWS Bedrock.

  • Node.js and yarn (…or npm) installed on your local machine.

  • Basic understanding of NestJS, TypeScript, and RESTful APIs.

Step 1: Select a base model from AWS Bedrock

  1. Access AWS Bedrock:

    • Log into your AWS Management Console and search for AWS Bedrock. It provides various foundation models like Anthropic, Stability AI, and others.
  2. Request access and get Model ID:

    • Browse the available models in the AWS Bedrock console.

    • Select which one you’ll be using based on your necessities and request access to it.

    • Note the model's ID that you wish to use (e.g., amazon.titan-text-premier-v1:0 or a similar identifier). You can refer to this page to find it.

Step 2: Create a NestJS project

  1. Create a New NestJS Project:

     npm i -g @nestjs/cli
     nest new nest-bedrock-chatbot
     cd nest-bedrock-chatbot
    
  2. Install Required Dependencies: We need AWS SDK to interact with bedrock and dotenv to secure our credentials.

     yarn add @aws-sdk/client-bedrock-runtime dotenv
    

Step 3: Set up environment variables

  1. Create a .env file in your project root with the following details:

     BEDROCK_MODEL_ID=bedrock-model-id
     AWS_ACCESS_KEY_ID=your-aws-access-key
     AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key
     AWS_REGION=your-aws-region
     CUSTOM_PROMPT="This is where you tell the model how to act and how to answer."
    

    Refer to this documentation regarding the creation of access keys.

  2. Configure dotenv (src/main.ts):

     import { NestFactory } from '@nestjs/core';
     import { AppModule } from './app.module';
     import * as dotenv from 'dotenv';
    
     dotenv.config();
    
     async function bootstrap() {
       const app = await NestFactory.create(AppModule);
       await app.listen(3000);
     }
     bootstrap();
    

Step 4: Create the service to communicate with AWS Bedrock

Create a new service in NestJS that will handle requests to the AWS Bedrock endpoint.

  1. Generate the Service:

     nest g service bedrock
    
  2. Implement the Bedrock Service (src/bedrock/bedrock.service.ts):

     import { Injectable } from '@nestjs/common';
     import {
       BedrockRuntimeClient,
       InvokeModelCommand,
     } from '@aws-sdk/client-bedrock-runtime';
    
     @Injectable()
     export class BedrockService {
       private readonly client: BedrockRuntimeClient;
       private readonly customPrompt: string;
    
       constructor() {
         this.client = new BedrockRuntimeClient({
           region: process.env.AWS_REGION,
           credentials: {
             accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
             secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
           },
         });
         this.customPrompt = process.env.CUSTOM_PROMPT || '';
       }
    
       async getResponseFromModel(userInput: string): Promise<string> {
         const body = {
           inputText: `${this.customPrompt}\n${userInput}`,
         };
    
         try {
           const command = new InvokeModelCommand({
             modelId: process.env.BEDROCK_MODEL_ID,
             body: JSON.stringify(body),
           });
    
           const response = await this.client.send(command);
    
           return this.extractResponse(response.body);
         } catch (error) {
           console.error('Error interacting with AWS Bedrock:', error);
           throw new Error('Failed to get a response from the model');
         }
       }
    
       extractResponse(data: any): string {
         const responseBody = new TextDecoder().decode(data);
         const parsedBody = JSON.parse(responseBody);
         return parsedBody.results[0].outputText;
       }
     }
    

    Note that the structure of the command body depends on the model.

Step 5: Create the controller to handle requests

  1. Generate the Controller:

     nest g controller chatbot
    
  2. Implement the Chatbot Controller (src/chatbot/chatbot.controller.ts):
    This endpoint will require a body such as { “prompt”: “Who are you?“ } and will return a json { “answer”: “I’m a chatbot” }.

     import { Body, Controller, Post } from '@nestjs/common';
     import { BedrockService } from 'src/bedrock/bedrock.service';
    
     @Controller('chatbot')
     export class ChatbotController {
       constructor(private readonly bedrockService: BedrockService) {}
    
       @Post('ask')
       async askModel(
         @Body() { prompt }: { prompt: string },
       ): Promise<{ answer: string }> {
         const answer = await this.bedrockService.getResponseFromModel(prompt);
         return { answer };
       }
     }
    

Step 6: Test Your Chatbot API

  1. Start the NestJS Application:

     npm run start:dev
    
  2. Test the API Using Postman or Curl:

    Make a POST request to http://localhost:3000/chatbot/ask

     curl --location 'localhost:3000/chatbot/ask' \
     --header 'Content-Type: application/json' \
     --data '{
         "prompt": "Who are you?"
     }'
    

    You should receive a response with the chatbot’s answer based on your custom prompt and user input.

Additional Tips:

  • Input Validation: Use NestJS DTOs, ValidationPipe, class-transformer, and class-validator to ensure clean inputs.

  • Error Handling: Enhance error handling for network issues and invalid Bedrock responses.

  • Custom Prompts: Adjust CUSTOM_PROMPT dynamically based on user inputs, allowing for more personalized chatbot interactions.

This guide has walked you through integrating AWS Bedrock models into a NestJS-based chatbot API, allowing you to harness the power of Generative AI without the complexity of deploying custom models. By customizing prompts and refining input/output handling, you can create highly interactive, responsive chatbots tailored to your needs.

Shoutout to Vithor Varela for inspiring me!

0
Subscribe to my newsletter

Read articles from Wiljeder Filho directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Wiljeder Filho
Wiljeder Filho

Coding stuff