SuperTrader
An example showcasing a simple app to trade crypto, shares and other assets.
SuperTrader is a product featuring the Trading
app with BuyAssetUCD
, CancelOrderUCD
and ListOrdersUCD
, exposed on the following targets :
server-node-express
A Node.js / Express server
spa
A React SPA (Single Page Application)
cli
A Node.js CLI
mcp-server
A Local Node.js Stdio MCP Server running
rn
A React Native Android & iOS app
server-node-hono
A Node.js / Hono server
server-nextjs
A Next.js server
server-cloudflare-worker
A Cloudflare Worker
UseCases
Here are the sequence diagrams generated by the automatic documentation.
BuyAsset
CancelOrder
ListOrders
Get & Build
Take the time to open the code in your favorite editor and browse it.
git clone git@github.com:c100k/libmodulor.git
cd libmodulor/examples/supertrader
pnpm install && touch .env && pnpm build
Targets
server-node-express
This target uses the built-in node-express-server
target,
is transpiled with tsc
and executed with Node.js
.
It uses SQLite
as UCDataStore
.
Start the server.
pnpm run:server-node-express
Test in your Terminal Emulator with curl
.
# BuyAsset
curl -X POST -H "Content-Type: application/json" http://localhost:7443/api/v1/Trading_BuyAsset
# ❌ {"message":"Invalid credentials"}
curl -X POST -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" http://localhost:7443/api/v1/Trading_BuyAsset
# ❌ {"message":"ISIN must be filled"}
curl -X POST -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" -d '{"isin":"US02079K3059","limit":123.5,"qty":150}' http://localhost:7443/api/v1/Trading_BuyAsset
# ✅ {"parts":{"_0":{"items":[{"isin":"US02079K3059","id":"882f384b-fa46-424b-8b82-e0781ab1fbec","limit":123.5,"qty":150,"status":"pending"}],"total":1}}}
# CancelOrder
curl -X DELETE -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" -d '{"id":"882f384b-fa46-424b-8b82-e0781ab1fbec"}' http://localhost:7443/api/v1/Trading_CancelOrder
# ✅ {"parts":{"_0":{"items":[{"id":"a2285506-1afd-4649-98a6-e4443d1c6ce7","isin":"US02079K3059","limit":123.5,"qty":150,"status":"cancelled"}],"total":1}}}
curl -X DELETE -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" -d '{"id":"882f384b-fa46-424b-8b82-e0781ab1fbec"}' http://localhost:7443/api/v1/Trading_CancelOrder
# ❌ {"message":"Cannot cancel an order that is not pending"}
# ListOrders
curl -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" http://localhost:7443/api/v1/Trading_ListOrders
# ✅ {"parts":{"_0":{"items":[{"id":"a2285506-1afd-4649-98a6-e4443d1c6ce7","isin":"US02079K3059","limit":123.5,"qty":150,"status":"cancelled"}],"total":1}}}
Keep your server running to test the "client" targets defined below.
spa
This target uses the built-in react-web-pure
target,
is bundled with vite
and uses custom components made with tailwindcss
& daisyUI
.
It is statically served by the server defined above for convenience, but it could also be served by anything else (e.g. an S3 bucket, a simple nginx server and so on).
Test in your browser at http://localhost:7443.
cli
This target uses the built-in node-core-cli
target,
is transpiled with tsc
and executed with Node.js
.
Test in your Terminal Emulator.
# BuyAsset
pnpm run:cli Trading_BuyAsset
# ❌ ISIN must be filled
pnpm run:cli Trading_BuyAsset --isin US02079K3059 --limit 123.5 --qty 150
# ✅ {"parts":{"_0":{"items":[{"isin":"US02079K3059","id":"c1ef95f1-f6e1-4616-a7a5-5584758b3a65","limit":123.5,"qty":150,"status":"pending"}],"total":1}}}
# CancelOrder
pnpm run:cli Trading_CancelOrder --id c1ef95f1-f6e1-4616-a7a5-5584758b3a65
# Are you sure ? [Y,y = Yes / N,n = Cancel] : y
# ✅ {"parts":{"_0":{"items":[{"id":"c1ef95f1-f6e1-4616-a7a5-5584758b3a65","isin":"US02079K3059","limit":123.5,"qty":150,"status":"cancelled"}],"total":1}}}
pnpm run:cli Trading_CancelOrder --id c1ef95f1-f6e1-4616-a7a5-5584758b3a65
# Are you sure ? [Y,y = Yes / N,n = Cancel] : y
# ❌ Cannot cancel an order that is not pending
# ListOrders
pnpm run:cli Trading_ListOrders
# ✅ {"parts":{"_0":{"items":[{"id":"a2285506-1afd-4649-98a6-e4443d1c6ce7","isin":"US02079K3059","limit":123.5,"qty":150,"status":"cancelled"},{"id":"882f384b-fa46-424b-8b82-e0781ab1fbec","isin":"US02079K3059","limit":123.5,"qty":150,"status":"pending"},{"id":"c1ef95f1-f6e1-4616-a7a5-5584758b3a65","isin":"US02079K3059","limit":123.5,"qty":150,"status":"cancelled"},{"id":"78509b91-0f33-43a2-b6e3-2ced5a1fa171","isin":"US02079K3059","limit":123.5,"qty":150,"status":"pending"}],"total":4}}}
mcp-server
This target target uses the built-in node-mcp-server
target,
is transpiled with tsc
and exposes an index.js
file to be used by the MCP client.
Test in Claude Desktop.
Open the config file to register the MCP server.
nano ~/Library/Application\ Support/Claude/claude_desktop_config.json
Paste the following config and adapt the absolute path.
{
"mcpServers": {
"libmodulor-supertrader": {
"command": "node",
"args": [
"/ABSOLUTE_PATH_TO_THE_CWD/dist/products/SuperTrader/mcp-server/index.js"
]
}
}
}
Launch Claude Desktop.
At the bottom right of the prompt, you should see a little hammer 🔨 indicating 1 MCP Tool available
.
Click on it. You should see the use cases registered.
Write a prompt to buy an asset.
Dear Claude. Please buy 150 shares of Google.
rn
This target uses the built-in react-native-pure
target,
is bundled with expo
and runs on android
and ios
.
Make sure your environment is setup to be able to use the Android Emulator and iOS Simulator (macOS only).
android
To call the server running on your machine, the Android emulator uses the special IP address 10.0.2.2
. Therefore, you need to restart you server to listen on 0.0.0.0
.
app_server_binding_host=0.0.0.0 pnpm run:server-node-express
Start the application on the android emulator.
pnpm run:rn:android
ios
Start the application on the ios simulator.
pnpm run:rn:ios
server-node-hono
This target uses the built-in node-hono-server
target.
It is very similar to node-express-server
, except that it uses hono
instead of express
.
It also uses SQLite
as UCDataStore
.
If the node-express-server
is still running, stop it and start this one.
pnpm run:server-node-hono
You can test it the exact same way as above (with curl
or the different clients).
server-nextjs
This target uses the built-in server-nextjs
target
and the same components as the spa
target defined above. Except that they are served by Next.js.
It also uses SQLite
as UCDataStore
.
pnpm run:server-nextjs
You can test it the exact same way as above (with curl
or the different clients).
The only difference is that the server listens on port 3000
instead of 7443
.
Thus, you need to adapt it in the clients' container.ts
. That's a good exercise to get used to the settings mechanism.
server-cloudflare-worker
This target uses the built-in edge-worker-hono-server
target in its sync
flavor
and is managed with wrangler
. It uses Cloudflare D1 as UCDataStore
.
You need a Cloudflare account to test this target, even locally, as you need to create a D1 database. Please note that this might occur charges on your account.
Initialize the D1 database.
pnpm wrangler d1 create libmodulor-examples-supertrader-uc-data-store
pnpm wrangler d1 list
Add the binding in dist/products/SuperTrader/server-cloudflare-worker/wrangler.jsonc
and adapt the database_id
.
"d1_databases": [
{
"binding": "UCDataStore",
"database_id": "{your_database_id}",
"database_name": "libmodulor-examples-supertrader-uc-data-store"
}
],
Apply the migrations to create the schema (local
and remote
).
pnpm wrangler d1 execute libmodulor-examples-supertrader-uc-data-store --cwd ./dist/products/SuperTrader/server-cloudflare-worker --local --file=./migrations/001_init.sql
pnpm wrangler d1 execute libmodulor-examples-supertrader-uc-data-store --cwd ./dist/products/SuperTrader/server-cloudflare-worker --remote --file=./migrations/001_init.sql
Start the server locally.
pnpm run:server-cloudflare-worker
You can test it the exact same way as above (with curl
or the different clients).
The only difference is that the server listens on port 8787
(or a random one assigned by wrangler) instead of 7443
.
Thus, you need to adapt it in the clients' container.ts
. That's a good exercise to get used to the settings mechanism.
You can even deploy it on your account.
pnpm deploy:server-cloudflare-worker
Once done, perform the exact same tests as above, simply by replacing the local address and port by the remote one.
You can inspect the requests in the Cloudflare Workers & Pages
dashboard as shown below.
Explore the data
SQLite
The server-node-express
, server-node-hono
and server-nextjs
targets rebind the UCDataStore
from InMemoryUCDataStore
(default) to KnexUCDataStore
configured with SQLite
.
Explore the data with your favorite DB tool like DBeaver or TablePlus.
open uc-data-store.sqlite
Cloudflare D1
The server-cloudflare-worker
target rebinds UCDataStore
from InMemoryUCDataStore
(default) to CloudflareD1UCDataStore
.
Explore the data with your favorite DB tool like DBeaver or TablePlus.
open dist/products/SuperTrader/server-cloudflare-worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/{hash}.sqlite
For remote data, open the Cloudflare D1
dashboard as shown below.