libmodulor v0.19.0 is out 🚀 => Check it out on GitHub or npm !
libmodulor
Examples

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 :

UseCases

Here are the sequence diagrams generated by the automatic documentation.

BuyAsset

BuyAsset Sequence Diagram

CancelOrder

CancelOrder Sequence Diagram

ListOrders

ListOrders Sequence Diagram

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.

Web UI

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.

Claude Desktop

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

Android & 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.

Cloudflare Worker Dashboard

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

SQLite UCDataStore

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.

Cloudflare D1 UCDataStore