asopi tech
asopi techIndie Developer

Asopitech With · Health app

Withings app with Alopex DB & Alopex Skulk

The Withings app is a health app that records and shows weight, sleep and heart rate measured by Withings devices. How would we make it with Alopex DB and Alopex Skulk?

Updated: Oct 12, 2026

What the service does

A Withings-like health service measures weight and body composition, sleep, activity and heart rate every day through scales, watches, blood pressure monitors and sleep sensors, and shows the long-term trend. The data is a time series of readings per device, goals, and daily scores derived from the readings.

Where the data lives

  • Skulk time series: weight, heart rate, steps and sleep readings are appended to Skulk, tagged by device and measure. Weekly averages and daily highs and lows come from time-bucketed aggregation. The same data answers PromQL queries too.
  • Alopex DB tables: profile, devices, goals and daily notes live in tables. Goals sit next to the values aggregated in Skulk to show progress.

Writing the readings that arrive from devices and computing each morning’s score are app code.

Deployment

Skulk and Alopex DB are embedded in the app on the device. Both run on the device and grow into a server and cluster on the same engine. Health data is stored in files on the device and stays there. Skulk keeps its data as Parquet, so the user can export it and read it directly in other analysis tools.

Why Alopex DB and Alopex Skulk fit

Health data is the data people most want to keep close. Skulk ingests and aggregates time series inside the app, and Alopex DB backs goals and records with SQL. Years of health records are handled on the device.

See it in code

Goals and records live in Alopex DB (measurement time series in Skulk). Just the core of placing the latest measurement next to the goal.

import alopex

# 目標と記録は Alopex DB(計測の時系列は Skulk が集計)
db = alopex.Database.open("withings.alopex")

db.execute_sql("CREATE TABLE IF NOT EXISTS goals (metric TEXT, target REAL)")
db.execute_sql("CREATE TABLE IF NOT EXISTS scores (ymd DATE, metric TEXT, value REAL)")

for metric, target in [("weight", 68.0), ("sleep_hours", 7.0)]:
    db.execute_sql("INSERT INTO goals (metric, target) VALUES (?, ?)", [metric, target])
for ymd, metric, value in [("2026-10-21", "weight", 69.5), ("2026-10-21", "sleep_hours", 6.5)]:
    db.execute_sql("INSERT INTO scores (ymd, metric, value) VALUES (?, ?, ?)", [ymd, metric, value])

# 最新の計測値を目標と並べて、達成の度合いを見せる
rows = db.execute_sql("""
    SELECT s.metric, s.value, g.target
    FROM scores AS s
    JOIN goals AS g ON g.metric = s.metric
    ORDER BY s.metric
""")
for row in rows:
    print(row["metric"], "今:", row["value"], "目標:", row["target"])

The goal and the value Skulk aggregated sit side by side to show how far along you are. The fine-grained weight, heart-rate and sleep time series is ingested by Skulk, tagged by device and metric.

Time series in Alopex Skulk, goals and records in Alopex DB. Both embed in the device, and we recommend the pair for a health app.