Concepts

Architecture

Where new code goes, in your app, a module or a package, and why dependencies between modules point one way.

When you add code to a Frigear app, it can go in the app itself, in a module or in a separate package. The wrong place makes it hard to reuse, or ties a module to one app. After this page you can decide where a piece of code belongs and what it may depend on.

For: app developers and module authors.

Which one do I use?

You are writingPut it inRead
A page or API that only your product needsYour host appProject structure
A feature another app could reuseA new module under packages/Writing a module
A helper for several modules that is not Nuxt-specificA package under packages/Four kinds of code
A decision about who may do whatA role, a policy or the authorization:before hook in your host app, never the moduleAccess control
Prefer adding shared logic to a package over copying it between apps. Do not create a package for a single one-off helper.

Terms

  • Host app. The Nuxt app that installs Frigear modules and configures them.
  • Module. A Nuxt module published as @frigear-nu/<name> that adds one feature to a host app.
  • Base layer. The shared Nuxt layer a host app extends for config, interface defaults and content pages.
  • Package. Any npm package in the repository, named in install and publish steps and for tooling that is not a module.

How it works

Four kinds of code

KindWhere it livesWhat it is
Host appapps/*, or apps/web in a starterA deployable Nuxt app, such as the Frigear website app. It registers modules and adds what is specific to it.
Base layerpackages/base-layer@frigear-nu/base: shared Nuxt config, interface defaults and content-driven pages for websites.
Modulepackages/<module>A Nuxt module for one feature, such as users or stripe.
Packagepackages/*Tooling and shared code that is not a Nuxt module, such as the CLI and the ESLint config.
For contributors. In the Frigear repository, apps and packages live in one pnpm workspace. Apps link to packages with the workspace:* protocol. Packages depend on each other with workspace:^, so published versions are carets. Run every command from the repository root (Local development).

What a host app does

A host app is thin. It lists the modules it wants, configures them, decides what the modules leave open, and adds its own pages and tables:

  • Modules in nuxt.config.ts. It lists only the modules it uses directly (Adding modules).
  • Policy. Modules ask the host app before they act. The host app decides who counts as staff, which roles an OAuth application accepts and which claims a token carries. It answers in Nitro hooks.
  • Its own database columns. It extends module tables from server/db/extend.<dialect>.ts and commits the migrations (Database extensions).
  • Its own pages and strings. A page at the same path as a module page replaces it. Any module string can be overridden in the host app's i18n/locales/<code>.json.

Dependencies point one way

A module declares the modules it needs, and Nuxt installs them in the right order. The chains are listed in How they fit together.

The rule underneath is that dependencies point one way:

  • auth sits below users. It reaches users only through the user store that users registers (AuthUserStore).
  • module-kit depends on no feature module, and almost every module depends on it.
  • Anything optional, such as menus or widgets, is an optional dependency and guarded. The module still works when it is absent.

Composing without importing

Modules rarely import each other's code. They add to each other through extension points:

  • Registries, filled at build time. Registration order does not matter.
  • Hooks, answered at run time. A module asks a question, such as authorization:before, and the host app answers it.
  • Database extensions. A module adds columns to a table another module owns, without the owner knowing.

So you can install a module and get its pages, menu items, dashboard widgets and MCP tools without touching any other module.

Common mistakes

Deciding policy inside a module. Who is staff is the host app's decision, not the module's. A module that decides it ties itself to one app's roles. Check a permission with authorize instead, and let the host app's roles and the authorization:before hook decide who holds it.
Importing a module that sits above yours. Dependencies point one way: auth never imports users, and reaches users only through the user store. Follow the same rule and use an extension point instead of an import.

Next steps

  • Access control – see how a route learns who is calling and what they may do.
  • Extension points – add pages, menus and widgets from a module.
  • Modules – find the module that already does what you need.
Copyright © 2026