Skip to content

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

pip install 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) or X-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-Match on the next PATCH/PUT/DELETE for the same document.
  • Request IDs — forwards X-Request-Id to the API for correlation with server logs.
  • Error surface — rejects with a typed ApiError carrying code, message, and details from 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.