LogoLogoOpCreative
OrdersProducts
What's new
Log InSign Up

What is our tech stack

The languages, frameworks and services behind OpCreative, and why we picked each one. Kept current as the platform evolves.

Reading time
5 min
Updated
Aug 23, 2026
Authors
Niyam Vora

On this page

  • Back-end
  • Front-end
  • Typography
  • Auth
  • Data & storage
  • Deployments & infrastructure
  • Integrations
  • AI
  • Testing
  • Observability
  • Miscellaneous

Back-end

We use TypeScript and Node.js for everything on the server, inside a Turborepo monorepo: one dashboard app and three workspace libraries (db, auth, shared) that ship as raw TypeScript and compile with the app.

The back-end is Next.js route handlers and Server Actions running on Vercel's Node runtime. There is no separate API server: the seller API under /api/v1 is versioned, JSON-only, authenticated with a single x-api-key header and described by an OpenAPI 3.1 document generated from the same source.

Every request and response is parsed with Zod into a typed schema. Rate limits are counted per API key in Postgres rather than per IP, so one integration on a shared cloud address cannot throttle another.

There is deliberately no API framework yet. At 8 endpoints, plain route handlers stay readable and the OpenAPI document is small enough to keep by hand. The next step is already decided, not deferred: at around twenty endpoints the API moves to Hono, a small, fast, standards-based router that runs inside the same Next.js app on Vercel, paired with zod-openapi so the spec is generated from the code instead of maintained beside it.

Front-end

We use React 19 with the Next.js App Router. Pages are Server Components first; TanStack Query handles the few client-side reads that need to stay live.

Styling is Tailwind CSS v4 with shadcn/ui components built on Base UI primitives, themed to Geist, Vercel's design system. Every colour on screen is a Geist scale token: a lint rule rejects raw hex values, and a contrast test reads the stylesheet so light and dark both stay accessible.

The dashboard speaks six languages: English, Chinese, Vietnamese, Japanese, Korean and French. The server reads the locale from a cookie and sends exactly one dictionary per request, and CI fails on any key that is missing from a locale or was left in English.

Icons are Lucide, with brand marks from Simple Icons. Charts are Recharts.

Typography

Our typeface is Geist Sans, with Geist Mono for code, identifiers and numbers. Geist is the typeface of the design system we follow, so the text reads as one family with the tokens around it rather than a font laid on top of someone else's system.

We self-host the variable build from vercel.com (open source under the OFL) instead of loading it from Google Fonts. The Google build ships without the stylistic sets, and we set font-feature-settings: "calt" 0, "rlig", "ss11", measured off vercel.com, which only takes effect on the original binary. One file covers every weight from 100 to 900.

Geist was drawn for interfaces: a large x-height and open counters keep 13–14px labels legible, negative tracking tightens only the headings, and body text carries none. Geist Mono has tabular figures, so quantities and money line up in tables. We deliberately run one family and at most two weights per view, which is the spec's rule and the reason nothing here is set in a serif.

Auth

We use Auth.js v5 with JWT sessions and three ways to sign in: Google OAuth, email and password (bcrypt), and a one-time code by email for sign-up verification and passwordless fallback.

Roles and permissions are a plain policy module in shared, evaluated on the server, with a unit test over the whole matrix. A browser can hold several signed-in accounts and switch between them without signing out.

Data & storage

PostgreSQL 17 on Neon is our only database. The app talks to it through a pooled, least-privilege connection; migrations use a separate direct URL.

Prisma 7 is the ORM, with the pg driver adapter. The schema is split one file per domain, and every change is an SQL migration committed to git: we never push a schema straight to a database. Operational tables live in their own ops schema so Prisma's drift check stays honest.

Search is pg_trgm inside Postgres, with no separate search service. Files such as mockups and imports go to Vercel Blob. Every sensitive write lands in an audit table.

Deployments & infrastructure

The dashboard deploys on Vercel: a Preview Deployment per branch, production on the apex domain, and Vercel Cron for the nightly webhook retry and tracking reconciliation.

CI is GitHub Actions. Beyond type-check, lint and build, every run applies the migrations to an empty database and fails on schema drift, runs the money invariants against a throwaway database, checks the rate limiter, rehearses the legacy cutover and verifies the i18n dictionaries.

We also build the app as a standalone Node 24 container and boot it in CI, so moving off Vercel to ECS or a plain VM stays a verified fact rather than a hope.

Integrations

Resend sends every transactional email. KiloShips prints shipping labels and reports tracking events through signed, replay-safe inbound webhooks.

Updates to customers and the team go out over Zalo and WhatsApp through the SimpliDeliver API, our own messaging platform: one API key, one endpoint, and the same send call whichever chat app the recipient uses.

Sellers receive outbound webhooks for order events, signed so they can verify the sender, with automatic retries. Vercel Analytics and Speed Insights cover traffic and Web Vitals.

A small cron-driven sync keeps data flowing from the previous system during the cutover, with checksum comparison and email alerts when it falls behind.

Coming next: Stripe for card top-ups and invoices, and Telegram as a third messaging channel beside Zalo and WhatsApp.

AI

The in-app assistant and the order-import matcher are built on the Vercel AI SDK. The provider is configured by an admin at runtime: Anthropic's Claude models directly or through the AI Gateway, or an OpenAI-compatible endpoint such as Moonshot's Kimi. Structured answers come from forced tool calls rather than free text.

AI stays inside the dashboard. The public API never calls a model, so integrations are deterministic and a model outage cannot block an order.

Testing

We use Node's built-in test runner (node --test) and no test framework. We prefer a few invariants over a coverage number: the money test applies the real migrations to a scratch database and checks that balances and ledgers still add up after every kind of transaction.

Type-check and lint are the first gate and run on every change; the database suites run in CI and before anything touching the schema or money is merged.

Observability

Vercel's runtime logs and Speed Insights for the app, /api/health as a liveness probe, and the audit table for who changed what. We keep it deliberately small and add a tool when a question goes unanswered, not before.

Miscellaneous

  • npm workspaces with Turborepo; Node 24 pinned in .nvmrc, and repo scripts run on node --experimental-strip-types with no build step
  • TypeScript in strict mode across every package
  • ESLint 9 and Prettier, plus house rules that reject raw colours and cross-domain imports
  • Claude Code for AI-assisted development, with a knowledge graph of the repository rebuilt on every commit
  • Docs in Markdown; the changelog is data in the repo, published as a page and an Atom feed
NextAPI reference
OpCreative

Print-on-demand fulfillment, simplified.

  • Instagram
  • Facebook
  • Messenger
  • X
  • Threads
  • YouTube
  • TikTok
  • Pinterest
  • Snapchat
  • WhatsApp
  • Zalo
  • Telegram
  • WeChat
  • LINE
  • Reddit
  • Discord

Legal

  • Privacy Policy
  • Terms of Service

Product

  • Orders
  • Catalogue
  • Wallet
  • Analytics

Developers

  • API reference
  • Connect AI
  • OpenAPI spec
  • Tech stack

Company

  • Changelog
  • About
  • Help & Support
  • Contact

© 2026 OpCreative. All rights reserved.

Designed & developed byNiyam VoraNNiiyyaamm  VVoorraa
  • GitHub
  • Zalo