← Back
raaqimorg

raaqimorg/qafiyah

The Arabic Poetry Reference

View on GitHub ↗qafiyah.com ↗
Stars
93
Forks
11
Watchers
93
Open issues
4
Contributors
7
Language
TypeScript
License
MIT License
Default branch
main
Created Sep 24, 2026Updated Oct 1, 2026

Star growth

Today—
This week—
This month—

Star history will appear here once this repo has been tracked for a couple of days.

README

Qafiyah

مرجع الشعر العربي, the Arabic poetry reference

Live site API docs Data snapshot 0031, 23 September 2026 Code: MIT Data: CC0 1.0

Qafiyah is an open-source reference for Arabic poetry. Search every verse for any word or phrase, or browse by poet, meter, rhyme, era, and theme, at qafiyah.com. The code, the data, and a free JSON API are all public, and contributions are welcome.

Poems Verses Poets Meters Rhymes Eras Themes Collections
374,176 6,287,322 17,338 44 36 12 10 1

From snapshot 0031_23_09_2026. Poets, eras, and meters each count one "unknown" entry for unattributed records.

The Qafiyah home page: a search box over the whole corpus A poem page on Qafiyah, showing the poet, era, meter, and verse count above the verses

Try it

Read. Browse qafiyah.com, or follow @qafiyahx on X for a poem a day.

Ask the API. No key, no sign-up:

curl "https://api.qafiyah.com/v1/poems/random?option=lines"

Run it locally. You need Bun 1.3.14, a Docker engine (OrbStack or Docker Desktop), and rustup:

git clone https://github.com/raaqimorg/qafiyah.git
cd qafiyah
bun install
bun run dev

The first run starts Postgres and Elasticsearch in Docker, restores a 100-poem sample, builds the search index, and serves the site at http://localhost:4321 and the API at http://localhost:8787, with no .env or secrets. Everyday commands and troubleshooting are in docs/development.md.

API

https://api.qafiyah.com/v1 serves read-only JSON for poems, poets, eras, meters, rhymes, themes, collections, and full-text search. Explore the interactive docs, the OpenAPI document, or llms.txt.

Access Limit
No key 60 requests per hour per address (an IPv6 /64 is one address)
Free key 500 requests per hour, burst of 10 per second
Higher write to api@qafiyah.com

Sign in with Google or GitHub on the developers page to create a key, and send it as the x-api-key header. Responses report x-ratelimit-remaining, a refused request returns 429 with Retry-After, and errors are RFC 9457 problem+json.

Data

Postgres is the source of truth, Elasticsearch is a search index rebuilt from it, and poet avatars are served from Cloudflare R2 at cdn.qafiyah.com. Snapshots are versioned in this repo: database dumps in data/db/ and avatar archives in data/avatars/.

The 100-poem sample that bun run dev restores is plain text. The full snapshots are encrypted but free for anyone: email dumps@qafiyah.com or avatars@qafiyah.com with what you plan to build, and a passphrase comes right back, with no vetting. Encryption only keeps a record withdrawable later, since a plaintext file pushed to a public repo stays in every clone. Details are in data/README.md.

How it works

A request enters through Cloudflare, passes the web application firewall, and reaches the web app or the API. The web app renders pages on the server by calling the API, which reads Postgres for records and Elasticsearch for search.

flowchart LR
    browser["Browser"] --> cf["Cloudflare edge + Tunnel"]
    cf --> edge["edge-gateway<br/>nginx + ModSecurity CRS"]
    edge -->|"qafiyah.com<br/>api.qafiyah.com"| web["web<br/>nginx + Astro SSR + React islands"]
    web -->|"api.qafiyah.com<br/>+ SSR /v1 fetch"| api["api<br/>Rust, axum"]
    api --> db[("Postgres")]
    api --> es[("Elasticsearch")]
    indexer["search-indexer<br/>one-shot job"] --> db
    indexer --> es
    browser -->|"t.qafiyah.com"| telemetry["telemetry-proxy<br/>Cloudflare Worker"]
    telemetry --> sentry[("Sentry")]
Loading
Part Role Built with
apps/web Server-rendered pages, with React islands for search, the random poem, and settings Astro, React, Tailwind CSS, Bun, PostHog
apps/api Read-only /v1 API, its OpenAPI contract generated from the code Rust, axum, sqlx, utoipa, PostgreSQL 18
apps/search-indexer One-shot job that builds a fresh index from Postgres and swaps the alias Rust, Elasticsearch 9
crates/elasticsearch Index schema, Arabic analyzers, and client shared by the two above Rust
apps/edge-gateway Web application firewall in front of everything, configuration only nginx, OWASP ModSecurity CRS
apps/telemetry-proxy Forwards browser error reports to Sentry from a first-party hostname Cloudflare Workers
apps/inspector Dev-only report of the metadata on every page type TypeScript
scripts/ Repo tooling and the CI gate, one bun run name per entry point Bun, Turborepo, oxlint, oxfmt, vitest

Search reads Arabic the way people do: hamza forms, alif maqsura, and ta marbuta fold to their base letters, diacritics are ignored for matching and kept for display, and an exact title always outranks scattered matches. The full account is in docs/search.md.

Production is one VPS running six containers behind a Cloudflare Tunnel, with nothing reachable inbound, and deploys are a manual step. docs/topology.md maps the whole system and docs/deployment/ covers operations.

Contributing

Report bugs and ideas in GitHub issues. To change code, open an issue first, then fork the repo, branch off main, run bun run ci, and open a pull request linked to the issue; CONTRIBUTING.md walks through it. Good places to start are the web app, search relevance, and the repo tooling. Every component has an AGENTS.md describing its shape, and docs/exceptions.md lists where the code departs from the usual approach, and why.

What bun run ci checks

scripts/ci.ts runs lint and format, type and repo checks, unit tests, and contract snapshots (OpenAPI, the generated client, Elasticsearch queries), then, with Docker, database-backed tests and smoke tests against the built stack. The pre-commit hook checks only the staged files, the pre-push hook runs the gate without Docker, and GitHub Actions runs the gate plus gitleaks on every push and pull request, and each Docker phase when the change touches what it uses (docs/topology.md, "CI/CD topology"). Clippy denies unwrap, expect, panic, indexing, and lossy casts in production code. More in docs/testing.md.

Documentation map
  • docs/development.md: running the stack locally, everyday commands, worktrees, committing.
  • docs/topology.md: a diagram-first map of the whole system, code and production.
  • docs/deployment/README.md: the production entry point.
  • docs/domain.md: what a poem, poet, meter, rhyme, era, theme, and collection mean.
  • docs/search.md: Arabic text handling, relevance tiers, snippet selection.
  • docs/code-conventions.md and the TypeScript, Rust, testing, and pull-request files next to it: how code, tests, and commits are written.
  • docs/identity.md: canonical name, description, organization, and links.
  • AGENTS.md and each component's AGENTS.md: repo layout and component shapes, written for AI agents and useful to anyone.
  • data/README.md: the encrypted database and avatar snapshots.

Contact

Qafiyah is maintained by Raaqim, an open-source organization behind several Arabic-language projects, together with its contributors. The site's about page tells the story. To reach us, write to the address that fits below; API and data requests have their own addresses in the sections above.

For Write to
General contact mail@qafiyah.com
Problems with the site or the data issues@qafiyah.com
Security reports, kept private (policy) security@qafiyah.com
Conduct concerns (code of conduct) conduct@qafiyah.com

We are also on Telegram.

License

The code and documentation are released under the MIT license. The data, meaning the poem catalog the site and API serve and the snapshots in data/, is dedicated to the public domain under CC0 1.0. The site's typeface, Amiri, is used under the SIL Open Font License 1.1, the code of conduct is adapted from the Contributor Covenant 2.1, and the edge gateway runs the stock owasp/modsecurity-crs nginx image.


الشِّعرُ ديوانُ العرب
ابن عباس