live in productionServices Marketplace & CMS· 2026

Padma Service

Full-stack platform for Padma Service Company — public service catalog with categories and blogs, phone-OTP member accounts, and an admin console to manage banners, homepage sections, and user-submitted listings through an approval queue. Next.js 16 storefront plus a Hono API on Bun, Postgres via Drizzle.

Next.js 16React 19HonooRPCDrizzle ORMPostgresBetter-AuthTipTapCloudinaryshadcn/ui

The brief

Padma Service Company needed more than a brochure site. They run a wide catalog of professional services — security, home services, vehicle and property listings, and more — and the old flow was scattered across phone calls and ad-hoc updates.

The brief: a public marketing site in Bengali and English, a member area where authenticated users can propose new service listings, and an admin CMS where staff approve, reorder, and publish without touching the database. One Postgres schema behind both surfaces.

My role

The problem

Three tensions showed up early:

  • Catalog depth — services nest under categories and subcategories, each with its own imagery, card labels, featured flags, and related posts. The admin UI has to edit that graph without losing order.
  • Member-submitted listings — logged-in users can request create, edit, or delete operations on services. Those requests sit in a queue (pendingapproved / rejected) with admin notes and display order set at approval time.
  • Auth that matches the market — phone OTP via SMS (not email-first). Better-Auth on both apps, with the API enforcing role separation between public members and admin operators.

The approach

One schema. Two apps. The contract is the seam.

when sketching the split

The storefront is Next.js 16 with route groups for the public site ((client)) and the admin console ((admin)). Data flows through oRPC + TanStack Query — types are imported from the server package at compile time so a renamed field breaks the build, not production.

The API is Hono on Bun with Drizzle against Postgres: services, categories, blogs, banners, homepage sections, media, and the user-service-request workflow. Cloudinary handles uploads; TipTap powers rich blog bodies in the admin.

Public routes cover home, category browsing, individual service pages, blogs, about-us, and a member dashboard. Admin routes cover the full CMS: services, categories, blog types, blogs, banners, homepage sections, media library, and the service-request inbox.

Padma Service marketing homepage
fig. — public site — bilingual metadata, service catalog entry points
Admin services management
fig. — admin CMS — dense tables for the service catalog
padma-servicenext.js 16 · (client) + (admin)padma-service-serverhono · bun · orpcpostgresdrizzle · better-auth
fig. — two repos, one oRPC contract, shared Postgres

The hard parts

User-submitted services without polluting the catalog

Members submit create, edit, or delete requests as JSON blobs attached to a workflow row. Admins see a dedicated inbox, set displayOrder on approval, and can leave a note on rejection. Until approval, nothing hits the public service table — the live catalog stays clean.

The tricky bit is edit and delete: the request carries a serviceId plus serialized payload so admins can diff what changed before approving. Create requests start with no serviceId and materialize a row only after approval.

Homepage as data, not hard-coded JSX

Homepage sections, subcategory carousels, and banners are all database-driven with drag-and-drop ordering (@dnd-kit in the admin). Marketing can reshuffle the front page without a deploy. Related posts tie services and blogs together so detail pages cross-sell without manual linking in every entry.

Bilingual SEO without two codebases

Root metadata targets bn_BD with Bengali titles and descriptions (metadataBase → padmaservice.com). Keywords span security and adjacent service lines the company lists. OG and Twitter images ship from the app directory so shares render correctly on Facebook and messaging apps common in Bangladesh.

Next.js 16 + React 19storefront
Route groups for public site vs admin; TipTap for blog bodies.
Hono + Bunapi
API process with hot reload in dev; Docker image for deploy.
oRPC + TanStack Querycontract
Typed client imported from server types; no drift at the boundary.
Drizzle + Postgresdata
Services, categories, blogs, banners, requests, media — one schema.
Better-Auth + SMS OTPauth
Phone verification for members; admin roles on the same auth tables.
Cloudinarymedia
Service imagery and media library uploads from the admin.

What's running today

2repos · web + api
livepadmaservice.com
CMSadmin · full catalog
OTPphone auth · sms

The public site and admin console are in production at padmaservice.com. The API ships in a Docker container alongside the Next.js deploy. Service count and traffic numbers stay with the client — the architecture is built to scale the catalog without restructuring.

What I'd do differently

I'd pull the two folders into a Turborepo workspace on day one. The compile-time import of AppRouterClient from the server package already enforces the contract; a monorepo would make shared Zod shapes and Drizzle types a package import instead of a relative path across repo roots. Same pattern I reached for on Bikalpo and Bright Tutor — this project got there through discipline, not tooling.