Most apps carry a handful of values that are not really code: a feature flag, a list of supported countries, the copy in a banner, the plans on a pricing page. They live in a constants file because that is the path of least resistance, and then every change to them needs a pull request and a deploy.
A silo is a JSON document with a URL. You edit it in the console, and the next request sees the new value. This post walks through one small case end to end.
The starting point#
Say your app has this sitting in a constants file:
export const PLANS = [
{ id: "free", name: "Free", price: 0 },
{ id: "basic", name: "Basic", price: 9 },
{ id: "pro", name: "Pro", price: 29 },
];
Changing a price means editing code. It is three lines, but it is three lines behind a deploy.
Move it into a silo#
Create a silo in the console and paste the same array in as its contents. You get back a UUID, which is also its address. If the silo is public, anyone with the URL can read it:
curl https://api.jsonsilo.com/public/<uuid>
If it is private — which is the right default for anything you would not publish — you read it from the API host with a key:
curl https://api.jsonsilo.com/<uuid> \
-H "X-SILO-KEY: silo_live_xxx"
Either way you get the document back, unchanged, as JSON.
Read it from your app#
The client-side change is small. Fetch instead of import:
const SILO = process.env.SILO_UUID;
async function getPlans() {
const res = await fetch(`https://api.jsonsilo.com/${SILO}`, {
headers: { "X-SILO-KEY": process.env.SILO_KEY },
});
if (!res.ok) throw new Error(`silo ${res.status}`);
return res.json();
}
Responses are cached at the edge, so a busy page is not hitting the origin for every visitor.
Ask for one field instead of the whole document#
You often do not want the whole array. The query engine runs on the private route and takes a dot path, so you can pull out a single value and skip the parsing entirely:
curl "https://api.jsonsilo.com/<uuid>?path=1.price&output=text" \
-H "X-SILO-KEY: silo_live_xxx"
That returns 9 as plain text. output also accepts json, int, float and bool, which
is handy when the thing you are fetching is a flag and you want a real boolean back.
You can index into a top-level array with idx, or filter it:
curl "https://api.jsonsilo.com/<uuid>?q=price&value=10&op=lt&path=name" \
-H "X-SILO-KEY: silo_live_xxx"
op takes eq, ne, gt, lt, lk and il, so the last one is "the names of every plan
under ten dollars".
What you get, and what you give up#
| Constants file | Silo | |
|---|---|---|
| Changing a value | Edit, review, deploy | Edit in the console |
| Read cost | Free, it is in the bundle | One HTTP request, cached at the edge |
| Failure mode | None, it cannot fail | Network — you need a fallback |
| Audit trail | Git history | The console |
That fourth row is the honest one. A network call can fail, and a config fetch that throws on a cold start is worse than a stale constant. Keep the last known-good value and fall back to it, or ship a default in the bundle and treat the silo as an override.
Start with something low-stakes — a banner, a flag, a list nobody will die without — and see whether not deploying to change it feels as good as it sounds.
Full details are in the documentation.
