madeenan

Next.js Integration Guide

Build an Islamic Chatbot With Next.js and Citations

A server-first Next.js tutorial for retrieval, grounded generation, citation rendering, and safe key handling.

What You Will Build

A Next.js route that calls Madeenan from the server and returns source blocks your chat interface can display beside an answer.

Prerequisites

  • Next.js 14 or newer
  • A Madeenan API key
  • A server-side environment variable
  • Basic React and TypeScript

Keep the API Key on the Server

Create a route handler inside your Next.js application. The browser sends the user's question to your route, and the route calls Madeenan with the secret API key.

This boundary matters. A key placed in a client component, a public environment variable, or a browser network request can be copied and abused.

// app/api/islamic-search/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const { question } = await request.json();
if (typeof question !== "string" || !question.trim()) {
return NextResponse.json({ error: "question_required" }, { status: 400 });
}
const params = new URLSearchParams({
q: question.trim(),
indexes: "quran,hadith,tafsir,dua",
limit_per_source: "4"
});
const response = await fetch(
`https://api.madeenan.com/v1/search?${params}`,
{
headers: { "X-Madeenan-API-Key": process.env.MADEENAN_API_KEY! },
cache: "no-store"
}
);
const payload = await response.json();
return NextResponse.json(payload, { status: response.status });
}

Render Sources as Product UI

Treat each result as a source object, not a decorative footnote. Show the reference, Arabic text when present, translation, and Hadith grade.

Keep the answer and sources close enough that a reader can check the evidence without leaving the conversation.

type Source = {
ref: string;
type: string;
arabic?: string | null;
translation?: string | null;
grade?: string | null;
};
export function SourceCard({ source }: { source: Source }) {
return (
<article aria-label={source.ref}>
<h3>{source.ref}</h3>
{source.grade ? <p>Grade: {source.grade}</p> : null}
{source.arabic ? <p lang="ar" dir="rtl">{source.arabic}</p> : null}
{source.translation ? <p>{source.translation}</p> : null}
</article>
);
}

Handle Empty and Failed Retrieval

Do not generate a confident answer when retrieval returns no useful source. Return a clear empty state and let the user refine the question.

Map authentication, rate-limit, and upstream errors to stable product messages. Log status codes, never questions or API keys.

Security Boundary

Only the server route reads MADEENAN_API_KEY. Never prefix this variable with NEXT_PUBLIC_ or pass it into a client component.

Test Before Shipping

  • Reject an empty question with status 400
  • Verify the browser never receives the Madeenan key
  • Render Arabic with lang and dir attributes
  • Show an empty state when no readable sources return
  • Test 401 and 429 responses