# Development

Running, testing and debugging the Player API locally.

---

## 1. Getting a key

The API needs a real key on a real account. On dev, read one straight out of the
database:

```bash
docker compose -f dev/docker-compose.yml --env-file dev/.env exec -T db \
  sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" gcc -N -e \
  "SELECT id, nic, api_key FROM user WHERE api_key IS NOT NULL AND api_key <> \"\" LIMIT 3;"'
```

If none exists, generate one in-game at `i.cfm?f=option_api`.

The columns come from `dev/db/05_api.sql` (`user.api_key`,
`user.api_key_created`, `gcc_log.api_log`). **That file is gitignored**, so a
fresh environment — and production — needs it applied by hand.

## 2. Driving it

```bash
KEY=gccapi_...

curl -s -H "X-API-Key: $KEY" "http://127.0.0.1:8888/api/?action=meta.endpoints"
curl -s -H "X-API-Key: $KEY" "http://127.0.0.1:8888/api/?action=ranks.top50&server=RT"
```

Status codes only, across everything — the fastest regression check after a
refactor:

```bash
for a in meta.endpoints meta.whoami ranks.top50 dsr.battles chat.recent \
         server.info feds.list market.prices research.list buildings.list \
         minerals.list artifacts.list planets.types races.list ships.list; do
  printf "%-16s %s\n" "$a" \
    "$(curl -s -o /dev/null -w '%{http_code}' -H "X-API-Key: $KEY" \
       "http://127.0.0.1:8888/api/?action=$a")"
done
```

Auth must still bite:

```bash
curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8888/api/?action=meta.endpoints"
# 401
```

## 3. Debugging a 500

**The response never tells you anything.** `Application.cfc:onError` returns a
flat envelope and writes the real message to the Lucee log:

```bash
docker compose -f dev/docker-compose.yml --env-file dev/.env exec -T app \
  sh -c 'tail -20 /opt/lucee/server/lucee-server/context/logs/gcc_player_api.log'
```

Each line carries the message and the `template:line` that threw. Reaching for
this log first saves a lot of guessing — the `meta.endpoints` shadowing bug was
invisible from the outside and obvious in one log line.

## 4. Compile-checking a component

Instantiating a CFC compiles it, so a parse error surfaces without routing:

```cfml
<cfscript>
try { o = createObject("component","api.components.Ranks"); writeOutput("OK"); }
catch(any e) { writeOutput("FAIL: " & e.message); }
</cfscript>
```

Drop that in a scratch `.cfm` under `app/`, curl it, **delete it**.

## 5. Cache and rate limits while testing

Both live in application scope, so both reset when the application restarts:

```cfml
<cfscript>applicationStop();</cfscript>
```

Handy when a cached response is masking a change, or when repeated test runs
approach the 120/minute ceiling. Note this restarts the *game* application too if
you call it from a game-side page — use a page under `/api/` to restart only
this one.

## 6. `test.cfm`

A browser harness at `/api/test.cfm`: enter a key, click an endpoint, see the
raw envelope. Useful while developing.

**It must be deleted before production.** It accepts arbitrary keys and has no
place on a live server. Its header says so too.

## 7. Before committing

- Every action returns 200 with a valid key; no key returns 401.
- `meta.endpoints` lists what you expect — if a new endpoint is missing there,
  it is missing from `Registry.cfc`.
- New endpoint gated? Verify the 403 with an account that lacks the entitlement,
  not just the happy path.
- `git status --short app/api` — no scratch files, no harnesses.
