Project Structure
Every Axis project splits into three areas: typed configuration in config/, stack composition in cdk/, and handler code in lambdas/. Two files at the root describe the project to tools — STRUCTURE.md for humans and AI assistants, and axis.json as the machine-readable contract.
The tree
Section titled “The tree”The rest-v1 starter in module layout is the canonical shape (leaner starters are subsets of it):
my-api/├── axis.json ← starter + layout + paths/conventions (v1 schema)├── STRUCTURE.md ← human-readable narrative of this tree├── package.json · tsconfig.json · cdk.json · jest.config.js├── .npmrc ← @axis-tech-co scope → registry mapping (no token)├── cdk/│ ├── bin/app.ts ← stack composition, gated by feature toggles│ └── lib/stacks/│ ├── api-stack.ts ← project subclass of the abstract ApiStack│ ├── budget-stack.ts│ └── observability-stack.ts├── config/│ ├── types.ts ← MyApiConfig extends BaseEnvConfig│ ├── dev-config.ts / prod-config.ts│ ├── loader.ts ← createEnvLoader — single env-selection source│ ├── features.ts ← FeatureToggles + DEFAULT_FEATURES│ ├── lambdas.ts ← apiConfigs[] registry│ ├── lambda-configs/ ← one config file per route│ ├── tables/ ← central table definitions + index.ts registry│ ├── buckets/ keys/ queues/ secrets/ topics/ (per-construct config groups)│ ├── security/{ses,waf}.ts│ ├── eventbridge/index.ts│ └── budget/index.ts├── lambdas/│ └── modules/│ ├── health/api/check/index.ts│ └── items/api/{create,get,update,patch,delete}/index.ts└── tests/synth.test.ts ← snapshot + assertion tests over the synth outputconfig/ — typed configuration
Section titled “config/ — typed configuration”types.tsdeclares your project’s config interface extendingBaseEnvConfig(which carriesappNameandstageName, the two values that drive physical naming and SSM paths).dev-config.ts/prod-config.tshold the per-environment values;loader.ts(createEnvLoader) selects between them from the--context env=dev|prodflag. One selection mechanism, used everywhere.features.tsdeclares which construct groups the app instantiates — see Feature Toggles.lambdas.tsandtables/index.tsare the two registries — plain typed arrays (LambdaConfig[], table definitions) assembled from the per-item files around them.- The remaining directories (
queues/,topics/,keys/,buckets/,secrets/,security/,eventbridge/,budget/) each configure one construct group, one file per resource.
cdk/ — stack composition
Section titled “cdk/ — stack composition”cdk/bin/app.ts reads the resolved feature toggles and instantiates only the stacks whose groups are on. cdk/lib/stacks/ holds your project’s subclasses of the framework’s abstract stacks (ApiStack, ObservabilityStack, BudgetStack, …) — each subclass implements small hook methods (for example getLambdaConfigs()) that hand the registries to the framework. The stack topology and its wiring rules are covered in Stacks & SSM Wiring.
lambdas/ — handler code
Section titled “lambdas/ — handler code”Handlers live under lambdas/modules/ (or lambdas/features/ in feature layout — see Layout). Each handler directory pairs an index.ts entry point with a service.ts for logic, and axs add lambda generates new ones in the right place by reading axis.json.
The contract is the registries
Section titled “The contract is the registries”The framework itself is deliberately layout-agnostic: its only contract with your code is the config/lambdas.ts and config/tables/index.ts registries. Both layouts produce identical registries — only file locations and import paths differ — and the framework never reads axis.json (only tooling does). That is what keeps the directory conventions a tooling concern you can evolve, rather than a framework constraint you’re stuck with.
Tests ship with the scaffold
Section titled “Tests ship with the scaffold”tests/synth.test.ts snapshot-tests the synthesized templates for dev and prod. It is the project-local version of the framework’s own examples-as-oracle discipline: infrastructure changes show up as reviewable template diffs before they reach AWS.
© 2026 Axis Tech. Powered by axis-tech.co.