URExpat is a multilingual information and resource platform for expatriates in Saudi Arabia. The repository is split into two applications:
cms: Payload CMS backend and admin panel for managing content, media, settings, and public API endpoints.frontend: Public Next.js website that consumes the CMS API and renders localized pages.
URExpat is an independent information platform. It is not affiliated with, endorsed by, or operated by the Government of Saudi Arabia.
urexpat/
|-- cms/ # Payload CMS, PostgreSQL, admin UI, REST API
`-- frontend/ # Public Next.js website
Each app has its own dependencies, lockfile, scripts, and README:
The CMS owns the content model and exposes curated API responses. The frontend uses those responses to render the public website.
Payload CMS admin
|-- Articles, categories, authors, media, FAQs, resources, tools
|-- Homepage, SEO pages, site settings
`-- /api/v1/* REST endpoints
Next.js frontend
|-- Localized public routes: /en, /ar, /ur
|-- Articles, categories, resources, tools, search, static pages
`-- SEO metadata, sitemap, robots.txt, structured data
The CMS is built with Payload CMS 3, Next.js 16, TypeScript, PostgreSQL, Lexical rich text, and Payload localization.
Collections:
- Users
- Media
- Authors
- Categories
- Government Sources
- Articles
- FAQs
- Newsletter Subscribers
- Resources
- Tools
Globals:
- Site Settings
- Homepage
- SEO Pages
The CMS supports Payload locales en, ar, ur, hi, bn, and tl. Arabic and Urdu are configured as RTL locales. Public frontend routing currently exposes en, ar, and ur.
Public CMS endpoints are mounted under /api/v1:
GET /api/v1/home
GET /api/v1/articles
GET /api/v1/articles/:slug
GET /api/v1/categories
GET /api/v1/faqs
GET /api/v1/resources
GET /api/v1/search
GET /api/v1/seo-pages
GET /api/v1/site-settings
GET /api/v1/tools
GET /api/v1/tools/:slug
Media uploads are handled through Payload cloud storage with a dynamic provider selected in Site Settings. New uploads can be stored in Supabase S3-compatible storage or Cloudinary.
The frontend is built with Next.js 15, React 19, TypeScript, Tailwind CSS, and localized App Router pages.
Main public routes:
/{locale}
/{locale}/articles
/{locale}/articles/{slug}
/{locale}/categories
/{locale}/resources/{category}
/{locale}/tools
/{locale}/tools/{slug}
/{locale}/search-results
/{locale}/about
/{locale}/contact
/{locale}/terms
/{locale}/privacy-policy
/{locale}/disclaimer
Supported public locales:
en: English, LTRar: Arabic, RTLur: Urdu, RTL
The frontend redirects non-localized routes to the default locale, en.
Install and run each app from its own directory.
CMS:
cd cms
pnpm install
pnpm devFrontend:
cd frontend
npm install
npm run devTypical local URLs:
CMS/admin: http://localhost:3000/admin
CMS API: http://localhost:3000/api/v1
Frontend: http://localhost:3000 or another available Next.js port
If both apps run at the same time, start one of them on a different port.
The CMS requires at least:
DATABASE_URL=
PAYLOAD_SECRET=
NEXT_PUBLIC_SERVER_URL=
NEXT_PUBLIC_SITE_URL=
SUPABASE_S3_BUCKET=
SUPABASE_S3_ENDPOINT=
SUPABASE_S3_REGION=
SUPABASE_S3_ACCESS_KEY=
SUPABASE_S3_SECRET_KEY=
SUPABASE_PUBLIC_URL=
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=The frontend requires:
NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
NEXT_PUBLIC_SITE_URL=http://localhost:3000Do not commit .env or .env.local files containing secrets.
CMS:
pnpm dev
pnpm build
pnpm start
pnpm lint
pnpm generate:types
pnpm generate:importmap
pnpm test:int
pnpm test:e2eFrontend:
npm run dev
npm run build
npm run start
npm run lint
npm run type-check
npm run formatDeploy the CMS and frontend as separate applications. Configure the frontend NEXT_PUBLIC_API_URL to point to the CMS /api/v1 base URL, and configure CMS CORS/CSRF origins with the public frontend and CMS server URLs.
Before production deployment, verify:
- CMS can connect to PostgreSQL.
PAYLOAD_SECRETis set.- Media storage credentials are valid.
- Frontend can fetch JSON from the CMS API.
NEXT_PUBLIC_SITE_URLmatches the public website domain.- Localized metadata, sitemap, and robots output use the correct domain.