--- title: Build a RAG chatbot with Astro, Postgres, and LlamaIndex subtitle: A step-by-step guide for building a RAG chatbot in an Astro application with LlamaIndex and Postgres author: rishi-raj-jain enableTableOfContents: true createdAt: '2024-06-11T00:00:00.000Z' updatedOn: '2024-06-11T00:00:00.000Z' --- ## Prerequisites To follow the steps in this guide, you will need the following: - [Node.js 18](https://nodejs.org/en) or later - A [Neon](https://console.neon.tech/signup) account - An [OpenAI](https://platform.openai.com/api-keys) account - An [AWS](https://aws.amazon.com/free) account ## Steps - [Generate the OpenAI API token](#generate-the-openai-api-token) - [Provisioning a Serverless Postgres](#provisioning-a-serverless-postgres) - [Create a new Astro application](#create-a-new-astro-application) - [Add Tailwind CSS to the application](#add-tailwind-css-to-the-application) - [Integrate React in your Astro project](#integrate-react-in-your-astro-project) - [Enabling Server Side Rendering in Astro using Node.js Adapter](#enabling-server-side-rendering-in-astro-using-nodejs-adapter) - [Setting up a Postgres database connection](#setting-up-a-postgres-database-connection) - [Define the Astro application routes](#define-the-astro-application-routes) - [Build Conversation User Interface using Vercel AI SDK](#build-conversation-user-interface-using-vercel-ai-sdk) - [Build UI to update Chabot’s Knowledge](#build-ui-to-update-chabots-knowledge) - [Build an entrypoint React component](#build-an-entrypoint-react-component) - [Initialize Postgres Vector Store in LlamaIndex](#initialize-postgres-vector-store-in-llamaindex) - [Build the Chat API Endpoint](#build-the-chat-api-endpoint) - [Build the Learn API Endpoint](#build-the-learn-api-endpoint) - [Dockerize your Astro application](#dockerize-your-astro-application) - [Deploy your Astro application to Amazon ECS](#deploy-your-astro-application-to-amazon-ecs) - [Create Amazon ECR private repository](#create-amazon-ecr-private-repository) - [Configure your IAM Roles](#configure-your-iam-roles) - [Create an Amazon ECS Task Definition](#create-an-amazon-ecs-task-definition) - [Create an Amazon ECS Cluster](#create-an-amazon-ecs-cluster) - [Create an Amazon ECS Service](#create-an-amazon-ecs-service) - [Create Access Keys for IAM users](#create-access-keys-for-iam-users) - [Configure GitHub Actions for Continuous Deployment (CD) Workflow](#configure-github-actions-for-continuous-deployment-cd-workflow) ## Generate the OpenAI API token To create vector embeddings, you will use OpenAI API with LlamaIndex. To set up OpenAI, do the following: - Log in to your [OpenAI](https://platform.openai.com/) account. - Navigate to the [API Keys](https://platform.openai.com/api-keys) page. - Enter a name for your token and click the **Create new secret key** button to generate a new key. - Copy and securely store this token for later use as the **OPENAI_API_KEY** environment variable. ## Provisioning a Serverless Postgres Using a serverless Postgres database lets you scale compute resources down to zero, which helps you save on compute costs. To get started, go to the [Neon Console](https://console.neon.tech/app/projects) and create a project. You will then be presented with a dialog that provides a connection string of your database. You can enable the **Connection pooling** toggle for a pooled connection string. ![](/guides/images/chatbot-astro-postgres-llamaindex/c200c4ed-f62d-469c-9690-c572c482c536.png) All Neon connection strings have the following format: ```bash postgres://:@.neon.tech:/?sslmode=require&channel_binding=require ``` - `user` is the database user. - `password` is the database user’s password. - `endpoint_hostname` is the host with `neon.tech` as the [top-level domain (TLD)](https://www.cloudflare.com/en-gb/learning/dns/top-level-domain/). - `port` is the Neon port number. The default port number is 5432. - `dbname` is the name of the database. `neondb` is the default database created with a Neon project if you do not define your own database. - `?sslmode=require&channel_binding=require` optional query parameters that enforce [SSL](https://www.cloudflare.com/en-gb/learning/ssl/what-is-ssl/) mode and channel binding for better security when connecting to the Postgres instance. Save the connection string somewhere safe. It will be used to set the **POSTGRES_URL** variable later. ## Create a new Astro application Let’s get started by creating a new Astro project. Open your terminal and run the following command: ```bash npm create astro@latest my-app ``` `npm create astro` is the recommended way to scaffold an Astro project quickly. When prompted, choose: - `Empty` when asked how to start the new project. - `Yes` when asked if you plan to write Typescript. - `Strict` when asked how strict Typescript should be. - `Yes` when prompted to install dependencies. - `Yes` when prompted to initialize a git repository. Once that’s done, change the project directory and start the app: ```bash cd my-app npm run dev ``` The app should be running on [localhost:4321](http://localhost:4321/). Let's close the development server for now. Next, execute the command in your terminal window below to install the necessary libraries and packages for building the application: ```bash npm install dotenv ai llamaindex@0.3.4 ``` The command installs the following packages: - `dotenv`: A library for handling environment variables. - `ai`: A library to build AI-powered streaming text and chat UIs. - `llamaindex`: A data framework for creating LLM applications. Next, make the following additions in your `astro.config.mjs` file to populate the environment variables and make them accessible via `process.env` object: ```tsx // File: astro.config.mjs import 'dotenv/config'; // [!code ++] import { defineConfig } from 'astro/config'; // https://astro.build/config export default defineConfig({}); ``` Then, add the following code to your `tsconfig.json` file to make relative imports within the project easier: ```json { "extends": "astro/tsconfigs/base", "compilerOptions": { // [!code ++] "baseUrl": ".", // [!code ++] "paths": { // [!code ++] "@/*": ["src/*"] // [!code ++] } // [!code ++] } // [!code ++] } ``` Let's move on to integrating Tailwind CSS into the Astro application. ### Add Tailwind CSS to the application For styling the app, you will be using Tailwind CSS. Install and set up Tailwind at the root of our project's directory by running: ```bash npx astro add tailwind ``` When prompted, choose: - `Yes` when prompted to install the Tailwind dependencies. - `Yes` when prompted to generate a minimal `tailwind.config.mjs` file. - `Yes` when prompted to make changes to Astro configuration file. After making the selections outlined above, the command finishes integrating TailwindCSS into your Astro project and installs the following dependencies: - `tailwindcss`: TailwindCSS as a package to scan your project files and generate corresponding styles. - `@astrojs/tailwind`: An adapter that brings Tailwind's utility CSS classes to every `.astro` file and framework component in your project. Let's move on to integrating React into the Astro application. ### Integrate React in your Astro project To prototype the reactive user interface quickly, you will use React as the library with Astro. In your terminal window, execute the following command: ```bash npx astro add react ``` `npx` allows us to execute npm package binaries without having to install `npm` globally. When prompted, choose the following: - `Yes` to install the React dependencies. - `Yes` to make changes to Astro configuration file. - `Yes` to make changes to `tsconfig.json` file. Let's move on to enabling server-side rendering in the Astro application. ### Enabling Server Side Rendering in Astro using Node.js Adapter To interact with the chatbot over a server-side API, you will enable server-side rendering in your Astro application. Execute the following command in your terminal: ```bash npx astro add node ``` When prompted, choose: - `Yes` to install the Node.js dependencies. - `Yes` to make changes to Astro configuration file. After making the selections outlined above, the command finishes integrating the Node.js adapter into your Astro project and installs the following dependency: - `@astrojs/node`: The adapter that allows your Astro SSR site to deploy to Node targets. Let's move on to loading the Postgres URL through an environment variable in the Astro application. ## Setting up a Postgres database connection Create an `.env` file in the root directory of your project with the following environment variable to initiate the setup of a database connection: ```bash # Neon Postgres Pooled Connection URL POSTGRES_URL="postgres://:@.neon.tech:/?sslmode=require&channel_binding=require&channel_binding=require" ``` The file, `.env`, should be kept secret and not included in your Git history. Ensure that `.env` is added to the `.gitignore` file in your project. ## Define the Astro application routes The structure below is what our `src/pages` directory will look like at the end of this section: ```bash ├── index.astro ├── api/ └───── chat.ts └───── learn.ts ``` - `index.astro` will serve responses with dynamically created HTML to incoming requests at the index route. - `api/chat.ts` will serve responses as an API Endpoint to incoming requests at `/api/chat`. - `api/learn.ts` will serve responses as an API Endpoint to incoming requests at `/api/learn`. ### Build Conversation User Interface using Vercel AI SDK Inside the `src` directory, create a `Chat.jsx` file with the following code: ```tsx // File: src/Chat.jsx import { useChat } from 'ai/react'; export default function () { const { messages, handleSubmit, input, handleInputChange } = useChat(); return (
Chat {messages.map((message, i) => (
{message.content}
))}
); } ``` The code above does the following: - Imports the `useChat` hook by `ai` SDK to manage the conversation between the user and the chatbot. It simplifies the management of the conversation between the user and the chatbot. By default, it posts to the `/api/chat` endpoint to obtain responses from the chatbot. - Exports a React component that returns a form containing an `` element to allow users to enter their query. - Creates a conversation UI looping over the set of messages (managed by the AI SDK). Now, let’s create a component that will allow the user to add text to the chatbot's knowledge. ### Build UI to update Chabot’s Knowledge Inside the `src` directory, create a `Learn.jsx` file with the following code: ```jsx // File: src/Learn.jsx import { useState } from 'react'; export default function () { const [message, setMessage] = useState(); return (
{ e.preventDefault(); if (message) { fetch('/api/learn', { method: 'POST', body: JSON.stringify({ message }), headers: { 'Content-Type': 'application/json' }, }); } }} > Learn