Note: Active development is being moved to nocfo-api-tool. New development will happen there.
This repository remains as an exploration archive. It records my late 2025 to early 2026 experiences with using LLMs to explore an area of coding where I had no experience. Through five iterations, I found a way to use a strongly typed functional language to interface with HTTP APIs in a streaming manner. I had done a somewhat similar prototype some 10 years back, at the time when ECMAScript 6 was still new and asynchronity was not that well supported by JS and libraries. At that time, the experience was quite frustrating and took weeks.
This time the experience was much smoother, largely thanks to the emerge of LLMs. I was able to accomplish a number of iterations in a few days and weeks, exploring various languages and OpenAPI code generators.
The Hawaii and F# based end result was good enough so that in April 2026 I decided to move the development into another repository, with the aim of making a clean, well managed, near-production quality open source tool.
Hence, I'm preserving this repository as it records a number of valuable lessons learned.
In the following, "we" denotes me as a human and my LLM helpers, ChatGPT and Claude.
This project documents five approaches we explored while building a small,
functional-streaming-friendly client for the NoCFO accounting API.
The fifth iteration hawaii-client/ — which pairs F#
and the Hawaii OpenAPI generator —
is the first version I am somewhat satisfied with.
The earlier attempts remain in the repo so future readers
can see what worked, what failed, and why.
If you are evaluating NoCFO, experimenting with functional programming against financial APIs, or comparing OpenAPI toolchains, this repository is intended as a concise field report.
hawaii-client/– F# library + scripts using Hawaii-generated types, lazyAsyncSeqstreams, and a thin domain layer. Start here.api/openapi.json– The upstream NoCFO OpenAPI document used for generation.tools/– CSV-first CLI (“nocfo”) built on top of the Hawaii F# client library; seetools/README.md.requests/– Raw HTTP checks (VS Code REST client format) used to validate authentication and pagination manually.v1-typescript/,v2-purescript/,v3-fsharp/,v4-fsharp/– Earlier experiments (kept for historical context; expect incomplete or abandoned code).vendor/Hawaii/– Forked generator with small fixes for nullable handling, enum tolerance, and operation name cleanup.LESSONS-LEARNED.md,hawaii-client/Domain-design.md,v5-fsharp-hawaii.md– Narrative notes about decisions, trade-offs, and follow-up ideas.
The CLI in tools/ is the easiest way to interact with the API. It streams
entities, writes them as CSV, and can reconcile edited rows back to the server.
-
Prerequisites
- .NET 10 SDK (
dotnet --version≥ 10.0) - macOS or Linux shell (the code itself is cross-platform)
NOCFO_TARGET_TOKEN(or fallbackNOCFO_TOKEN) exported; optionallyNOCFO_TARGET_BASE_URL(defaults tohttps://api-tst.nocfo.io)NOCFO_SOURCE_TOKENonly when running dual-environment commands likemap accounts- If a local
.envexists, you maysource .envto populate tokens and aliases for your shell session. The GitHub version of this repo does not include.env.
export NOCFO_TOKEN="paste-your-token"
- .NET 10 SDK (
-
Build once and run tests (from repo root):
dotnet build dotnet test -
List businesses:
dotnet run --project tools -- \ list businesses --fields "id,name,slug" > businesses.csv
-
List accounts for a business:
dotnet run --project tools -- list accounts \ -b <business-id> \ --fields "id,number,name,type" > accounts.csv
-
List documents for a business:
dotnet run --project tools -- list documents \ -b <business-id> \ --fields "id,number,date,balance" > documents.csv
-
Update accounts by editing the CSV (keep
id) and piping it back in:dotnet run --project tools -- update accounts \ -b <business-id> \ --fields "id,number,name" < accounts.csv
-
Update contacts by editing exported contacts (keep
id) and piping them back in:dotnet run --project tools -- update contacts \ -b <business-id> \ --fields "id,name,invoicing_email,notes" < contacts.csv
-
Delete accounts by piping a CSV with the account
ids you want to drop:dotnet run --project tools -- delete accounts \ -b <business-id> < ids-to-delete.csv
-
Map account IDs between environments (source -> target by account
number):NOCFO_SOURCE_TOKEN="paste-source-token" NOCFO_TARGET_TOKEN="paste-target-token" \ dotnet run --project tools -- map accounts \ -b <business-id> > csv/account-id-map.csv
-
Create minimal documents in target:
dotnet run --project tools -- create documents \
-b <target-business-id> \
--account-id-map csv/account-id-map.csv \
< csv/documents-create.csv--fieldscontrols both which columns are emitted and which columns are read back.idis always required when executing updates or deletes.- Output defaults to stdout and input defaults to stdin;
--out/--inoverride those streams without shell redirection. - Currently implemented verbs:
list,update accounts,update contacts,delete accounts,delete contacts,delete documents,map accounts, and minimalcreate documents. - Errors and HTTP traces go to stderr so you can keep piping stdout to files.
See tools/README.md for a deeper dive into configuration, CSV expectations,
and the internal architecture.
If you want to hack on the streaming library itself (e.g., extend the domain
model or write new folds), the various test scripts under hawaii-client/ may be useful.
-
Prerequisites – same as above.
-
Build:
cd hawaii-client dotnet build -
Set your token:
export NOCFO_TOKEN="paste-your-token"
-
Run a script:
dotnet fsi TestBalance.fsx
By default, the script streams accounts for a demo business, hydrates each account on demand, and folds balances by class before printing a trial balance.
Consult hawaii-client/README.md for a tour of the modules and guidance on
regeneration, extending AsyncSeq wrappers, or writing new reports.
The generated client under hawaii-client/generated/ is checked in, so the repo builds without a regeneration step.
Regenerate only when the NoCFO OpenAPI spec changes.
-
Refresh
api/openapi.json.curl -L --fail --silent --show-error \ -H "Accept: application/vnd.oai.openapi+json;version=3.0" \ "https://api-tst.nocfo.io/openapi/" \ -o api/openapi.json
-
Build the local Hawaii fork:
dotnet build vendor/Hawaii/src/Hawaii.fsproj -c Release
-
From the repo root, run the built Hawaii CLI against the curated config:
dotnet ./vendor/Hawaii/src/bin/Release/net6.0/Hawaii.dll \ --config ./hawaii-client/nocfo-api-hawaii.json \ --no-logo
-
Rebuild everything:
dotnet build
Notes:
- The current local Hawaii fork targets
net6.0, so newer SDKs emit end-of-support warnings during build. hawaii-client/nocfo-api-hawaii.jsoncurrently assumes you run Hawaii from the repo root:schemais resolved from the current working directory, whileoutputis resolved relative to the config file.
For a more step-by-step build instructions, see hawaii-client/README.md.
- Lazy streaming over paginated endpoints –
AsyncSeqwrappers provide pull-based iteration over businesses and accounts while preserving the original pagination semantics.Streams.streamBusinessesandStreams.streamAccountsadapt the generated client into domain-friendly shapes. - Hydratable domain model –
Hydratablediscriminated unions keep initial payloads lightweight and defer full record loading until needed by folds or reports.Domain.BusinessandDomain.Accountshowcase the pattern. - Token-based auth done correctly – All HTTP helpers attach the
Authorization: Token <value>header per request, preventing subtle errors with .NET’s default header validation. - Thin, testable folds – Sample scripts (
TestBalance.fsx,TestStreams.fsx) emphasize pure folds on top of streams rather than mutable accumulation.
- Skim
LESSONS-LEARNED.mdfor the narrative of what we tried and why we settled on Hawaii. - Open
hawaii-client/TestBalance.fsxto see how little code is needed to compute a trial balance over the live API. - Browse
vendor/Hawaiiif you are interested in generator customization for F#.
We consider this iteration “good enough to be somewhat useful for developers.” As of April 13, 2026, all new development is being moved to the new repo.
If you use any part of this repo, please do so at your own discretion. Contributions are welcome, but we may not respond quickly.