What We\'re Building
We\'ll create a full-featured astrology app with these capabilities:
- Birth chart calculator with planetary positions
- Kundali matching for relationships
- Daily horoscope generator
- AI-powered astrology chatbot
- User authentication and profile management
- Mobile-responsive design
Tech Stack
Frontend
- • React 18 / Next.js 14
- • TailwindCSS for styling
- • Axios for API calls
- • React Query for caching
Backend
- • Python Flask API
- • SQLite/PostgreSQL
- • JWT authentication
- • Vedika API integration
Prerequisites
- Basic knowledge of JavaScript/React
- Basic Python experience
- Node.js 18+ and Python 3.8+ installed
- Code editor (VS Code recommended)
- Vedika API key (get free at vedika.io/dashboard)
Step 1: Project Setup
Create Project Structure
# Create main project directory
mkdir astrology-app
cd astrology-app
# Create frontend (React)
npx create-next-app@latest frontend
cd frontend
npm install axios react-query @tanstack/react-query
# Create backend (Flask)
cd ..
mkdir backend
cd backend
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install flask flask-cors flask-jwt-extended requests python-dotenv
Environment Variables
Create .env files in both frontend and backend:
backend/.env:
VEDIKA_API_KEY=your_vedika_api_key_here
VEDIKA_BASE_URL=https://api.vedika.io
SECRET_KEY=your_secret_key_for_jwt
DATABASE_URL=sqlite:///astrology.db
frontend/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:5000
Step 2: React Frontend
Create Birth Chart Component
frontend/components/BirthChartCalculator.jsx:
import { useState } from 'react';
import axios from 'axios';
export default function BirthChartCalculator() {
const [formData, setFormData] = useState({
name: '',
date: '',
time: '',
city: ''
});
const [chart, setChart] = useState(null);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
// Get coordinates from city name
const geocode = await axios.get(
`https://nominatim.openstreetmap.org/search?q=${formData.city}&format=json`
);
const location = geocode.data[0];
// Call our backend API
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/api/birth-chart`,
{
datetime: `${formData.date}T${formData.time}:00`,
latitude: parseFloat(location.lat),
longitude: parseFloat(location.lon),
timezone: '+05:30' // IST; derive the offset from the city for other places
}
);
setChart(response.data);
} catch (error) {
console.error('Error:', error);
alert('Failed to generate chart');
} finally {
setLoading(false);
}
};
return (
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">Birth Chart Calculator</h1>
<form onSubmit={handleSubmit} className="bg-white p-6 rounded-lg shadow mb-6">
<div className="grid md:grid-cols-2 gap-4">
<div>
<label className="block mb-2 font-semibold">Name</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
className="w-full border p-2 rounded"
required
/>
</div>
<div>
<label className="block mb-2 font-semibold">Birth Date</label>
<input
type="date"
value={formData.date}
onChange={(e) => setFormData({...formData, date: e.target.value})}
className="w-full border p-2 rounded"
required
/>
</div>
<div>
<label className="block mb-2 font-semibold">Birth Time</label>
<input
type="time"
value={formData.time}
onChange={(e) => setFormData({...formData, time: e.target.value})}
className="w-full border p-2 rounded"
required
/>
</div>
<div>
<label className="block mb-2 font-semibold">Birth City</label>
<input
type="text"
value={formData.city}
onChange={(e) => setFormData({...formData, city: e.target.value})}
className="w-full border p-2 rounded"
placeholder="New Delhi, India"
required
/>
</div>
</div>
<button
type="submit"
className="mt-4 bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-indigo-700 font-semibold"
disabled={loading}
>
{loading ? 'Calculating...' : 'Generate Birth Chart'}
</button>
</form>
{chart && (
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-2xl font-bold mb-4">Your Birth Chart</h2>
<div className="grid md:grid-cols-2 gap-6">
<div>
<h3 className="font-bold mb-2 text-lg">Planetary Positions</h3>
<ul className="space-y-2">
{chart.planets.map((planet) => (
<li key={planet.name} className="flex justify-between">
<span className="font-semibold">{planet.name}:</span>
<span>{planet.sign} ({planet.degree.toFixed(2)}°)</span>
</li>
))}
</ul>
</div>
<div>
<h3 className="font-bold mb-2 text-lg">Houses</h3>
<ul className="space-y-2">
{chart.houses.slice(0, 4).map((house) => (
<li key={house.number} className="flex justify-between">
<span className="font-semibold">House {house.number}:</span>
<span>{house.sign}</span>
</li>
))}
</ul>
</div>
</div>
<div className="mt-6">
<h3 className="font-bold mb-2 text-lg">Signs</h3>
<div className="bg-yellow-50 p-3 rounded">
<p>Ascendant: {chart.ascendant.sign}</p>
<p>Sun sign: {chart.sunSign}</p>
<p>Moon sign: {chart.moonSign}</p>
<p>Moon nakshatra: {chart.nakshatra}</p>
</div>
</div>
</div>
)}
</div>
);
}
Create AI Chatbot Component
frontend/components/AstrologyChatbot.jsx:
import { useState, useRef, useEffect } from 'react';
import axios from 'axios';
export default function AstrologyChatbot({ birthData }) {
const [messages, setMessages] = useState([
{ role: 'assistant', content: 'Ask me anything about your birth chart!' }
]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const messagesEnd = useRef(null);
const handleSend = async () => {
if (!input.trim()) return;
const userMessage = { role: 'user', content: input };
setMessages([...messages, userMessage]);
setInput('');
setLoading(true);
try {
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/api/ai-chat`,
{
birth_data: birthData,
question: input
}
);
const aiMessage = {
role: 'assistant',
content: response.data.response
};
setMessages(prev => [...prev, aiMessage]);
} catch (error) {
console.error('Error:', error);
setMessages(prev => [...prev, {
role: 'assistant',
content: 'Sorry, I encountered an error. Please try again.'
}]);
} finally {
setLoading(false);
}
};
useEffect(() => {
messagesEnd.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="bg-white rounded-lg shadow p-4 max-w-2xl mx-auto">
<h2 className="text-2xl font-bold mb-4">AI Astrology Assistant</h2>
<div className="h-96 overflow-y-auto mb-4 space-y-3">
{messages.map((msg, idx) => (
<div
key={idx}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-xs md:max-w-md p-3 rounded-lg ${
msg.role === 'user'
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-900'
}`}
>
{msg.content}
</div>
</div>
))}
<div ref={messagesEnd} />
</div>
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="Ask: When will I get married?"
className="flex-1 border p-2 rounded"
disabled={loading}
/>
<button
onClick={handleSend}
className="bg-indigo-600 text-white px-4 py-2 rounded hover:bg-indigo-700"
disabled={loading}
>
{loading ? '...' : 'Send'}
</button>
</div>
</div>
);
}
Step 3: Flask Backend
Create Main API Server
backend/app.py:
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
import os
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
CORS(app)
VEDIKA_API_KEY = os.getenv('VEDIKA_API_KEY')
VEDIKA_BASE_URL = os.getenv('VEDIKA_BASE_URL')
@app.route('/api/birth-chart', methods=['POST'])
def birth_chart():
"""Generate birth chart"""
data = request.json
try:
# Call Vedika API
response = requests.post(
f'{VEDIKA_BASE_URL}/v2/astrology/birth-chart',
json=data,
headers={
'Authorization': f'Bearer {VEDIKA_API_KEY}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return jsonify(response.json()['data'])
except requests.exceptions.RequestException as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/kundali-matching', methods=['POST'])
def kundali_matching():
"""Match two kundalis"""
data = request.json
try:
response = requests.post(
f'{VEDIKA_BASE_URL}/v2/astrology/kundali-matching',
json=data,
headers={
'Authorization': f'Bearer {VEDIKA_API_KEY}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return jsonify(response.json()['data'])
except requests.exceptions.RequestException as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/ai-chat', methods=['POST'])
def ai_chat():
"""AI chatbot for astrology queries"""
data = request.json
try:
response = requests.post(
f'{VEDIKA_BASE_URL}/api/v1/astrology/query',
json={'question': data['question'], 'birthDetails': data['birth_data']},
headers={
'Authorization': f'Bearer {VEDIKA_API_KEY}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return jsonify(response.json())
except requests.exceptions.RequestException as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/daily-horoscope/<sign>', methods=['GET'])
def daily_horoscope(sign):
"""Get daily horoscope for zodiac sign"""
try:
response = requests.get(
f'{VEDIKA_BASE_URL}/v2/astrology/horoscope/{sign}',
headers={
'Authorization': f'Bearer {VEDIKA_API_KEY}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return jsonify(response.json()['data'])
except requests.exceptions.RequestException as e:
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(debug=True, port=5000)
Step 4: Mobile Integration
React Native Version
For mobile apps, use React Native with similar components:
npx react-native init AstrologyAppMobile
cd AstrologyAppMobile
npm install axios @react-navigation/native
Flutter Alternative
// lib/services/vedika_api.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
class VedikaAPI {
final String apiKey;
final String baseUrl = 'https://api.vedika.io';
VedikaAPI(this.apiKey);
Future<Map<String, dynamic>> getBirthChart(Map<String, dynamic> birthData) async {
final response = await http.post(
Uri.parse('$baseUrl/v2/astrology/birth-chart'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json'
},
body: jsonEncode(birthData)
);
if (response.statusCode == 200) {
return jsonDecode(response.body)['data'];
} else {
throw Exception('Failed to load birth chart');
}
}
}
Step 5: Deployment
Deploy Frontend to Vercel
# Install Vercel CLI
npm i -g vercel
# Deploy
cd frontend
vercel --prod
Deploy Backend to Heroku
# Create Procfile
echo "web: gunicorn app:app" > Procfile
# Install gunicorn
pip install gunicorn
pip freeze > requirements.txt
# Deploy to Heroku
heroku create astrology-api-backend
git init
git add .
git commit -m "Initial commit"
heroku git:remote -a astrology-api-backend
git push heroku main
Production Best Practices
- Use environment variables for API keys
- Implement rate limiting
- Add caching layer (Redis)
- Set up monitoring (Sentry)
- Use CDN for static assets
- Implement user authentication
- Add analytics tracking
Ready to Build Your App?
FREE Sandbox for development. Plans from $12/month