4. Try it#

What this step does. Exercises the full API with curl — seeding, querying, updating, upserting by natural key, deleting — to confirm the handlers are wired correctly before any frontend code exists.

Restart the app and hit the endpoints with curl.

Seed a few departments#

curl -sX POST http://localhost:8080/api/department \
     -H 'Content-Type: application/json' \
     -d '{"code":"HR",    "name":"Human Resources"}'

curl -sX POST http://localhost:8080/api/department \
     -H 'Content-Type: application/json' \
     -d '{"code":"ENG",   "name":"Engineering"}'

curl -sX POST http://localhost:8080/api/department \
     -H 'Content-Type: application/json' \
     -d '{"code":"SALES", "name":"Sales"}'

List them back:

curl -s 'http://localhost:8080/api/department?_orderBy=code&_total=true'
{
  "result": [
    { "id": 2, "code": "ENG",   "name": "Engineering",      "description": null },
    { "id": 1, "code": "HR",    "name": "Human Resources",  "description": null },
    { "id": 3, "code": "SALES", "name": "Sales",            "description": null }
  ],
  "limit":  15,
  "offset": 0,
  "total":  3
}

Create an employee#

curl -sX POST http://localhost:8080/api/employee \
     -H 'Content-Type: application/json' \
     -d '{
       "loginName":   "ada@example.com",
       "firstName":   "Ada",
       "lastName":    "Lovelace",
       "department":  { "id": 2 },
       "joiningDate": "2025-11-03",
       "status":      "ACTIVE"
     }'

Query employees#

Filter by flattened parent column (Palmyra adds the JOIN automatically):

curl -s 'http://localhost:8080/api/employee?departmentCode=ENG&_orderBy=lastName'
{
  "result": [
    {
      "id": 1,
      "loginName":       "ada@example.com",
      "firstName":       "Ada",
      "lastName":        "Lovelace",
      "department":      { "id": 2, "code": "ENG", "name": "Engineering" },
      "departmentCode":  "ENG",
      "joiningDate":     "2025-11-03",
      "status":          "ACTIVE"
    }
  ],
  "limit":  15,
  "offset": 0
}

Update by primary key#

curl -sX POST http://localhost:8080/api/employee/1 \
     -H 'Content-Type: application/json' \
     -d '{ "status": "RESIGNED" }'

Upsert by natural key#

preferredKey = "loginName" lets the same POST route either insert or update:

curl -sX POST http://localhost:8080/api/employee \
     -H 'Content-Type: application/json' \
     -d '{ "loginName": "ada@example.com", "lastName": "Lovelace-Byron" }'

The row is matched on loginName, so this updates employee 1 — no need to know the surrogate id on the client.

Delete#

curl -sX DELETE http://localhost:8080/api/employee/1

Backend is done. The frontend track consumes exactly these endpoints.

More queries to try#

  • Sort by multiple columns. Comma-separate — prefix with - for DESC:
    curl -s 'http://localhost:8080/api/employee?_orderBy=status,-joiningDate'
  • Exact-match filter on a joined column. The flattened departmentCode works, but you can also filter on the full path:
    curl -s 'http://localhost:8080/api/employee?department.code=ENG'
    Palmyra adds the JOIN automatically — same as departmentCode=ENG.
  • Large page with total count. For admin dashboards that need the total even on a big page:
    curl -s 'http://localhost:8080/api/employee?_limit=200&_total=true'
  • Single-record read. Identity lookup by primary key:
    curl -s http://localhost:8080/api/employee/1
    Returns the bare object, no envelope.