Products · Build a dot
Network and storage
Call your server from a dot, download and upload files, and keep data on the phone in the dot's own files, cache and secrets.
Your dot talks to your server through this.dash.network, and keeps things on the phone with this.dash.storage. Your server needs no CORS headers for a dot.
Allowed hosts
A dot can only reach the hosts listed under network.hosts in dot.json. A request anywhere else fails with denied:
{
"network": { "hosts": ["api.yourcompany.sl", "*.cdn.yourcompany.sl"] }
}*.cdn.yourcompany.sl covers its subdomains, but not cdn.yourcompany.sl itself. If your server redirects, the new host must be listed too.
The list also covers pictures: <Image src="https://cdn.yourcompany.sl/menu/jollof.jpg" /> shows only when its host is listed. A page can't reach anything else directly, fetch included. All your dot's traffic goes through dash.network.
A server on your computer
While you develop, the list can name a server on your own computer or network, such as localhost or 192.168.1.20. That works in monime dot dev, the simulator and live tests. monime dot build refuses these hosts, and Dash refuses them in published dots. Before you publish, list your server's public name instead.
Requests
dash.network.http.request works a lot like fetch. This sends a meter top-up order to your server:
const response = await this.dash.network.http.request<Order>({
url: `${env.API_URL}/orders`,
method: 'POST',
headers: { 'Idempotency-Key': state.orderKey },
body: { meter: state.meter, amount: state.amount },
as: 'json',
});
if (!response.ok) {
this.setState({ note: "Your order didn't go through. Try again." });
return;
}
this.setState({ order: response.body });- A 404 or a 500 is still an answer. Check
okorstatus. The call only rejects, withfailed, when no answer arrives. Its message says why, like "Could not reach api.boenergy.sl: the phone is offline". - An object body goes as JSON, and a string as text. Leave out
as: 'json'to get the body as text. - Dash keeps no cookies. Send any token your server needs in a header.
Send an order once
A person may tap Pay twice, or retry after a dropped connection. Make one key per order with this.dash.crypto.randomUUID(), keep it in state, and send it as the Idempotency-Key header, as above. Your server then handles each key once, and a retry gets back the first answer. Monime's API works the same way: see Idempotency.
Online or not
Before a payment, or anything else that needs the network, check dash.network.status:
const { online } = await this.dash.network.status();
if (!online) {
this.setState({ note: "You're offline. Connect to pay." });
return;
}Online doesn't mean your server will answer. Still handle a failed request.
Download and upload files
download saves a response to a file, and upload sends files as a form:
const { ok, file } = await this.dash.network.http.download({ url: `${env.API_URL}/receipts/${state.orderId}` });
if (ok) {
this.setState({ receipt: file });
}
await this.dash.network.http.upload({
url: `${env.API_URL}/scans`,
files: [{ field: 'scan', file: state.receipt }],
});File objects
Downloads, photos and files the person chooses all arrive as a file object, { id, name, size, contentType, url }. Show an image with its url, as in <Image src="{{ receipt.url }}" />. To upload, share or save it, pass the object itself. It's gone when the dot closes, unless you save it.
The dot's own files
dash.storage.file keeps files on the phone, in a folder only your dot can see. They stay until your dot removes them, or the person clears the dot's data in Dash.
await this.dash.storage.file.write('meters.json', JSON.stringify(state.meters));
await this.dash.storage.file.save(state.receipt, `receipts/${state.orderId}.pdf`); // Keep a download.Paths are relative (receipts/42.pdf), and missing folders are made for you. save, move and copy won't replace a file unless you pass { overwrite: true }.
Reading a path with nothing there rejects with missing. On a first visit, that only means nothing's saved yet:
import { isDashError } from '@monimesl/dot-runtime';
try {
const saved = await this.dash.storage.file.read('meters.json', { as: 'text' });
this.setState({ meters: JSON.parse(saved) });
} catch (error) {
if (!isDashError(error, 'missing')) {
throw error;
}
}The Files reference has the rest: open, list, remove and more. In the simulator, the files live in your project's .dot/files/ folder.
To let the person pick a file from their phone, see dash.media.pickFile.
Cache and secrets
The cache holds data you can fetch again, such as this week's okada fares. Secrets hold the person's own tokens, encrypted on the phone. Their session with your server is one.
await this.dash.storage.cache.set('fares', fares, { ttl: 60 * 60 * 1000 }); // An hour, in milliseconds.
const cached = await this.dash.storage.cache.get('fares'); // null once it's expired.
await this.dash.storage.secret.set('session', token);Your own API keys stay off the phone, even in secrets. In the simulator, secrets sit in .dot/.secrets/ as plain JSON.
Limits
A later Dash may raise these limits, but won't lower them while the Dash API stays on version 1.x.
| What | Limit |
|---|---|
| The dot's files | 256 MB in all |
One read of a file | 5 MB |
| The cache | 64 MB in all |
| One cache value | 1 MB |
| A cache key | 512 characters |
| Secrets | 32 secrets, up to 4 KB each |
| A response's body | 5 MB |
| A download | 256 MB |
| A file the person chooses | 25 MB |
| A photo | 2048 pixels on its longest side |
| Downloads, photos and chosen files, each time the dot opens | 200 files and 512 MB in all |
| Network calls at once | 6 |
Past the limit for downloads, photos and chosen files, more fail with failed until the dot opens again. Network calls past 6 wait their turn.