Docs / Contact / sdks/testing-locally
Testing Against a Local or Staging Stack
Every SDK defaults to https://app.tendrl.com. One environment variable — TENDRL_APP_URL — moves it somewhere else, so the same application code can run against production, a staging environment, or a stack on your own machine without a code change.
The variable#
| Client | How to set the server |
|---|---|
| Python SDK | TENDRL_APP_URL, or the app_url= constructor argument |
| Go SDK | TENDRL_APP_URL (no config-file key exists for it) |
| JavaScript SDK | TENDRL_APP_URL in Node, or the apiBaseUrl constructor option |
| Nano Agent | TENDRL_APP_URL, or the -appURL flag |
| MicroPython client | Not this variable — set app_url in the on-device config |
All of them accept either form:
TENDRL_APP_URL=http://localhost:8000 # bare origin; the SDK appends /api
TENDRL_APP_URL=http://localhost:8000/api # full base URL
The explicit constructor argument always wins over the environment variable, and the environment variable wins over the production default.
Python#
TENDRL_APP_URL=http://localhost:8000 TENDRL_KEY=your_api_key python app.py
Or in code, which is useful when one process talks to more than one environment:
from tendrl import Client
client = Client(api_key="your_api_key", app_url="http://localhost:8000")
client.start()
client.publish({"temperature": 23.5}, tags=["sensor"])
Turn on logging while you are pointing at something new. The SDK reports undelivered messages on the tendrl logger, and a wrong URL shows up there rather than as an exception:
logging.basicConfig(level=logging.WARNING)
# Tendrl: DROPPED 3 message(s) that could not be delivered to http://localhost:8000/api
The warning names the URL it tried, which is usually enough to spot a typo or a stack that is not running.
Go#
TENDRL_APP_URL=http://localhost:8000 TENDRL_KEY=your_api_key go run .
A managed Go client validates the key against /claims before NewClient returns, so a wrong URL or a bad key is a constructor error you cannot miss:
API key validation failed: claims request failed: Get "http://localhost:8000/api/claims": dial tcp [::1]:8000: connect: connection refused
An unreachable stack looks like the line above; a wrong key for the right stack reads API key validation failed: invalid API key (unauthorized).
JavaScript#
In Node:
TENDRL_APP_URL=http://localhost:8000 TENDRL_KEY=your_api_key node app.js
In a browser there is no process.env, so pass the URL directly:
const client = new TendrlClient({
apiKey: 'your_api_key',
apiBaseUrl: 'http://localhost:8000',
});
The React hook forwards its apiBaseUrl option to the client, and falls back to TENDRL_APP_URL when you omit it:
const { client, publish } = useTendrlClient({
apiBaseUrl: 'http://localhost:8000',
onMessage: handleMessage,
});
Nano Agent#
tendrl-agent -appURL http://localhost:8000/api
The agent's default already ends in /api, and it accepts either form. Anything talking to the agent over its Unix socket — the Python SDK with mode="agent", or your own code speaking the socket protocol — then reaches your local stack without any per-client configuration, because it never sees a URL at all.
Keys are per-environment#
An API key is issued by one Contact environment and is meaningless in another. Create an entity in the stack you are pointing at and use its key; a production key against a local stack fails as an authentication error, not as a routing problem.
A quick end-to-end check#
The fastest way to confirm a client is really talking to your stack is to publish one message with wait_response and look at what comes back, instead of relying on the fire-and-forget path:
client = Client(api_key="your_api_key", app_url="http://localhost:8000", headless=True)
print(client.publish({"hello": "local"}, wait_response=True))
In headless mode every publish is synchronous, so a failure surfaces immediately rather than a minute later in a log line.
Tendrl