Build an Astrology Dashboard with Angular & Vedika API

Build an Astrology Dashboard with Angular and Vedika API
Published: March 13, 2026 | By Vedika Intelligence | Reading time: 16 minutes

Angular is the enterprise SPA framework of choice for teams that value TypeScript-first development, strong conventions, and a comprehensive built-in toolchain. Whether you're building a standalone astrology app or adding horoscope features to an existing enterprise portal, Angular's dependency injection, reactive forms, and RxJS integration make it exceptionally well-suited for working with REST APIs like Vedika.

This tutorial walks you through building a complete astrology dashboard — birth chart input, AI-powered predictions, zodiac horoscope display, and a live chatbot — using Angular 17 standalone components and Vedika API, the only astrology API with a built-in AI engine.

What You'll Build: A production-ready Angular 17 astrology dashboard with a birth chart reactive form, AI chatbot component, daily horoscope panel, and Angular Material UI. Complete with HttpClient service, RxJS error handling, lazy-loaded routing, and environment-based API key management.

Why Angular for Astrology Applications?

TypeScript-First

Full type safety for Vedika API response models — catch shape mismatches at compile time, not in production.

RxJS Built-In

Observables are a natural fit for streaming API responses, combining multiple astrology data streams, and reactive UI updates.

Angular Material

Polished datepicker, autocomplete, progress indicators, and card components — production UI without custom CSS.

Enterprise Scale

Dependency injection, lazy loading, and a structured module system make Angular dashboards maintainable as they grow.

Prerequisites

Step 1: Create the Angular Project

Generate a new standalone Angular 17 project with routing and SCSS styling:

# Create project with standalone components (Angular 17+ default) ng new astrology-dashboard \ --routing=true \ --style=scss \ --standalone=true cd astrology-dashboard # Add Angular Material ng add @angular/material # Install additional packages npm install

Environment Configuration

Store the API key in Angular's environment files — never in component code. For production, inject the key server-side and proxy through your own backend:

// src/environments/environment.ts (development) export const environment = { production: false, // In production: proxy calls through your backend; never expose real keys client-side vedikaApiUrl: 'http://localhost:4200/api-proxy', vedikaApiKey: 'YOUR_DEV_API_KEY' }; // src/environments/environment.prod.ts (production) export const environment = { production: true, vedikaApiUrl: '/api-proxy', // Backend proxy endpoint vedikaApiKey: '' // Empty — handled server-side };

Step 2: TypeScript Model Interfaces

Define strongly-typed interfaces for all Vedika API request and response shapes. This gives you autocomplete and compile-time safety throughout your components:

// src/app/models/astrology.models.ts export interface BirthDetails { datetime: string; // ISO-8601: "1990-06-15T14:30:00" latitude: number; longitude: number; timezone: string; // "+05:30" } export interface AstrologyQuery { question: string; birthDetails?: BirthDetails; system?: 'vedic' | 'western' | 'kp'; } export interface AstrologyResponse { answer: string; system: string; birthChart?: { planets: PlanetData[]; ascendant: string; currentDasha: string; }; usage?: { tokensUsed: number; cost: number; }; } export interface PlanetData { name: string; sign: string; house: number; degree: number; dignity: string; isRetrograde: boolean; } export interface ChatMessage { role: 'user' | 'assistant'; content: string; timestamp: Date; }

Step 3: Vedika API Service

Create an injectable service that encapsulates all API calls. The service uses HttpClient and returns typed Observables that components can subscribe to:

// src/app/services/vedika.service.ts import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError, shareReplay } from 'rxjs'; import { catchError, map, retry } from 'rxjs/operators'; import { environment } from '../../environments/environment'; import { AstrologyQuery, AstrologyResponse, BirthDetails } from '../models/astrology.models'; @Injectable({ providedIn: 'root' }) export class VedikaService { private readonly baseUrl = environment.vedikaApiUrl; private readonly headers = new HttpHeaders({ 'Content-Type': 'application/json', 'x-api-key': environment.vedikaApiKey }); constructor(private http: HttpClient) {} /** Ask any astrology question with optional birth details */ query(query: AstrologyQuery): Observable<AstrologyResponse> { return this.http.post<AstrologyResponse>( `${this.baseUrl}/api/vedika/chat`, query, { headers: this.headers } ).pipe( retry(1), catchError(this.handleError) ); } /** Get complete Vedic birth chart */ getBirthChart(details: BirthDetails): Observable<AstrologyResponse> { return this.query({ question: 'Generate my complete Vedic birth chart with all planetary positions, active yogas, and current dasha period.', birthDetails: details }).pipe( shareReplay(1) // Cache for component lifetime ); } /** Get daily horoscope for a zodiac sign */ getDailyHoroscope( zodiacSign: string, details?: BirthDetails ): Observable<AstrologyResponse> { return this.query({ question: `What is today's detailed horoscope for ${zodiacSign}? ` + 'Cover career, relationships, health, and wealth. Be specific and practical.', birthDetails: details }); } /** Get Kundali compatibility score */ getCompatibility( person1: BirthDetails, zodiacPair: string ): Observable<AstrologyResponse> { return this.query({ question: `Calculate Ashtakoot Guna Milan compatibility for ${zodiacPair}. ` + 'Provide the score out of 36 and detailed assessment.', birthDetails: person1 }); } private handleError(error: HttpErrorResponse): Observable<never> { let message = 'An error occurred'; if (error.status === 402) message = 'Insufficient API credits. Please top up your wallet.'; else if (error.status === 429) message = 'Rate limit reached. Please try again in a moment.'; else if (error.status === 401) message = 'Invalid API key. Check your configuration.'; else if (error.error?.message) message = error.error.message; return throwError(() => new Error(message)); } }

Step 4: Birth Chart Form Component

Build a reactive form that collects birth details with Angular Material components and client-side validation before calling the API:

// src/app/components/birth-chart-form/birth-chart-form.component.ts import { Component, OnInit, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatButtonModule } from '@angular/material/button'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { VedikaService } from '../../services/vedika.service'; import { AstrologyResponse } from '../../models/astrology.models'; @Component({ selector: 'app-birth-chart-form', standalone: true, imports: [ CommonModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, MatButtonModule, MatProgressSpinnerModule ], template: ` <mat-card class="birth-chart-card"> <mat-card-header> <mat-card-title>Enter Birth Details</mat-card-title> <mat-card-subtitle>Powered by Swiss Ephemeris astronomical precision</mat-card-subtitle> </mat-card-header> <mat-card-content> <form [formGroup]="birthForm" (ngSubmit)="onSubmit()"> <mat-form-field appearance="outline" class="full-width"> <mat-label>Date of Birth</mat-label> <input matInput [matDatepicker]="picker" formControlName="date" placeholder="DD/MM/YYYY"> <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker #picker></mat-datepicker> <mat-error *ngIf="birthForm.get('date')?.hasError('required')"> Birth date is required </mat-error> </mat-form-field> <mat-form-field appearance="outline" class="full-width"> <mat-label>Time of Birth (HH:MM)</mat-label> <input matInput formControlName="time" placeholder="14:30" pattern="^([01]?[0-9]|2[0-3]):[0-5][0-9]$"> <mat-error *ngIf="birthForm.get('time')?.hasError('pattern')"> Enter time in HH:MM format (e.g. 14:30) </mat-error> </mat-form-field> <mat-form-field appearance="outline"> <mat-label>Latitude</mat-label> <input matInput type="number" formControlName="latitude" placeholder="28.6139" step="0.0001"> </mat-form-field> <mat-form-field appearance="outline"> <mat-label>Longitude</mat-label> <input matInput type="number" formControlName="longitude" placeholder="77.2090" step="0.0001"> </mat-form-field> <button mat-raised-button color="warn" type="submit" [disabled]="birthForm.invalid || loading()"> <mat-spinner diameter="20" *ngIf="loading()"></mat-spinner> <span *ngIf="!loading()">Generate Birth Chart</span> </button> </form> </mat-card-content> </mat-card> <mat-card *ngIf="result()" class="result-card"> <mat-card-header> <mat-card-title>Your Vedic Birth Chart</mat-card-title> </mat-card-header> <mat-card-content> <p>{{ result()?.answer }}</p> </mat-card-content> </mat-card> <mat-error *ngIf="error()">{{ error() }}</mat-error> ` }) export class BirthChartFormComponent implements OnInit { birthForm!: FormGroup; loading = signal(false); result = signal<AstrologyResponse | null>(null); error = signal<string | null>(null); constructor( private fb: FormBuilder, private vedikaService: VedikaService ) {} ngOnInit() { this.birthForm = this.fb.group({ date: [null, Validators.required], time: ['12:00', [Validators.required, Validators.pattern(/^([01]?\d|2[0-3]):[0-5]\d$/)]], latitude: [28.6139, [Validators.required, Validators.min(-90), Validators.max(90)]], longitude: [77.2090, [Validators.required, Validators.min(-180), Validators.max(180)]] }); } onSubmit() { if (this.birthForm.invalid) return; const { date, time, latitude, longitude } = this.birthForm.value; const dateStr = date.toISOString().split('T')[0]; this.loading.set(true); this.error.set(null); this.vedikaService.getBirthChart({ datetime: `${dateStr}T${time}:00`, latitude, longitude, timezone: '+05:30' }).subscribe({ next: (res) => { this.result.set(res); this.loading.set(false); }, error: (err) => { this.error.set(err.message); this.loading.set(false); } }); } }

Ready to Build Your Angular Astrology Dashboard?

Try the FREE Sandbox — 65 mock endpoints, no credit card required. Plans from $12/month.

Explore the Sandbox

Step 5: Daily Horoscope Component

Create a zodiac picker component that loads horoscopes on selection using switchMap to cancel pending requests when the user changes their sign:

// src/app/components/daily-horoscope/daily-horoscope.component.ts import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Subject } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; import { VedikaService } from '../../services/vedika.service'; const ZODIAC_SIGNS = [ { name: 'Aries', symbol: '♈' }, { name: 'Taurus', symbol: '♉' }, { name: 'Gemini', symbol: '♊' }, { name: 'Cancer', symbol: '♋' }, { name: 'Leo', symbol: '♌' }, { name: 'Virgo', symbol: '♍' }, { name: 'Libra', symbol: '♎' }, { name: 'Scorpio', symbol: '♏' }, { name: 'Sagittarius', symbol: '♐' }, { name: 'Capricorn', symbol: '♑' }, { name: 'Aquarius', symbol: '♒' }, { name: 'Pisces', symbol: '♓' } ]; @Component({ selector: 'app-daily-horoscope', standalone: true, imports: [CommonModule], template: ` <div class="zodiac-grid"> <button *ngFor="let sign of zodiacSigns" class="zodiac-btn" [class.active]="selectedSign() === sign.name" (click)="selectSign(sign.name)"> <span class="zodiac-symbol">{{ sign.symbol }}</span> <span class="zodiac-name">{{ sign.name }}</span> </button> </div> <div class="horoscope-result" *ngIf="selectedSign()"> <div class="loading-overlay" *ngIf="loading()"> <div class="spinner"></div> <p>Consulting the stars...</p> </div> <div class="prediction" *ngIf="prediction() && !loading()"> <h3>Today's Horoscope — {{ selectedSign() }}</h3> <p>{{ prediction() }}</p> </div> </div> ` }) export class DailyHoroscopeComponent { zodiacSigns = ZODIAC_SIGNS; selectedSign = signal(''); prediction = signal(''); loading = signal(false); private signChange$ = new Subject<string>(); constructor(private vedikaService: VedikaService) { // switchMap cancels the previous in-flight request when sign changes this.signChange$.pipe( tap(() => { this.loading.set(true); this.prediction.set(''); }), switchMap(sign => this.vedikaService.getDailyHoroscope(sign)) ).subscribe({ next: (res) => { this.prediction.set(res.answer); this.loading.set(false); }, error: () => this.loading.set(false) }); } selectSign(sign: string) { this.selectedSign.set(sign); this.signChange$.next(sign); } }

Step 6: AI Chat Component

Build a conversational AI chatbot component that maintains message history and streams responses from Vedika AI:

// src/app/components/astro-chat/astro-chat.component.ts @Component({ selector: 'app-astro-chat', standalone: true, imports: [CommonModule, ReactiveFormsModule, MatInputModule, MatButtonModule, MatProgressSpinnerModule], template: ` <div class="chat-container" #chatContainer> <div class="messages"> <div *ngFor="let msg of messages()" [class]="'message ' + msg.role"> <div class="bubble">{{ msg.content }}</div> <span class="timestamp">{{ msg.timestamp | date:'HH:mm' }}</span> </div> <div class="typing" *ngIf="loading()"> <span></span><span></span><span></span> </div> </div> </div> <div class="input-row"> <mat-form-field appearance="outline" class="flex-grow"> <input matInput [formControl]="inputControl" placeholder="Ask about career, love, health, timing..." (keyup.enter)="sendMessage()"> </mat-form-field> <button mat-raised-button color="warn" (click)="sendMessage()" [disabled]="loading() || !inputControl.value?.trim()"> Ask Vedika AI </button> </div> ` }) export class AstroChatComponent { messages = signal<ChatMessage[]>([]); loading = signal(false); inputControl = new FormControl(''); @Input() birthDetails!: BirthDetails; constructor(private vedikaService: VedikaService) {} sendMessage() { const question = this.inputControl.value?.trim(); if (!question || this.loading()) return; this.messages.update(msgs => [...msgs, { role: 'user', content: question, timestamp: new Date() }]); this.inputControl.reset(); this.loading.set(true); this.vedikaService.query({ question, birthDetails: this.birthDetails }) .subscribe({ next: (res) => { this.messages.update(msgs => [...msgs, { role: 'assistant', content: res.answer, timestamp: new Date() }]); this.loading.set(false); }, error: (err) => { this.messages.update(msgs => [...msgs, { role: 'assistant', content: `Error: ${err.message}`, timestamp: new Date() }]); this.loading.set(false); } }); } }

Step 7: Application Routing

Configure lazy-loaded routes so Angular only loads what's needed — critical for dashboard performance:

// src/app/app.routes.ts import { Routes } from '@angular/router'; export const routes: Routes = [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', loadComponent: () => import('./pages/dashboard/dashboard.component') .then(m => m.DashboardComponent) }, { path: 'birth-chart', loadComponent: () => import('./components/birth-chart-form/birth-chart-form.component') .then(m => m.BirthChartFormComponent) }, { path: 'horoscope', loadComponent: () => import('./components/daily-horoscope/daily-horoscope.component') .then(m => m.DailyHoroscopeComponent) }, { path: 'chat', loadComponent: () => import('./components/astro-chat/astro-chat.component') .then(m => m.AstroChatComponent) } ];

Step 8: Proxy Configuration for Development

Set up Angular's dev server proxy to forward API calls to Vedika — avoids CORS issues during local development:

// proxy.conf.json { "/api-proxy": { "target": "https://api.vedika.io", "secure": true, "changeOrigin": true, "pathRewrite": { "^/api-proxy": "" }, "logLevel": "debug" } } // angular.json — add proxyConfig under serve options: { "serve": { "options": { "proxyConfig": "proxy.conf.json" } } }
# Start the dev server with proxy ng serve # Build production bundle ng build --configuration production # Run unit tests ng test # Generate component with CLI ng generate component components/panchang-calendar --standalone

Why Choose Vedika API Over Competitors?

AI Chatbot Included

One endpoint answers any natural language astrology question. Your Angular chatbot component becomes fully functional with a single API call — no complex orchestration.

140+ Calculations

Birth charts, ashtakavarga, compatibility, muhurta, numerology, panchang — comprehensive coverage that competitors simply don't match.

TypeScript-Friendly

Clean JSON responses that map directly to TypeScript interfaces. No XML parsing, no unusual data formats — just pure JSON.

MCP Server

The world's first astrology MCP server. Integrate Vedika directly with AI agent frameworks for the next generation of astrology applications.

30 Languages

Hindi, Tamil, Telugu, Bengali, Gujarati, Marathi, and 24 more. Build truly multilingual Angular dashboards with a single API.

$12/Month Starting

Transparent wallet-based billing. Start at $12/month Starter — far cheaper than competitors charging $19-49 for fewer endpoints.

Pricing

Vedika API pricing scales with your needs:

All plans include the full 140+ endpoint catalog, AI chatbot, and 30-language support. View full pricing details.

Conclusion

Angular's strong TypeScript integration, RxJS-first design, and Angular Material component library make it an excellent choice for building production astrology dashboards. The key patterns from this tutorial:

Next steps:

  1. Get your API key at vedika.io/dashboard
  2. Explore the free sandbox — 65 mock endpoints, zero cost
  3. Browse Angular-specific docs for all 140+ endpoints
  4. Add an interceptor to attach the API key header globally instead of per-service
  5. Implement an NgRx store for cross-component birth chart state sharing

About Vedika Intelligence: Vedika is the only B2B astrology API with an AI-powered chatbot engine, serving production applications worldwide. The Vedika Intelligence Engine enables natural language astrology queries with Swiss Ephemeris-verified precision, supporting both Vedic and Western astrology across 30 languages.

Try the #1 Astrology API for Angular Developers

140+ endpoints, Swiss Ephemeris precision, AI chatbot, 30 languages. Free sandbox — no credit card required.

Try Free Sandbox View Documentation