JSONsilo
All posts

Hosting a config file as an API

A small, real example — moving a hardcoded list of pricing plans out of your app and into a silo you can update without a deploy.

By Jaironlanda2 Min read

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 fileSilo
Changing a valueEdit, review, deployEdit in the console
Read costFree, it is in the bundleOne HTTP request, cached at the edge
Failure modeNone, it cannot failNetwork — you need a fallback
Audit trailGit historyThe 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.

Create your first silo in about two minutes

Paste some JSON, copy the URL into your code. The Free plan is enough to ship something real.