Skip to content
Loading...

Case study

Building Odesk: turning a fast internal tool into a full-stack operations platform

A project about architecture, integrations, AI-assisted workflows, WooCommerce edge cases, and the slow work of making dangerous automation safe enough to trust.

Next.js 16 front end with authenticated workspace views and proxy/BFF routes

NestJS backend with PostgreSQL, TypeORM modules, JWT auth, and domain services

WooCommerce catalog sync, enrichment, re-enrich previews, and guarded apply flows

Meta OAuth, encrypted credentials, publishing, insights sync, and webhooks

AI-assisted scraping, discovery, translation, product copy, and diagnostics

Migration from legacy SQLite-heavy routes toward Nest-owned business logic

The Starting Point

Odesk began as a practical internal platform: one place to manage clients, brands, content, catalog work, publishing, reports, tasks, and operational data. Like many real projects, it did not start perfectly separated. The first version optimized for speed, with a Next.js app holding both product UI and a lot of backend behavior.

That was useful early on, but it created pressure as the project grew. Product enrichment, WooCommerce sync, Meta publishing, dashboards, assets, documents, and AI jobs all wanted reliable state, clearer ownership, and stronger boundaries.

Splitting The System

The main architecture move was turning the Next.js app into a thinner front end and BFF, while moving durable business logic into a NestJS backend backed by PostgreSQL. Next.js still owns the user-facing experience, cookies, streaming UI, uploads, and proxy routes. NestJS owns the canonical modules, data model, integrations, and long-running workflows.

That split made the system easier to reason about. Instead of every route inventing its own behavior, the backend became a set of domain modules: workspaces, brands, content, catalog, WooCommerce, Meta, assets, reports, tasks, tickets, notifications, AI jobs, and observability.

Commerce And Enrichment

The hardest part was the catalog. A product is not just a title and an image. It can have manufacturer SKUs, WooCommerce IDs, Hebrew copy, image galleries, categories, global attributes, variable products, variation SKUs, and store-specific rules.

I built workflows for discovery, scraping, product enrichment, Hebrew translation, WooCommerce template generation, previewing changes, and applying approved updates. The re-enrich flow became a good example of the kind of engineering this project required: the UI had to be useful, but the backend had to be conservative enough not to damage existing products.

A Real Debugging Story

One bug looked simple from the UI: re-enrich produced bad variation results. Underneath, the problem touched WooCommerce variation semantics, SKU identity, parent attributes, global taxonomy attributes, Hebrew and RTL text, and whether an update should create new rows or only modify existing ones.

The fix was not just a patch. I changed the process. Re-enrich now fetches live variation hints from the same WooCommerce connection used for apply, preserves the length-to-SKU pairing, requires the preview variation SKUs to exactly match live Woo variations, and updates only existing variation attributes by variation ID. It does not create or delete variations, invent SKUs, or touch stock, price, or status.

Integrations

Meta integration added another shape of complexity. The platform supports OAuth, encrypted credential storage, workspace and brand-level connections, Facebook Page and Instagram Business asset attachment, publishing, insight syncing, and webhooks. The frontend never needs raw tokens; it talks through safe proxy routes and the backend owns the Graph API details.

WooCommerce follows a similar principle. Store connections, catalog sync, product enrichment, and updates are treated as backend-owned workflows, with the frontend focused on review, approval, and diagnostics.

What I Learned

This project taught me that a business platform is less about isolated features and more about state, trust, and recovery. A beautiful screen is not enough if the write path can damage production data. A clever AI flow is not enough if it cannot explain what source data it used. A migration is not done just because the backend exists; the front end has to stop relying on the old paths.

The strongest parts of the project came from treating bugs as design feedback. Each failure clarified a boundary: what should be generated, what should be preserved, what must be matched by SKU, what should fail closed, and what deserves a test before it reaches a real store.

Stack

Next.js, React, TypeScript, Tailwind, NestJS, PostgreSQL, TypeORM, WooCommerce REST API, Meta Graph API, OpenAI, Jest, Vitest, and Playwright.

← Back to portfolio