Skip to content

02 — Architecture overview

This doc describes the big-picture architecture of the Someli frontend so you can place new code correctly without re-deriving it from scratch.

Two apps in one repo

someli-platform/                ← Nuxt 2 / Vue 2 SPA (the "main app")
├── pages/                      ← Nuxt routes
├── components/                 ← Vue components
├── layouts/                    ← Page layouts (default, error, onBoard, …)
├── store/                      ← Vuex modules
├── middleware/                 ← Nuxt router middleware (axios interceptor, redirects)
├── api/                        ← Nuxt server middleware (small Express helpers, OAuth callbacks)
├── plugins/                    ← Nuxt plugins (auth, gtm, snowfall, polotno-related, …)
├── helpers/                    ← Shared JS utilities
├── constants/                  ← PROVIDER, PLAN, PATH, etc.
├── nuxt.config.js              ← The single source of truth for app config
├── polotno-bundle.js, .css     ← Prebuilt React/Polotno editor bundle (output of polotno-editor/)
└── polotno-editor/             ← Independent React 18 + MobX-State-Tree app (Parcel build)

The Nuxt app is a pure SPA (ssr: false in nuxt.config.js). There is no server-side rendering — Nuxt is used as a development environment, build pipeline, and routing layer, not as an SSR framework.

The Polotno editor is a separate React app that builds with Parcel into a single JS+CSS bundle written into the root of the main repo. Editor pages in the Nuxt app import that bundle as an ES module:

import { createEditor } from '../../../../polotno-bundle'

The two projects have separate package.json files, separate node_modules, and separate lockfiles. They share no source code.

Request lifecycle for an authenticated page

When a logged-in user navigates to, say, /<accountId>/contentplanner:

Browser
Nuxt router middleware chain (defined in nuxt.config.js → router.middleware)
  1. auth         → middleware/redirect.js  (decides where the user belongs)
  2. axios        → middleware/axios.js     (installs an axios interceptor for this nav)
  3. validUrl     → middleware/validUrl.js  (records last-known-good path)
  4. appendUTM    → middleware/appendUTM.js (decorates outbound links with UTM)
  5. appendUrl    → middleware/appendUrl.js (rewrites unprefixed URLs to /<accountId>/...)
Page component (pages/_accid/contentplanner/...)
Component-level data fetch
  ├─ store.dispatch('api/fetchSubscription')
  ├─ store.dispatch('api/fetchAccountDetails')
  └─ this.$axios.get('/auth/getMyListPosts')
axios interceptor (middleware/axios.js)
  ├─ Adds `token` header (from cookie) for /auth/* URLs
  ├─ Adds `accountId` header (from cookie.usedetail) for /auth/* URLs
  └─ Adds base64(`apptype`) header on every request
Backend API at process.env.API_URL

There are four important things to internalise from this picture:

  1. Identity lives in cookies, not in the Vuex store. The store sometimes mirrors cookie values, but cookies are the source of truth — see 03-auth-and-sessions.md for the full inventory.
  2. The axios interceptor only adds auth headers for /auth/... URLs. Endpoints outside that prefix are unauthenticated from the frontend's perspective.
  3. store/api.js is a 5-minute cache layer in front of the most-used endpoints. Components should prefer dispatching api/fetch* actions over calling $axios directly. See 05-state-management.md.
  4. Routes are rewritten by appendUrl middleware to inject the accountId. A bare /contentplanner link is intentionally allowed — the middleware will redirect to /<accountId>/contentplanner. See 04-routing.md.

Layering / module map

Layer Where it lives Notes
Routing pages/, nuxt.config.js → router.extendRoutes File-system routing. pages/_accid/... is the dynamic-account-id segment that holds most authenticated routes.
Layouts layouts/default.vue, error.vue, onBoard.vue, someliNetwork.vue, specialOffer.vue, fourOhOne.vue Pages pick a layout via the layout option.
Middleware (router) middleware/ Run on every navigation, in the order declared in nuxt.config.js. Specific pages can also opt in to per-page middleware (e.g. editor pages declare middleware: 'auth').
Middleware (server) api/index.js, api/twitter-middleware.js, api/test-middleware.js Express handlers mounted by serverMiddleware in nuxt.config.js. Not the backend — small helper endpoints only. The only currently-live endpoint in api/index.js is GET /api/youtube-videos; the rest is commented-out scaffolding.
Plugins plugins/ Initialise singletons (auth, GTM, Clarity, vue-snotify, Polotno-adjacent client-only libs, …). Many are declared ssr: false / mode: 'client' because we don't SSR.
State store/ Vuex 3 in non-strict mode (export const strict = false). Modules: post/, dashboard/, chat/, leaderboard/, plan/, reach/, common/, onboarding, api, modules/sitemap.
Components components/ Auto-registered (components: true in nuxt.config.js). Subdirectories group related components (Calender/, Charts/, Dashboard/, Buttons/, Cards/, Sidebars/, …).
Helpers helpers/helper.js, helpers/circleNetworkHelper.js, helpers/mixins/, helpers/models/ Pure utilities and shared mixins. Notably, helpers/helper.js owns the post-status state machine (renderPostStatus, calStatus) and the billing-plan filter (getPlanList). See 13-post-lifecycle.md and 17-helpers.md.
Constants constants/constants.js, constants/authProviders.js PROVIDER, PLAN, PATH, INVALID_RESPONSE, etc.
Static assets static/, assets/ static/ is served verbatim (/owl.carousel.min.js, /common.js, /identity.js, /universal.tag.js, …). assets/ is processed by the build (CSS, SCSS, images).
Editor polotno-editor/ Independent React app. See 06-polotno-integration.md.

Cross-cutting concerns

Authentication

@nuxtjs/auth v4 with the local strategy. Login endpoint is webauthenticate; the user-fetch endpoint is me. Logout is disabled at the strategy level — the app calls app.$auth.logout() directly and clears cookies plus localStorage itself. See 03-auth-and-sessions.md.

Account scoping

After login, every meaningful URL is prefixed with the user's accountId. The appendUrl middleware actively rewrites unprefixed URLs in a hardcoded allow-list (/contentplanner, /dashboard/*, /billing, /userSetting, …) to insert the account ID. See 04-routing.md.

Caching

store/api.js is the single most important state file in the project. It wraps ~12 backend endpoints in cache-with-TTL actions (fetchSubscription, fetchUserDetails, fetchSettings, fetchPosts(page), fetchFavPosts(page), …). The default TTL is 5 minutes. See 05-state-management.md.

Third-party integrations

Most external services are integrated by either (a) a global <script> tag in nuxt.config.js → head.script (jQuery, Bootstrap, Stripe, Paddle, Clarity, slick-carousel, Flagmeister, …), or (b) a Nuxt plugin in plugins/ (GTM via @nuxtjs/gtm, Hotjar, Fullstory, Clarity, Salesforce IQ, …). See 08-integrations.md.

Where to start when you have a feature to build

Read in this order:

  1. The page file in pages/_accid/... (or wherever it belongs).
  2. The Vuex module(s) it dispatches against.
  3. store/api.js to see if the backend call is already wrapped there.
  4. middleware/redirect.js if the feature has any post-login or auth-state implication.
  5. The relevant component subdirectory in components/.

When in doubt, grep for an analogous existing feature and copy its structure rather than inventing a new one.