data.mealieshopper.nl

MealieShopper logo # MealieShopper data **Shared data for MealieShopper: submitted, checked and approved.** The public JSON data of [MealieShopper](https://github.com/helmerzNL/MealieShopper) at [data.mealieshopper.nl](https://data.mealieshopper.nl). Anyone can send data as `.json` or `.zip` through a pull request; it goes online only once it is approved. 🇳🇱 [Nederlands](/) · 🇬🇧 **English** [![Format](https://img.shields.io/badge/format-JSON%20%7C%20ZIP-blue)](#sending-data) [![Site](https://img.shields.io/badge/site-data.mealieshopper.nl-007d41)](https://data.mealieshopper.nl) [![Languages](https://img.shields.io/badge/languages-NL%20%7C%20EN-lightgrey)](README.md) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-flux76-FFDD00?logo=buymeacoffee&logoColor=black)](https://buymeacoffee.com/flux76) [![Ko-fi](https://img.shields.io/badge/Ko--fi-flux76-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/flux76) [Site](https://data.mealieshopper.nl) · [Catalogue](https://data.mealieshopper.nl/index.json) · [MealieShopper](https://github.com/helmerzNL/MealieShopper)

Contents

Why MealieShopper data?

Some data does not belong to one household but to everyone who uses MealieShopper: a list kept up together, a mapping someone already worked out once. It belongs in one fixed place, at the same address for everyone, where nothing is added without someone having looked at it. This repository is that place. You send a file as a pull request; a check shows what changes on the site, including what is inside a zip; the owner approves it, and then it goes online by itself.

What can it do?

Using the data

Everything is at https://data.mealieshopper.nl. Start with the catalogue:

curl https://data.mealieshopper.nl/index.json
{
  "site": "https://data.mealieshopper.nl",
  "generated_at": "2026-11-07T10:00:00Z",
  "commit": "…",
  "collections": [
    {
      "name": "recepten",
      "title": "Recepten",
      "description": "…",
      "schema": "https://data.mealieshopper.nl/schemas/recepten.schema.json",
      "files": [
        { "path": "recepten/soepen.json", "url": "https://data.mealieshopper.nl/recepten/soepen.json",
          "bytes": 2048, "sha256": "…", "items": 12 }
      ]
    }
  ]
}

Sending data

You need a GitHub account and nothing else.

  1. Pick the collection: the folder under data/, for example data/recepten/. If it does not exist yet, create it in your pull request (a new collection preferably comes with a schema, see schemas/).
  2. Open that folder on GitHub and choose Add file → Upload files. Drop your .json or .zip file in.
  3. At the bottom choose Create a new branch … and start a pull request, and fill in the template: what it is, where it comes from, and that it may be public.
  4. Wait for the check Inzending controleren. If it is red, click Details: the problem is shown next to the file. Upload a corrected version to the same branch; the check runs again.
  5. When it is green, open Details → Summary: it lists what changes on the site. The owner approves and merges.

What a submission may contain

Rule Why
Only .json, or a .zip with only .json What is in it must be checkable. What zip tools add on their own (__MACOSX/, .DS_Store) is skipped.
A document belongs to a collection: data/<collection>/…/<name>.json The first folder is the collection.
Collection names: lowercase letters, digits and - (not schemas). File names: letters, digits, ., - and _, no spaces Every path is also a web address.
A zip unpacks where it is: data/recepten/bundel.zip with a.json inside becomes recepten/a.json That is how you send many files at once. A zip directly in data/ carries the collections as folders.
Every path on the site comes from one file, also when case is ignored Two sources for the same address cannot both win.
Strict JSON: UTF-8, no duplicate keys, no NaN, an object or a list at the top A duplicate key disappears without a trace.
Matches schemas/<collection>.schema.json, when there is one That is how a collection decides what it accepts.
No keys that suggest a secret (token, api_key, password, secret, cookie, …) Everything here is public.
At most 10 MB per document, 25 MB per zip, 100 MB unpacked per zip and 2000 files per zip 25 MB is also GitHub’s limit for a file added in the browser.
Data and code not in one pull request A submission cannot change the check that judges it.

How a submission is published

  1. Send: a pull request into main with files in data/ (and possibly a schema in schemas/).
  2. Check: the check Inzending controleren reads the pull request as it would be in main after the merge, with the rules and the code of main (not those of the pull request), and lists in its summary what changes on the site. The check Tests runs the tests.
  3. Approve: the owner reviews the pull request and the summary, approves and merges. A change after the approval needs a new approval.
  4. Publish: the workflow Publiceren builds the site from main: every document unpacked and formatted, the schemas, index.json and an overview page. It puts that as a new commit on the branch publicatie and deploys it to GitHub Pages, at data.mealieshopper.nl.

main keeps the files as they were approved, a zip included. If you prefer loose files in main, unpack a zip with python scripts/datastore.py unpack data/<collection>/<file>.zip and send that as a pull request. Nothing changes on the site.

Setting up

Once, by the owner, in the repository settings on GitHub.

  1. main as the default branch. Create the branch main (if it does not exist yet) and select it under Settings → General → Default branch.
  2. GitHub Pages. Settings → Pages → Build and deployment → Source: GitHub Actions. Enter data.mealieshopper.nl under Custom domain and turn on Enforce HTTPS. Pages for a private repository needs GitHub Pro or a paid plan; the site itself is always public.
  3. DNS. A CNAME record for data.mealieshopper.nl pointing to helmerznl.github.io.
  4. Branch protection for main (Settings → Rules → Rulesets, or Branches):
    • a pull request required, with 1 approval and Require review from Code Owners (CODEOWNERS);
    • Dismiss stale pull request approvals when new commits are pushed;
    • the checks Inzending controleren and Tests required;
    • no force pushes and no deletion.

    Do not protect the branch publicatie in a way that blocks github-actions[bot]: only the workflow writes to it.

  5. Make it public, if anyone should be able to send data. While the repository is private, only collaborators can open a pull request.

GitHub does not let you approve your own pull request. When you send something yourself, merge as an administrator with Merge without waiting for requirements to be met, once the checks are green.

Administration

Task How
Approve a submission Review the pull request, Files changed and the summary of Inzending controleren, then Review changes → Approve and merge
Take something off the site A pull request that removes the file from data/ (or replaces the zip it is in)
Roll back a publication Revert the pull request and merge the revert; the site follows main
Publish again Actions → Publiceren → Run workflow
Check locally python -m pip install -r requirements.txt, then python scripts/datastore.py check
Build the site locally python scripts/datastore.py build --out _site and open _site/index.html

Data and backups

Where What it holds
main, folder data/ The approved files, as they were sent
main, folder schemas/ The JSON Schema per collection
Branch publicatie The published site: everything unpacked and formatted, with index.json. Every publication is a commit
GitHub Pages The same site, at data.mealieshopper.nl

The repository is the backup: every submission and every publication is in the git history. publicatie and the site can always be rebuilt from main (Run workflow). Make a copy with git clone --mirror.

Architecture

flowchart LR
    I[Submitter] -->|pull request<br/>.json or .zip| PR[Pull request into main]
    PR --> C[Inzending controleren<br/>and Tests]
    C -->|summary| E[Owner approves<br/>and merges]
    E --> M[(main: data/, schemas/)]
    M --> P[Publiceren]
    P --> B[(branch publicatie)]
    P --> G[GitHub Pages<br/>data.mealieshopper.nl]
    G --> A[MealieShopper and<br/>other users]
Part Purpose
scripts/datastore.py check, build and unpack: every rule lives here. Python with only jsonschema added
inzending.yml Inzending controleren, with main’s code (pull_request_target); runs nothing the pull request brings in
controleren.yml Tests, with the pull request’s code
publiceren.yml Build, put on publicatie and deploy to GitHub Pages
site/ The overview page and the logo

Contributing

Sending data is described above. A change to the code (the check, the site, the workflows) goes in its own pull request into main, never together with data. The working conventions for people and AI assistants are in AGENTS.md.

python -m pip install -r requirements.txt
python -m unittest discover -s tests
python scripts/datastore.py check

Privacy and security

Support MealieShopper

MealieShopper is free. Find it useful? A coffee keeps development going.

Buy me a coffee Support me on Ko-fi

License

No license has been chosen yet for the data or the code: all rights reserved.


Made with ❤️ in the Netherlands by Flux76