Safe Health Chat is a responsive, secure, health-focused chat application built with Next.js and Firebase. It leverages the Perplexity AI API to provide users with safe and informative answers to their health-related questions. The application features user authentication, persistent chat history, configurable information source filtering, integrated safety guardrails, and rich inline citations
- User Authentication: Secure sign-up and login using Firebase Authentication (email/password).
- Persistent Chat History: Conversations are automatically saved to Firestore for each user. Users can view, resume, and delete past conversations from a collapsible sidebar.
- Health-Focused AI Chat: A clean chat interface for interacting with the Perplexity AI API (
sonar-promodel). - Configurable Information Sources: Users can restrict the AI's search to specific trusted domains (e.g.,
medlineplus.gov). - Safety Guardrails: All user inputs and AI outputs are processed by an external guardrail service to ensure safety and appropriateness. Blocked messages are clearly indicated.
- Rich Inline Citations: AI responses include inline citations
[1][2]that, on click, reveal a popover with links to the source articles. - Developer Debug View: A collapsible panel shows the raw JSON for the last API request and response, aiding in development and debugging.
- Responsive Design: The UI is built with ShadCN UI and Tailwind CSS, ensuring a great experience on both desktop and mobile devices.
- Framework: Next.js (App Router)
- UI Library: React with TypeScript
- Styling: Tailwind CSS
- UI Components: ShadCN UI
- Backend: Firebase (Authentication, Firestore)
- AI Orchestration: Genkit
- AI Model: Perplexity AI
- State Management: React Hooks and component state.
The application follows a standard Next.js App Router structure, with Firebase integration for backend services.
.
├── src
│ ├── app
│ │ ├── api/guardrails/route.ts # Backend proxy for the guardrails service
│ │ ├── globals.css # Global styles and Tailwind directives
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # The main chat page component
│ │
│ ├── components
│ │ ├── ui/ # ShadCN UI components
│ │ ├── auth-dialog.tsx # Login/Sign-up modal
│ │ ├── chat-message.tsx # Renders a single chat message
│ │ ├── conversation-history.tsx # Sidebar for managing conversations
│ │ ├── debug-view.tsx # Displays API request/response JSON
│ │ ├── guardrail-result-dialog.tsx # Dialog to show raw guardrail JSON
│ │ ├── settings-dialog.tsx # Dialog for managing settings
│ │ └── user-menu.tsx # Dropdown menu for authenticated user
│ │
│ ├── firebase
│ │ ├── client-provider.tsx # Provides Firebase context to the client
│ │ ├── config.ts # Firebase project configuration
│ │ ├── firestore/ # Firestore-related hooks (useCollection)
│ │ └── index.ts # Firebase initialization and exports
│ │
│ ├── hooks
│ │ ├── use-settings.ts # Custom hook to manage and persist user settings
│ │ └── use-toast.ts # Custom hook for displaying toast notifications
│ │
│ ├── lib
│ │ └── types.ts # TypeScript type definitions
│ │
│ └── ai
│ ├── flows/chat.ts # Genkit flow to call the Perplexity AI API
│ └── genkit.ts # Genkit initialization and configuration
│
├── .env # Environment variables (for API keys)
├── firestore.rules # Firestore security rules
├── next.config.ts # Next.js configuration
└── tailwind.config.ts # Tailwind CSS configuration
src/app/page.tsx: The main client component that manages the chat state, user input, and orchestrates calls to the AI, guardrails, and Firestore.src/firebase/*: This directory contains all Firebase-related logic, including initialization, authentication hooks (useUser,useAuth), and real-time Firestore hooks (useCollection).src/components/conversation-history.tsx: A stateful component that fetches and displays the user's conversation history from Firestore, allowing them to switch between or delete chats.src/ai/flows/chat.ts: A server-side Genkit flow that acts as a secure wrapper around the Perplexity AI API. It receives the chat history from the client and forwards the request with the necessary credentials.src/hooks/use-settings.ts: This custom hook manages user-configurable settings. It syncs settings betweenlocalStorage(for logged-out users) and Firestore (for logged-in users).
To run this project locally, you need to configure your environment variables and set up Firebase.
Create a .env file in the root of the project and add your API keys and service URLs:
PERPLEXITY_API_KEY="your_perplexity_api_key_here"
NEXT_PUBLIC_FIREBASE_API_KEY="your_firebase_web_api_key"You can get a Perplexity API key from the Perplexity AI Developer Portal. The Firebase Web API key is available in your Firebase project's web app settings.
This project uses Firebase for authentication and Firestore as a database.
- Create a new Firebase project in the Firebase Console.
- Add a new Web App to your project.
- Copy the Firebase configuration object and paste it into
src/firebase/config.ts, replacing theNEXT_PUBLIC_FIREBASE_API_KEYenvironment variable value in your.envfile. - In the Firebase Console, go to Authentication -> Sign-in method and enable the Email/Password provider.
- Go to Firestore Database, create a database, and start in Production mode. You will apply security rules in the next step.
Copy the contents of the firestore.rules file from this project and paste them into the Rules tab of your Firestore database in the Firebase Console. Publish the changes.
Install the project dependencies using npm:
npm installStart the Next.js development server:
npm run devThe application will be available at http://localhost:9002.
The chat flow depends on a few explicit inputs and files. The project will not boot (or will crash at runtime) if any of these requirements are missing:
- Environment variables
PERPLEXITY_API_KEY: Required by the server-side Genkit flow insrc/ai/flows/chat.ts. If it is undefined the API call short-circuits withPERPLEXITY_API_KEY is not defined in environment variables.NEXT_PUBLIC_FIREBASE_API_KEY: Used bysrc/firebase/config.tsto initialize Firebase when running locally. The file throws an error when the value is absent, so populate it even if you rely on Firebase App Hosting defaults in production.
.envfile- Create the file in the project root and define the variables above. Any other Firebase values (auth domain, project ID, etc.) should be duplicated from your Firebase console when you customize
src/firebase/config.ts.
- Create the file in the project root and define the variables above. Any other Firebase values (auth domain, project ID, etc.) should be duplicated from your Firebase console when you customize
- Firebase configuration file
- The template in
src/firebase/config.tscontains a placeholder project. Replace theprojectId,appId,authDomain, and other fields with your project's metadata if you are not using Firebase App Hosting auto-configuration.
- The template in
- Firestore security rules
- The
firestore.rulesfile in the root of the repo must be deployed to the same Firebase project that the app talks to. Without those rules, reads and writes performed by hooks such asuseCollectionwill fail.
- The
- Firestore data layout
- User-specific data is stored under the
users/{uid}document. Theuse-settingshook storessearchDomains,systemPrompt, anduseGuardrailson that document, so the authenticated user needs write access there. - Conversations live at
users/{uid}/conversations/{conversationId}with fieldstitleandcreatedAt(serverTimestamp). Each conversation also contains amessagessub-collection with documents shaped likeChatMessageinsrc/lib/types.ts(role,content,createdAt, optionalreferences,guardrailResult, andisBlocked). Ensure your Firestore rules allow these nested writes.
- User-specific data is stored under the
- Guardrails service
src/app/actions.tsposts to the Cloud Run URL stored inGUARDRAILS_URL. Update that constant (or replace it with an environment variable) so it points at your guardrail endpoint.- The service must accept JSON payloads that include either
user_promptorllm_response(or both) and return a JSON object with at leastis_safe: boolean. Optional fields such asprompt_processedorllm_response_processedare honored if present; the UI stores the raw response inguardrailResultfor auditing.
/api/chatpayload contract- The frontend sends requests to
POST /api/chat(handled bysrc/app/api/chat/route.ts). The body must be valid JSON that follows theChatInputschema defined insrc/ai/flows/chat.ts: amessagesarray of{role: 'user' | 'assistant', content: string}objects, an optionalsystemstring, and an optionalsearch_domain_filterstring array. The server rejects requests that cannot be parsed or do not contain at least one non-empty user message.
- The frontend sends requests to
You can deploy this project to Vercel in just a few steps:
-
Create a Vercel Project
- Sign in to Vercel and click Add New... → Project.
- Import this GitHub repository (or your fork) and select the
mainbranch.
-
Configure Environment Variables
- In the new project's Settings → Environment Variables, add the same variables defined in your local
.envfile (e.g.,PERPLEXITY_API_KEY, Firebase config values, and any custom guardrail URLs). - Repeat the variables for each environment (
Production,Preview, and optionallyDevelopment) so your previews behave like production.
- In the new project's Settings → Environment Variables, add the same variables defined in your local
-
Set the Build Configuration
- Vercel automatically detects Next.js projects. Ensure the build command is
npm run buildand the output directory remains the default.next. - If you use a custom Node.js version locally, set Environment → General → Node.js Version to match (the project currently targets Node 18 LTS).
- Vercel automatically detects Next.js projects. Ensure the build command is
-
Link Firebase
- Add your Firebase project's authorized domains by navigating to Firebase Console → Authentication → Settings → Authorized Domains and adding your
*.vercel.appdomain so hosted builds can sign in users.
- Add your Firebase project's authorized domains by navigating to Firebase Console → Authentication → Settings → Authorized Domains and adding your
-
Trigger the Deployment
- Click Deploy in Vercel. Each push to the selected branch (and pull requests) will automatically create new deployments. Use the Preview URL to test before promoting to production.
Once deployed, Vercel will handle SSL, CDN caching, and automatic scaling, so no additional infrastructure is required.
- A user signs up or logs in via the
AuthDialog. TheuseUserhook provides their authentication state throughout the app. - The
ConversationHistorycomponent fetches the user's past conversations from/users/{userId}/conversationsin Firestore. - The user types a message in the
Textareaon the main page. If no conversation is active, a new one is created in Firestore. - On submission, the user's prompt is optionally sent to the guardrails service.
- The safe prompt is written as a new document to
/users/{userId}/conversations/{convoId}/messages. - The
safeHealthChatGenkit flow is invoked with the current conversation history. - The flow calls the Perplexity AI API. The AI's response is optionally sent to the guardrails service.
- The safe AI response is written as another new message to Firestore.
- The
useCollectionhook listens for these changes in real-time, and the UI updates automatically to display the new messages.