A minimal Node.js / Express chat app that runs on Embr and uses an Azure
AI Foundry-deployed model as its LLM. The agent loop is orchestrated locally
via the Vercel AI SDK (ai + @ai-sdk/openai-compatible);
only the LLM inference call reaches Foundry.
This is the Node sibling of embr-foundry-chat-sample-python
and embr-foundry-chat-sample-dotnet.
Same UI, same tools, same flows — different stack.
- ESM Node app on Node 20+ / Express 5.
- Two local tools the agent can call:
getWeatherandrollDice— defined withtool()from the AI SDK + Zod schemas. - Foundry model is configured through the OpenAI-compatible endpoint via
createOpenAICompatible({ baseURL, apiKey }), so the same code path works against Azure OpenAI or Foundry without any SDK swap. - Multi-step tool-call loop courtesy of
generateText({ stopWhen: stepCountIs(5) }). - Single static
public/index.htmlchat UI (markdown via marked + DOMPurify from CDN, no bundler).
app/
server.js # Express endpoints, message history, static mount
agent.js # Vercel AI SDK agent + Foundry client construction
tools.js # local tools the agent can invoke
public/index.html # chat UI
embr.yaml # Embr deploy config (platform: nodejs)
.env.example # required env vars (copy to .env for local runs)
Three env vars (see .env.example):
| Variable | Purpose |
|---|---|
FOUNDRY_BASE_URL |
OpenAI-compatible endpoint for your Foundry project, e.g. https://<resource>.services.ai.azure.com/api/projects/<project>/openai/v1 |
FOUNDRY_API_KEY |
API key from the Foundry resource (Keys + Endpoint page) |
FOUNDRY_MODEL_DEPLOYMENT |
Deployment name, e.g. gpt-4o-mini, gpt-5.4-mini-1 |
cp .env.example .env # then fill in real values
npm install
npm startThen open http://localhost:5000.
Smoke test:
curl http://localhost:5000/health
curl http://localhost:5000/api/config
curl -X POST http://localhost:5000/api/chat \
-H 'Content-Type: application/json' \
-d '{"message":"Roll a 20-sided die"}'gh repo create embr-foundry-chat-sample-node --source=. --public --push
embr quickstart deploy <your-user>/embr-foundry-chat-sample-node
embr variables set FOUNDRY_BASE_URL ... -p <id> -e <id> -s
embr variables set FOUNDRY_API_KEY ... -p <id> -e <id> -s
embr variables set FOUNDRY_MODEL_DEPLOYMENT ... -p <id> -e <id> -sThe first deploy provisions the project + environment and triggers a build.
Subsequent pushes auto-deploy. Note: setting variables on a live deployment
requires embr deployments trigger to pick them up.
| Path | Method | Purpose |
|---|---|---|
/ |
GET | Chat UI (static public/index.html) |
/health |
GET | Liveness probe |
/api/config |
GET | Resolved Foundry endpoint summary (host/project/model) |
/api/chat |
POST {message, threadId?} |
Run one agent turn |
/api/reset |
POST ?thread_id=… |
Clear server-side history for a thread |
- Agent and message history are in-memory — fine for the demo, not durable.
- Errors from the model surface as HTTP
502with the provider message indetail. The user message is rolled back on failure so retries don't compound. - Same conversation shape as the Python and .NET samples (
{role, content}).