Projects islamic-network sdks js Files
dist Loading last commit info...
src
tests
.gitignore
CHANGELOG.md
README.md
package-lock.json
package.json
tsconfig.json
tsup.config.ts
README.md

Islamic Network SDK (JavaScript/TypeScript)

Typed SDK for the Islamic Network APIs designed to work in React, Next.js, and React Native.

Supported APIs:

  • AlAdhan (prayer times, Islamic calendar, qibla, Asma Al Husna)
  • AlQuran (surahs, ayahs, sections, editions, search, sajda, meta)
  • Boycott Israeli (companies, categories, search)
  • Sermons (sources, languages, year/month sermon feeds)
  • Pray (Hijri months and holy days, prayers, ṣalawāt, adhkār, duʿās, search)
  • Events (Islamic calendar events, per-day/per-month calendars, people, search)
  • People (the canonical person registry: biographies, aggregated events and quotes, search)
  • Quotes (quotes with translations, random quote, per-person quotes, search)
  • Stories (marifa.org stories, tags, artwork assets, search)
  • One (authenticated per-user saves & sync across the apps — requires a Keycloak Bearer token)

Installation

npm install @islamicnetwork/sdk
# or
pnpm add @islamicnetwork/sdk
# or
yarn add @islamicnetwork/sdk

Usage

AlAdhan

import { AlAdhanClient, AlAdhanRequests } from "@islamic-network/sdk";

const client = AlAdhanClient.create();

const request = new AlAdhanRequests.DailyPrayerTimesByCoordinatesRequest(
  "01-01-2025",
  51.5194682,
  -0.1360365,
  new AlAdhanRequests.PrayerTimesOptions()
);

const response = await client.prayerTimes().dailyByCoordinates(request);

console.log(response.data.timings.Fajr);
import { AlAdhanClient, AlAdhanRequests } from "@islamic-network/sdk";

const client = AlAdhanClient.create();

const qibla = await client.qibla().direction(
  new AlAdhanRequests.QiblaDirectionRequest(19.071017570421, 72.838622286762)
);

console.log(qibla.data.direction);
import { AlAdhanClient, AlAdhanRequests } from "@islamic-network/sdk";

const client = AlAdhanClient.create();

const asma = await client
  .asmaAlHusna()
  .byNumber(new AlAdhanRequests.AsmaAlHusnaByNumberRequest([1, 2, 3]));

console.log(asma.data[0]?.en.meaning);

Qibla Compass (Binary)

import { AlAdhanClient, AlAdhanRequests } from "@islamic-network/sdk";

const client = AlAdhanClient.create();
const image = await client
  .qibla()
  .compass(new AlAdhanRequests.QiblaCompassRequest(19.071017570421, 72.838622286762));

// Browser/React: build a Blob URL
const blob = new Blob([image.body], { type: image.contentType });
const url = URL.createObjectURL(blob);

AlQuran

Every method on AlQuranClient accepts either bare arguments or the matching *Request class — pick whichever reads better:

import { AlQuranClient, AlQuranRequests } from "@islamicnetwork/sdk";

const client = AlQuranClient.create();

// Bare arguments — concise, recommended for app code.
const fatiha = await client.surahByEditions(1, ["quran-uthmani", "en.sahih"]);
const kursi  = await client.ayahByNumberEdition("2:255", "en.sahih");
const page2  = await client.page(2);
const results = await client.search("mercy");

// Request-class form — still supported. Useful when you need to
// customise SectionOptions or pre-build a request elsewhere.
const ayah = await client.ayahByNumber(new AlQuranRequests.AyahByNumberRequest(5));
const editions = await client.editions(
  new AlQuranRequests.EditionListRequest(null, "text", "en"),
);

Ayah references accept either a global number (16236) or a sūrah-ayah string ("2:255"):

await client.ayahByNumberEdition(262, "en.sahih");   // by global number
await client.ayahByNumberEdition("2:255", "en.sahih"); // by surah:ayah

surahListWithMeta()

surahList joined with /meta so each sūrah carries firstPage (in the 604-page muṣḥaf) and firstAyahGlobalNumber (1–6236). /meta is memoised on the client instance — repeated calls are one HTTP round-trip in total.

const list = await client.surahListWithMeta();
for (const s of list.data) {
  console.log(s.number, s.englishName, "page", s.firstPage);
}

searchEnriched()

Search and enrich each match with the Arabic counterpart (from quran-uthmani) plus pre-computed character-offset highlights of the query inside the match text. Heavy consumers can opt out via SearchOptions.

const res = await client.searchEnriched("mercy");
for (const m of res.data.matches) {
  console.log(`${m.surah.englishName} ${m.numberInSurah}: ${m.text}`);
  console.log("   arabic:", m.arabic);
  console.log("   matches at:", m.highlights);   // e.g. [[4, 9], [42, 47]]
}

// Skip the Arabic counterpart fetches and/or highlight computation:
await client.searchEnriched("mercy", { includeArabic: false });
await client.searchEnriched("mercy", { computeHighlights: false });
await client.searchEnriched("mercy", { arabicFetchLimit: 10 });  // cap fetches

Also available: searchBySurahEnriched(query, surahNumber, options?) and searchBySurahLanguageEnriched(query, surahNumber, language, options?).

Async factories

Every client offers createAsync(options?) alongside create(options?) so async-scope callers don't have to mix sync + async factories:

const client = await AlQuranClient.createAsync();

Today createAsync resolves to the synchronous create result; the async signature reserves the slot for future capability-probe work (e.g. preloading /meta).

Error handling

Every method throws ApiException on non-2xx responses. Catch via instanceof to access the statusCode and the upstream payload:

import { AlQuranClient, ApiException } from "@islamicnetwork/sdk";

const client = AlQuranClient.create();

try {
  await client.surah(999); // out-of-range
} catch (e) {
  if (e instanceof ApiException) {
    if (e.statusCode === 404)  // surah doesn't exist
    if (e.statusCode === 429)  // rate-limited
    console.error(e.error?.status, e.error?.data);
  } else {
    throw e;
  }
}

Boycott Israeli

import { BoycottIsraeliClient, BoycottIsraeliRequests } from "@islamic-network/sdk";

const client = BoycottIsraeliClient.create();

const categories = await client.categories(
  new BoycottIsraeliRequests.CategoriesRequest(
    new BoycottIsraeliRequests.PaginationOptions({ limit: 5 })
  )
);

const companies = await client.search(
  new BoycottIsraeliRequests.SearchRequest(
    "food",
    new BoycottIsraeliRequests.PaginationOptions({ limit: 2 })
  )
);

console.log(categories.data[0]?.name);
console.log(companies.data[0]?.name);

Sermons

import { SermonsClient, SermonsRequests } from "@islamic-network/sdk";

const client = SermonsClient.create();

const sources = await client.sources(new SermonsRequests.SourcesRequest());
const languages = await client.languages(new SermonsRequests.LanguagesRequest());

const months = await client.yearSermons(
  new SermonsRequests.YearSermonsRequest("uae-awqaf", 2021, SermonsRequests.SermonType.Friday)
);

const month = await client.monthSermons(
  new SermonsRequests.MonthSermonsRequest("uae-awqaf", 2021, 7, SermonsRequests.SermonType.Friday)
);

console.log(sources[0]?.handle);
console.log(languages[0]?.code);
console.log(months[0]?.sermons[0]?.title);
console.log(month.month.number);

Pray

Holy days and recommended worship on the Islamic calendar (pray.api.islamic.network). Every response is a { code, status, data } envelope; list endpoints add meta pagination (?page=, ?limit= — default 50, max 200) via PrayRequests.PaginationOptions.

import { PrayClient, PrayRequests } from "@islamicnetwork/sdk";

const client = PrayClient.create();

const months = await client.months(new PrayRequests.MonthsRequest());
const today = await client.calendarToday(new PrayRequests.CalendarTodayRequest("08-07-2026"));
const ashura = await client.calendarDay(new PrayRequests.CalendarDayRequest(1, 10));
const salaat = await client.salaat(new PrayRequests.SalaatRequest("salat-al-tasbih"));
const hits = await client.search(new PrayRequests.SearchRequest("tasbih"));

console.log(months.data[0]?.name["ar-Latn"]);
console.log(today.data.date.hijri.year);
console.log(ashura.data.salawaats[0]?.formula);
console.log(salaat.data.description?.en?.raw);
console.log(hits.data.results[0]?.matched_in);

Events

What happened / what is observed on the days of the Islamic calendar (events.api.islamic.network).

import { EventsClient, EventsRequests } from "@islamicnetwork/sdk";

const client = EventsClient.create();

const events = await client.events(new EventsRequests.EventsRequest());
const event = await client.event(new EventsRequests.EventRequest("martyrdom-of-husayn"));
const ashura = await client.calendarDay(new EventsRequests.CalendarDayRequest(1, 10));
const personEvents = await client.personEvents(new EventsRequests.PersonEventsRequest("husayn-ibn-ali"));

console.log(events.data[0]?.title.en);
console.log(event.data.year?.hijri);
console.log(ashura.data.events[0]?.slug);
console.log(personEvents.data.person.slug);

People

The canonical person registry — biographies plus what the ecosystem knows about each person (people.api.islamic.network).

import { PeopleClient, PeopleRequests } from "@islamicnetwork/sdk";

const client = PeopleClient.create();

const companions = await client.people(new PeopleRequests.PeopleRequest("companion"));
const person = await client.person(new PeopleRequests.PersonRequest("husayn-ibn-ali"));
const quotes = await client.personQuotes(new PeopleRequests.PersonQuotesRequest("husayn-ibn-ali"));

console.log(companions.data[0]?.name.en);
console.log(person.data.biography?.en?.html);
console.log(quotes.data.quotes[0]?.original.text);

Quotes

The quotes.islamic.network corpus (quotes.api.islamic.network).

import { QuotesClient, QuotesRequests } from "@islamicnetwork/sdk";

const client = QuotesClient.create();

const quotes = await client.quotes(
  new QuotesRequests.QuotesRequest(
    new QuotesRequests.QuoteFilterOptions({ author: "bayazid-bistami" }),
    new QuotesRequests.PaginationOptions({ limit: 2 })
  )
);
const quote = await client.quote(new QuotesRequests.QuoteRequest(88));
const random = await client.random(new QuotesRequests.RandomQuoteRequest());

console.log(quotes.data[0]?.original.text);
console.log(quote.data.translations?.en?.text);
console.log(random.data.source);

Stories

The marifa.org stories corpus (stories.api.islamic.network). Story artwork is served as binary data through asset().

import { StoriesClient, StoriesRequests } from "@islamicnetwork/sdk";

const client = StoriesClient.create();

const stories = await client.stories(new StoriesRequests.StoriesRequest({ tag: "remembrance" }));
const story = await client.story(
  new StoriesRequests.StoryRequest("one-thing-which-must-never-be-forgotten")
);
const tags = await client.tags(new StoriesRequests.TagsRequest());

console.log(stories.data[0]?.title.en);
console.log(story.data.body.en?.html);
console.log(tags.data[0]?.stories);

// Artwork (binary, like the qibla compass)
if (story.data.image) {
  const image = await client.asset(new StoriesRequests.AssetRequest(story.data.image.id));
  const blob = new Blob([image.body], { type: image.contentType });
}

One (per-user saves & sync)

one.api.islamic.network — authenticated per-user saves across the islamic.network apps (quran ayahs, pray items, quotes, people, stories). Unlike the content clients, every call needs a Bearer token from the Islamic Network Keycloak (id.mamluk.net, realm mamluk): pass token as a raw access token or as a provider function, which is called before every request — back it with your OIDC library so a refreshed token is always sent.

A save is a reference plus your metadata ({ savedAt, note? }) — never a copy of the content; render saves by fetching the refs from the content APIs.

import { OneClient, OneRequests } from "@islamicnetwork/sdk";

const client = OneClient.create({
  token: async () => auth.getAccessToken() // or a plain string
});

// Idempotent upsert — envelope code 201 on create, 200 on update
const put = await client.save(
  new OneRequests.PutSaveRequest("quran", "ayah", "2:255", {
    savedAt: Date.now(),
    note: "Āyat al-Kursī"
  })
);

// Your saves, newest first (optionally filtered by type)
const saves = await client.saves(new OneRequests.ListSavesRequest("quran"));

// Sign-in sync: POST locally-held saves, adopt the returned list (server wins)
const merged = await client.merge(
  new OneRequests.MergeSavesRequest("quran", [
    { type: "ayah", ref: "1:1", payload: { savedAt: Date.now() } }
  ])
);

// Idempotent removal
await client.unsave(new OneRequests.DeleteSaveRequest("quran", "ayah", "2:255"));

Errors follow the API contract: 401 no token sent, 400 expired/malformed token or invalid payload/ref, 503 saves store temporarily unreachable (retry with backoff) — all thrown as ApiException.

Configuration

Both clients accept the same options:

import { AlAdhanClient } from "@islamic-network/sdk";

const client = AlAdhanClient.create({
  baseUrl: "https://api.aladhan.com/v1",
  defaultHeaders: { "X-App": "my-app" },
  defaultQuery: { iso8601: true },
  timeoutMs: 10_000,
  userAgent: "my-sdk-client",
  fetch: globalThis.fetch // optional, provide for custom environments
});

Notes

  • The SDK uses the global fetch implementation by default. Provide a custom fetch for non-standard environments.
  • Prayer timings preserve API field names (e.g., Fajr, Dhuhr, Firstthird).
  • Qibla compass requests return binary image data with a content type.
  • Integration tests call live API endpoints and retry once on 429 or network errors.

Testing

npm test

Integration tests call the live APIs. When a 429 or timeout occurs, the client retries once after 1 second.


Islamic Network     Bahriya

An Islamic Network project, hosted on Bahriya's Distributed Container Platform.

Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover