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:

dot.json
{
  "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:

TypeScript
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 ok or status. The call only rejects, with failed, 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:

TypeScript
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:

TypeScript
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.

TypeScript
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:

TypeScript
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.

TypeScript
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.

WhatLimit
The dot's files256 MB in all
One read of a file5 MB
The cache64 MB in all
One cache value1 MB
A cache key512 characters
Secrets32 secrets, up to 4 KB each
A response's body5 MB
A download256 MB
A file the person chooses25 MB
A photo2048 pixels on its longest side
Downloads, photos and chosen files, each time the dot opens200 files and 512 MB in all
Network calls at once6

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.

Keep reading