Project Templates¶
craft-easy-template is a cookiecutter project that scaffolds ready-to-run Craft Easy services. It ships two separate templates — one for an API service, one for a frontend app — each with its own prompts and generated layout.
Use the templates when you want a new project that already has the wiring, infrastructure, and CI you would otherwise have to assemble by hand. If you prefer to start from scratch, see the manual setup at the bottom of Quick Start.
Installing cookiecutter¶
You can invoke the templates straight from GitHub, or point at a local checkout if you have cloned craft-easy-template:
# From GitHub
cookiecutter gh:easy-software-system/craft-easy-template --directory=api
cookiecutter gh:easy-software-system/craft-easy-template --directory=frontend
# From a local clone
cookiecutter /path/to/craft-easy-template --directory=api
The API template¶
Scaffolds a complete Python service: FastAPI app factory, Craft Easy settings, a sample resource, Dockerfile, docker-compose.yml, infrastructure modules for your chosen cloud, and a working test suite.
Prompts¶
The API template asks the following questions. All values have sensible defaults — press enter to accept them.
| Prompt | Default | Description |
|---|---|---|
project_name |
My API |
Human-readable project name, shown in README.md and APP_TITLE. |
project_slug |
my-api |
Directory and Docker image name. |
package_name |
my_api |
Python package name used in imports (from my_api.app import ...). |
description |
A REST API built on Craft Easy |
Shown in pyproject.toml and README.md. |
author_name |
Your Name |
Written to pyproject.toml. |
author_email |
you@example.com |
Written to pyproject.toml. |
python_version |
3.12 |
Minimum Python version pinned in pyproject.toml. |
craft_easy_version |
>=0.1 |
Version constraint for the craft-easy-api dependency. |
database_uri |
mongodb://localhost:27017 |
Default MongoDB URI written to .env.example. |
database_name |
{package_name}_dev |
Default database name written to .env.example. |
auth_method |
jwt | oauth2 | otp |
Pre-configures the authentication pipeline and seeds example routes. |
cloud_provider |
azure | gcp | none |
Selects which infrastructure module is copied into infrastructure/. |
cloud_region_azure |
swedencentral |
Azure region used in generated Bicep modules. |
cloud_region_gcp |
europe-north1 |
GCP region used in generated Terraform modules. |
enable_jobs_deployment |
no |
If yes, also generates infrastructure for a standalone jobs service. |
Feature flags¶
Six yes/no prompts toggle whole subsystems. Each flag pulls in the relevant settings, sample models, and tests. Flags you say no to leave no trace in the generated project.
| Flag | What it enables |
|---|---|
enable_jobs |
Job framework wiring in app.py, a sample job in src/{package}/jobs/, and JOBS_ENABLED=true in .env.example. |
enable_file_import |
File import engine, sample pipeline in src/{package}/imports/, FILE_IMPORT_ENABLED=true. |
enable_payments |
Payment provider registration, Stripe env vars in .env.example, PAYMENTS_ENABLED=true. |
enable_bookkeeping |
Chart of accounts seed, bookkeeping routes, BOOKKEEPING_ENABLED=true. |
enable_gdpr |
GDPR export/erasure routes and retention job stubs, GDPR_ENABLED=true. |
enable_bi_export |
BI export job, sample export handler, BI_EXPORT_ENABLED=true. |
What you get¶
my-api/
├── pyproject.toml
├── README.md
├── .env.example
├── Dockerfile
├── docker-compose.yml
├── src/
│ └── my_api/
│ ├── __init__.py
│ ├── app.py # create_app() factory with resource registration
│ ├── settings.py # Settings(BaseSettings) with project defaults
│ ├── models/
│ │ └── example.py # Sample TenantScopedDocument
│ └── routes/
│ └── __init__.py
├── tests/
│ ├── conftest.py # pytest fixtures, in-memory MongoDB
│ └── test_example.py # End-to-end CRUD test
├── infrastructure/
│ └── modules/ # Bicep (Azure) or Terraform (GCP)
└── .github/
└── workflows/
└── ci.yml # Lint, type-check, pytest
The generated app.py already registers the sample resource and wires every enabled module:
from craft_easy import create_base_app
from craft_easy.core.crud import Resource
from my_api.models.example import Example
from my_api.settings import Settings
def create_app():
settings = Settings()
app = create_base_app(settings=settings)
app.register_resource(Resource(Example, path="/examples"))
return app
Running the generated project¶
cd my-api
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Either boot MongoDB yourself, or use the included compose file:
docker compose up -d mongo
cp .env.example .env
uvicorn my_api.app:create_app --factory --port 5001
Open http://localhost:5001/docs for the interactive OpenAPI UI. Run pytest for the test suite.
The frontend template¶
Scaffolds an Expo app that talks to a Craft Easy API. The generated app is schema-driven — it calls /admin/schema on startup, discovers resources, and renders list/detail/create/edit screens without hand-written UI per resource.
Prompts¶
| Prompt | Default | Description |
|---|---|---|
project_name |
My App |
Human-readable app name, shown in app.json and the splash screen. |
project_slug |
Derived from project_name |
Directory and npm package name. |
api_url |
http://localhost:5001 |
Default API base URL the client points at. |
auth_method |
jwt | api_key |
Selects which authentication flow is pre-wired. |
description |
A Craft Easy frontend application |
Written to package.json. |
author_name |
Developer |
Written to package.json. |
cloud_provider |
azure | gcp | none |
Selects hosting infrastructure for the web build. |
cloud_region_azure |
swedencentral |
Azure region for Static Web Apps. |
cloud_region_gcp |
europe-north1 |
GCP region for Cloud Run / Storage. |
What you get¶
my-app/
├── package.json
├── app.json # Expo configuration
├── tsconfig.json
├── src/
│ ├── api/
│ │ ├── client.ts # fetch wrapper with auth + ETag + retry
│ │ ├── schema.ts # /admin/schema loader and cache
│ │ └── resource.ts # useResource hook (Zustand store)
│ ├── auth/
│ │ └── provider.tsx # JWT or API-key provider
│ ├── screens/
│ │ ├── Connect.tsx
│ │ ├── Login.tsx
│ │ ├── ResourceList.tsx
│ │ ├── ResourceDetail.tsx
│ │ └── ResourceForm.tsx
│ ├── theme/
│ │ └── tokens.ts # Design tokens, light/dark palettes
│ └── navigation/
│ └── index.tsx # react-navigation stack + tabs
└── app/
└── _layout.tsx # Expo Router entry point
The API client¶
The generated src/api/client.ts is a thin wrapper around fetch that handles:
- Authentication — attaches
Authorization: Bearer <token>(JWT) orX-API-Key: <key>(API key) from secure storage. - Token refresh — on 401, calls the refresh endpoint and retries the original request once.
- ETag concurrency — captures ETag headers on GET and automatically sends
If-Matchon the next PATCH/PUT/DELETE for the same document. - Request IDs — forwards
X-Request-Idto the API for correlation with server logs. - Error surface — rejects with a typed
ApiErrorcarryingcode,message, anddetailsfrom the Craft Easy error envelope.
The useResource hook¶
import { useResource } from "./api/resource";
export function ProductList() {
const { items, isLoading, refresh, create, update, remove } = useResource("products");
if (isLoading) return <Spinner />;
return <ProductTable rows={items} onEdit={update} onDelete={remove} />;
}
useResource is backed by a Zustand store, so multiple components share the same cache and stay in sync. It automatically paginates, applies filters, and exposes loading and error states.
Running the generated project¶
cd my-app
npm install
npm run web # Expo web
# or
npm run ios # iOS simulator
npm run android # Android emulator
Updating a generated project¶
The templates are a starting point, not a framework you inherit from. Once you have run cookiecutter, the generated code is yours — edit it freely. If craft-easy-template ships new features later, pull them in selectively rather than regenerating the whole project.
Source¶
craft-easy/craft-easy-template/api/cookiecutter.json— API template prompts and defaults.craft-easy/craft-easy-template/api/{{cookiecutter.project_slug}}/— API template body.craft-easy/craft-easy-template/frontend/cookiecutter.json— frontend template prompts.craft-easy/craft-easy-template/frontend/{{cookiecutter.project_slug}}/— frontend template body.craft-easy/craft-easy-template/api/hooks/— post-generation hooks that strip disabled feature flags.