Shape: Full-Stack
A full-stack project has two stacks -- a frontend and a backend -- in the same repository. Each stack has its own language, test runner, build process, and deployment target. The main configuration challenge is making CI smart enough to test only the stack that changed.
When this shape applies
Use the full-stack shape when your project has:
- A frontend (React, Vue, Angular, Svelte) and a backend (Express, Django, Rails, Spring Boot) in the same repository.
- Two distinct build and test processes.
- Two deployment targets (e.g., a static site host for the frontend and a container platform for the backend).
If you have three or more deployable services, see Shape: Microservices. If your application is a single server-rendered framework (Rails with embedded views, Django with templates), see Shape: Single Repo.
Folder layout
The recommended layout puts each stack in its own top-level directory:
my-project/
.gaia/
config/
project-config.yaml
docs/
planning-artifacts/
implementation-artifacts/
packages/
web/ # Frontend stack
src/
tests/
package.json
api/ # Backend stack
src/
tests/
requirements.txt # (or package.json, go.mod, etc.)
README.md
Alternative layout names are fine (frontend/ and
backend/, client/ and server/,
apps/web/ and apps/api/). What matters is
that each stack has a distinct path that GAIA can use for path-based
CI filtering.
Configuration
The stacks section has two entries, each with its own
path:
# .gaia/config/project-config.yaml
project:
name: my-fullstack-app
description: A task management application
stacks:
- name: frontend
language: typescript
framework: react
path: packages/web
test_command: npm test
build_command: npm run build
lint_command: npm run lint
- name: backend
language: python
framework: fastapi
path: packages/api
test_command: pytest
build_command: docker build -t my-api .
lint_command: ruff check src/
platforms:
- web
The path field is what makes path-based CI filtering
work. When a PR changes only files under packages/web/,
GAIA's generated CI workflow runs only the frontend test job.
Path-based CI filters
Path filters prevent unnecessary test runs. Without them, every change triggers both frontend and backend tests, doubling your CI time.
# .gaia/config/project-config.yaml
ci_cd:
platform: github-actions
preset: small-team
promotion_chain:
- staging
- production
path_filters:
enabled: true
concurrency:
group: ci-${{ github.ref }}
cancel_in_progress: true
With path_filters.enabled: true, running
/gaia-ci-setup
generates workflow triggers that match each stack's path:
- Changes to
packages/web/**trigger the frontend job. - Changes to
packages/api/**trigger the backend job. - Changes to root-level files (
project-config.yaml,README.md) trigger both jobs.
Shared dependencies
If both stacks depend on a shared library (e.g.,
packages/shared/), changes to the shared directory
should trigger both test suites. Path filters include root-level
and shared paths in the trigger for both stacks by default.
Per-stack test execution
When
/gaia-dev-story
implements a story, it determines which stack the story belongs to
(based on the files being changed) and runs only that stack's test
command.
If a story touches both stacks (e.g., adding a new API endpoint and the frontend component that calls it), both test commands run. This is the expected behavior for cross-stack stories.
Configure per-stack test settings in the test_execution
section:
# .gaia/config/project-config.yaml
test_execution:
default_command: npm test
timeout_seconds: 300
coverage:
enabled: true
threshold: 80
The default_command is a fallback. Each stack's
test_command takes precedence when GAIA knows which
stack to test.
Deployment considerations
In a full-stack project, the frontend and backend typically deploy to different targets:
- Frontend: Static hosting (Vercel, Netlify, S3 + CloudFront, GitHub Pages).
- Backend: Container platform (AWS ECS, Google Cloud Run, Kubernetes) or serverless (AWS Lambda, Cloud Functions).
Each stack can have its own deployment configuration in the
environments section:
# .gaia/config/project-config.yaml
environments:
staging:
frontend_url: https://staging.example.com
backend_url: https://api.staging.example.com
auto_deploy: true
production:
frontend_url: https://example.com
backend_url: https://api.example.com
auto_deploy: false
requires_approval: true
When deploying, consider whether the frontend and backend need to be deployed together (because of breaking API changes) or can be deployed independently. Independent deploys are faster and safer but require backward-compatible APIs.
Complete example
Here is a full project-config.yaml for a React +
FastAPI application:
# .gaia/config/project-config.yaml
project:
name: taskmaster
description: A task management application with React frontend and FastAPI backend
stacks:
- name: frontend
language: typescript
framework: react
path: packages/web
test_command: npm test -- --coverage
build_command: npm run build
lint_command: npm run lint
- name: backend
language: python
framework: fastapi
path: packages/api
test_command: pytest --cov=src --cov-report=term-missing
build_command: docker build -t taskmaster-api packages/api
lint_command: ruff check packages/api/src/
platforms:
- web
ci_cd:
platform: github-actions
preset: small-team
promotion_chain:
- staging
- production
path_filters:
enabled: true
concurrency:
group: ci-${{ github.ref }}
cancel_in_progress: true
triggers:
pull_request:
checks:
- lint
- unit-tests
- integration-tests
push_to_staging:
checks:
- smoke-test
push_to_production:
checks:
- smoke-test
- deploy
environments:
staging:
frontend_url: https://staging.taskmaster.example.com
backend_url: https://api.staging.taskmaster.example.com
auto_deploy: true
production:
frontend_url: https://taskmaster.example.com
backend_url: https://api.taskmaster.example.com
auto_deploy: false
requires_approval: true
test_execution:
default_command: npm test
timeout_seconds: 300
coverage:
enabled: true
threshold: 80
smoke:
command: npm run test:smoke
timeout_seconds: 60
What to read next
- Configuring CI Pipelines -- trigger strategy and path filters in detail.
- Test Strategy Configuration -- per-stack test commands and the Test Execution Bridge.
- Environments and Promotion -- deploying frontend and backend to different targets.
- Shape: Microservices -- when your project grows beyond two stacks.
/gaia-config-stack-- editing stack configuration.