Skip to content

Quick Start

Build and run a Craft Easy API in under five minutes. At the end you will have a running API with a Product resource, automatic CRUD endpoints, and a working curl flow.

1. Create the project

The fastest path is the API cookiecutter template:

pip install cookiecutter
cookiecutter gh:easy-software-system/craft-easy-template --directory=api

Answer the prompts (defaults are fine for a first run) and cd into the generated directory:

cd my-project
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

If you prefer to start from scratch, see the manual setup at the end of this page.

2. Define a model

Open src/my_project/models/product.py and define a Beanie document:

from beanie import Document, Indexed
from pydantic import Field
from craft_easy.core.models import TenantScopedDocument


class Product(TenantScopedDocument):
    name: Indexed(str)
    price: float = Field(..., ge=0)
    sku: str
    in_stock: bool = True

    class Settings:
        name = "products"

TenantScopedDocument adds tenant_id, soft-delete, timestamps, and audit fields automatically. Every query is filtered by the caller's tenant.

3. Register the resource

In src/my_project/app.py, register the model as a resource:

from craft_easy import create_base_app
from craft_easy.core.crud import Resource
from my_project.models.product import Product

def create_app():
    app = create_base_app()
    app.register_resource(Resource(Product, path="/products"))
    return app

That single register_resource call gives you:

Method Path Purpose
GET /products List with filtering, sorting, pagination
GET /products/{id} Get one by id, returns ETag
POST /products Create
PATCH /products/{id} Partial update (ETag required)
DELETE /products/{id} Soft delete

4. Run the API

MONGODB_URI="mongodb://localhost:27017/?directConnection=true" \
DATABASE_NAME="my-project-dev" \
ENVIRONMENT="development" \
AUTH_ENABLED="false" \
DEFAULT_CURRENCY="EUR" \
DEFAULT_VAT_RATE="0.20" \
uvicorn my_project.app:create_app --factory --port 5001

Verify it is up:

curl http://localhost:5001/health
# {"status":"ok"}

The interactive OpenAPI docs are at http://localhost:5001/docs.

5. Create, list, update, delete

BASE=http://localhost:5001

# Create
curl -X POST $BASE/products \
  -H "Content-Type: application/json" \
  -d '{"name": "Coffee Mug", "price": 12.50, "sku": "MUG-001"}'

# List (filter active ones)
curl "$BASE/products?in_stock=true&sort=name&per_page=20"

# Get one (capture the ETag)
ETAG=$(curl -sD - $BASE/products/{id} | awk '/^ETag/ {print $2}' | tr -d '\r')

# Update (must supply If-Match)
curl -X PATCH $BASE/products/{id} \
  -H "Content-Type: application/json" \
  -H "If-Match: $ETAG" \
  -d '{"price": 14.00}'

# Soft delete
curl -X DELETE $BASE/products/{id}

For the full filter syntax (where JSON clauses, suffix operators, sorting) see Filtering & Querying.

6. Enable the admin UI

Start the admin app and point it at your API:

# In another terminal
git clone https://github.com/easy-software-system/craft-easy-admin.git
cd craft-easy-admin
npm install
npm run web

On the Connect screen, enter http://localhost:5001. The admin app calls /admin/schema, discovers your Product resource, and renders a list/detail/create/edit UI — without you writing any UI code. See Admin App overview for the schema protocol.

Manual setup (without the template)

If you skipped cookiecutter, here is the minimum working project:

my-project/
├── pyproject.toml
└── src/
    └── my_project/
        ├── __init__.py
        ├── app.py
        ├── settings.py
        └── models/
            ├── __init__.py
            └── product.py

src/my_project/settings.py:

from craft_easy.settings import Settings as BaseSettings

class Settings(BaseSettings):
    app_name: str = "my-project"

src/my_project/app.py:

from craft_easy import create_base_app
from craft_easy.core.crud import Resource
from my_project.models.product import Product
from my_project.settings import Settings

def create_app():
    settings = Settings()
    app = create_base_app(settings=settings)
    app.register_resource(Resource(Product, path="/products"))
    return app

pyproject.toml:

[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["craft-easy-api>=0.1"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]

Run it the same way as step 4.

Next steps