Next.js Integration

Server Components and API Routes

Setup

# Create Next.js app
npx create-next-app@latest my-astro-app --typescript --tailwind --app

# Install SDK
npm install @anthropic/vedika-sdk
# .env.local
VEDIKA_API_KEY=vk_live_your_api_key

API Route (App Router)

// app/api/chart/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { VedikaClient } from '@anthropic/vedika-sdk';

const vedika = new VedikaClient({
  apiKey: process.env.VEDIKA_API_KEY
});

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    const chart = await vedika.birthChart({
      datetime: body.datetime,
      latitude: body.latitude,
      longitude: body.longitude
    });

    return NextResponse.json(chart);
  } catch (error: any) {
    return NextResponse.json(
      { error: error.message },
      { status: 500 }
    );
  }
}

Streaming API Route

// app/api/chat/stream/route.ts
import { NextRequest } from 'next/server';
import { VedikaClient } from '@anthropic/vedika-sdk';

const vedika = new VedikaClient({
  apiKey: process.env.VEDIKA_API_KEY
});

export async function POST(request: NextRequest) {
  const { question, birthDetails } = await request.json();

  const stream = await vedika.queryStream({
    question,
    birthDetails
  });

  // Create a ReadableStream for SSE
  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const event of stream) {
        const data = JSON.stringify(event);
        controller.enqueue(encoder.encode(`data: ${data}\n\n`));
      }
      controller.close();
    }
  });

  return new Response(readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  });
}

Server Component

// app/chart/[id]/page.tsx
import { VedikaClient } from '@anthropic/vedika-sdk';

const vedika = new VedikaClient({
  apiKey: process.env.VEDIKA_API_KEY
});

interface PageProps {
  params: { id: string };
  searchParams: {
    datetime: string;
    lat: string;
    lng: string;
  };
}

export default async function ChartPage({ searchParams }: PageProps) {
  // Fetch data server-side
  const chart = await vedika.birthChart({
    datetime: searchParams.datetime,
    latitude: parseFloat(searchParams.lat),
    longitude: parseFloat(searchParams.lng)
  });

  return (
    <div className="max-w-4xl mx-auto p-8">
      <h1 className="text-3xl font-bold mb-6">Your Birth Chart</h1>

      <div className="grid grid-cols-3 gap-4 mb-8">
        <div className="bg-yellow-50 p-4 rounded-lg text-center">
          <div className="text-sm text-gray-500">Sun Sign</div>
          <div className="text-2xl font-bold">{chart.sunSign}</div>
        </div>
        <div className="bg-blue-50 p-4 rounded-lg text-center">
          <div className="text-sm text-gray-500">Moon Sign</div>
          <div className="text-2xl font-bold">{chart.moonSign}</div>
        </div>
        <div className="bg-purple-50 p-4 rounded-lg text-center">
          <div className="text-sm text-gray-500">Ascendant</div>
          <div className="text-2xl font-bold">{chart.ascendant}</div>
        </div>
      </div>

      <h2 className="text-xl font-semibold mb-4">Planetary Positions</h2>
      <div className="bg-white shadow rounded-lg overflow-hidden">
        <table className="w-full">
          <thead className="bg-gray-50">
            <tr>
              <th className="px-4 py-3 text-left">Planet</th>
              <th className="px-4 py-3 text-left">Sign</th>
              <th className="px-4 py-3 text-left">House</th>
            </tr>
          </thead>
          <tbody>
            {chart.planets.map((planet) => (
              <tr key={planet.name} className="border-t">
                <td className="px-4 py-3">{planet.name}</td>
                <td className="px-4 py-3">{planet.sign}</td>
                <td className="px-4 py-3">{planet.house}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

Server Actions

// app/actions.ts
'use server';

import { VedikaClient } from '@anthropic/vedika-sdk';

const vedika = new VedikaClient({
  apiKey: process.env.VEDIKA_API_KEY
});

export async function generateChart(formData: FormData) {
  const datetime = formData.get('datetime') as string;
  const latitude = parseFloat(formData.get('latitude') as string);
  const longitude = parseFloat(formData.get('longitude') as string);

  const chart = await vedika.birthChart({
    datetime,
    latitude,
    longitude
  });

  return chart;
}

export async function getPanchang(date: string, lat: number, lng: number) {
  return await vedika.panchang({ date, latitude: lat, longitude: lng });
}
// app/chart/form.tsx
'use client';

import { useState } from 'react';
import { generateChart } from '../actions';

export function ChartForm() {
  const [chart, setChart] = useState(null);

  async function handleSubmit(formData: FormData) {
    const result = await generateChart(formData);
    setChart(result);
  }

  return (
    <form action={handleSubmit}>
      <input name="datetime" type="datetime-local" required />
      <input name="latitude" type="number" step="0.0001" required />
      <input name="longitude" type="number" step="0.0001" required />
      <button type="submit">Generate Chart</button>
    </form>
  );
}

Caching Strategies

// app/panchang/page.tsx
import { VedikaClient } from '@anthropic/vedika-sdk';
import { unstable_cache } from 'next/cache';

const vedika = new VedikaClient();

// Cache panchang for 1 hour
const getCachedPanchang = unstable_cache(
  async (date: string, lat: number, lng: number) => {
    return vedika.panchang({ date, latitude: lat, longitude: lng });
  },
  ['panchang'],
  { revalidate: 3600 }  // 1 hour
);

export default async function PanchangPage() {
  const today = new Date().toISOString().split('T')[0];
  const panchang = await getCachedPanchang(today, 28.6139, 77.2090);

  return (
    <div>
      <h1>Today's Panchang</h1>
      <p>Tithi: {panchang.tithi.name}</p>
      <p>Nakshatra: {panchang.nakshatra.name}</p>
    </div>
  );
}

Rate Limiting Middleware

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

const rateLimit = new Map<string, number[]>();
const WINDOW_MS = 60 * 1000;  // 1 minute
const MAX_REQUESTS = 10;

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const ip = request.ip || 'anonymous';
    const now = Date.now();

    const timestamps = rateLimit.get(ip) || [];
    const recent = timestamps.filter(t => t > now - WINDOW_MS);

    if (recent.length >= MAX_REQUESTS) {
      return NextResponse.json(
        { error: 'Rate limit exceeded' },
        { status: 429 }
      );
    }

    recent.push(now);
    rateLimit.set(ip, recent);
  }

  return NextResponse.next();
}

export const config = {
  matcher: '/api/:path*'
};

Next Steps