close
Skip to main content

JavaScript SDK

Purpose: Choose the correct SDK entrypoint before you write code. Use this when: You want to use Quran Foundation APIs from JavaScript or TypeScript. Do not use this when: You only need raw HTTP examples and do not want an SDK. Backend required: Depends on the API family and your client type. Allowed runtimes: Node.js, serverless functions, workers, browser apps, mobile apps. Required credentials: client_id always. client_secret only on the server. Minimal import: @quranjs/api/server or @quranjs/api/public.

The SDK now has two runtime entrypoints:

  • @quranjs/api/server
  • @quranjs/api/public

Use these entrypoints for new code. They make the safe runtime boundary obvious by separating server-only API access from browser and mobile flows.

For backwards compatibility, @quranjs/api still exports the legacy QuranClient, so existing integrations do not need to migrate immediately. Moving to @quranjs/api/server or @quranjs/api/public is recommended cleanup, not a breaking upgrade requirement.

If you are still asking "what kind of app am I building?" start with App Shapes. It is the shortest path to the right architecture.

Production defaults are built in. If you are maintaining the SDK itself and need local smoke or local docs instructions, use the maintainer guide in the api-js repo.

SDK vs Starter App

The SDK is the library: import @quranjs/api/server in backend code and @quranjs/api/public in browser or mobile-facing code.

The starter is a generated Next.js app built with the SDK. It gives you a working reader, search, OAuth2 session, notes, bookmarks, collections, goals, preferences, and QuranReflect-related surfaces. Use the starter when you want a working app to customize. Use the SDK directly when you already have your own UI and backend.

User APIs can be called with the SDK, but your app still needs OAuth2 login, session storage, token refresh, and approved scopes. The starter includes those pieces; SDK-only integrations must wire them in their own backend.

If you use the full-stack OAuth2 example, remember that it is an interactive React SPA shell backed by server-side sessions. For serious multi-instance production deployments, use a shared session store such as Redis.

Quick Start With Scaffold

Use the official NPX scaffold when you want a complete app before customizing the UI or reading lower-level OAuth2 details:

npx @quranjs/create-app@latest my-quran-app --template next --package-manager npm --install --git --sdk-source npm --yes

Then follow:

Two Token Paths

There are two different tokens in the system:

  • The user token is for personal actions like notes, bookmarks, collections, and QuranReflect user data.
  • The app token is for app-level reads like Quran content and search.

They solve different jobs, so they can expire at different times. That is why Content can still work even when User APIs start failing. Content and Search use a separate app token that @quranjs/api/server gets with client_credentials. User APIs use the signed-in user's session token.

The browser never gets the client_secret. The secret stays on the backend so the backend can exchange codes, refresh user sessions, and ask for app tokens safely.

PKCE + Backend

PKCE is the "proof" the app sends during login. It helps the app prove the same login request came back to it.

For most clients, PKCE is only half the story:

  • The frontend or mobile app starts login with PKCE.
  • The backend or BFF finishes the confidential parts, like code exchange and refresh.
  • The backend stores the session server-side.

That is why modern edge or serverless frontends still usually need a backend layer. The edge can host the UI, but the confidential token work and shared session storage still belong in a backend or BFF.

Pick Your Setup

If you want the very short version, use this:

  • SPA only: User APIs only, and only if Quran Foundation approved your app as a public client.
  • Mobile only: same as SPA only.
  • Frontend + backend: the normal path for Content, Search, and apps that mix user features with Quran data.

I have a backend

Use @quranjs/api/server.

This is the right choice for:

  • Content APIs
  • Search APIs
  • Confidential OAuth2 token exchange
  • Cron jobs
  • Workers
  • Server-side rendering

I have frontend + backend

Use both:

  • frontend: @quranjs/api/public
  • backend: @quranjs/api/server

This is the recommended setup for most apps.

I only have frontend or mobile

Use @quranjs/api/public.

This only works safely for:

  • PKCE login
  • user-session flows
  • clients that Quran Foundation explicitly provisioned as public

It does not support client_secret.

Do I Need a Backend?

API familyBackend required?Notes
ContentYesUse @quranjs/api/server
SearchYesUse @quranjs/api/server
User APIsUsually yesMost clients are confidential by default
OAuth2 loginUsually yesFrontend starts PKCE, backend exchanges code
OAuth2 login for approved public clientsNoOnly if Quran Foundation explicitly says your client is public

If your app is deployed as an edge frontend, keep the confidential exchange, refresh, and shared session store in a backend or BFF layer. The edge can still be the entrypoint for users.

Use the Right Import

Server

import { createServerClient } from "@quranjs/api/server";

const client = createServerClient({
clientId: process.env.QF_CLIENT_ID!,
clientSecret: process.env.QF_CLIENT_SECRET!,
});

const chapters = await client.content.v4.chapters.list();

Public

import { createPublicClient } from "@quranjs/api/public";

const client = createPublicClient({
clientId: "your-client-id",
clientType: "confidential-proxy",
});

const authUrl = client.oauth2.v1.authorizeUrl({
client_id: "your-client-id",
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope: "openid offline_access user bookmark",
state: "random-state",
});

First Working Examples

Server example

import { createServerClient } from "@quranjs/api/server";
import { SearchMode } from "@quranjs/api";

const client = createServerClient({
clientId: process.env.QF_CLIENT_ID!,
clientSecret: process.env.QF_CLIENT_SECRET!,
});

const chapters = await client.content.v4.chapters.list();
const results = await client.search.v1.query({
query: "mercy",
mode: SearchMode.Quick,
});

Full-stack example

Frontend:

import { createPublicClient } from "@quranjs/api/public";

const client = createPublicClient({
clientId: "your-client-id",
clientType: "confidential-proxy",
});

Backend:

import { createServerClient } from "@quranjs/api/server";

const client = createServerClient({
clientId: process.env.QF_CLIENT_ID!,
clientSecret: process.env.QF_CLIENT_SECRET!,
});

Common Mistakes

  • Putting client_secret in browser or mobile code.
  • Calling Content or Search from @quranjs/api/public.
  • Assuming every client can exchange OAuth2 codes directly.
  • Treating word.audioUrl as a Content API path. Resolve word-by-word audio against https://audio.qurancdn.com/ instead.
  • Starting new code with the legacy root QuranClient instead of choosing the runtime-specific client for your app shape.

AI Handoff Prompt

Canonical prompt ID: QF_NPX_STARTER_PROMPT_V1

The copyable prompt above is published as QF_NPX_STARTER_PROMPT_V1: