A self-hosted, local-first calendar API. Own your schedule data, query it with SQL, import/export standard calendar files, and build whatever you want on top of it.
Built with TypeScript, Koa.js, and DuckDB.
Most calendar apps are black boxes. You can't query your own data, script against it, or export it in a useful way without jumping through hoops. CalendAPIr gives you a clean REST API backed by an embedded database you fully control — no accounts, no sync, no lock-in.
Because the storage layer is DuckDB (an embedded OLAP database), you can run analytical queries against your schedule that no cloud app will ever let you do: how many hours did you spend in meetings last month, which days are most fragmented, what your average meeting duration is.
- Time audit — Import your Google Calendar or Outlook export and run SQL queries against your own schedule history.
- Automation — Script event creation from cron jobs, config files, or any external trigger via the REST API.
- Custom frontends — Build a minimal calendar UI, a CLI planner, or a dashboard on top of a stable local API.
- Availability checker — Query your events to find free blocks for scheduling, focus time, or deep work.
- Self-hosted alternative — Replace a cloud calendar backend for personal or small-team use without any external dependencies.
CalendAPIr ships with a built-in single-page interface available at http://localhost:3000 when the server is running. No separate frontend setup required.
Events tab — lists all events ordered by date, with upcoming/past badges. Create, edit, and delete events through a modal form. Meeting links are clickable directly from the list.
Analytics tab — shows a live summary (total events, upcoming count, this-week count, average duration, hours scheduled this week), a breakdown by day of week, and the five busiest days on record.
Import / Export tab — drag and drop an .ics file to import events from Google Calendar, Outlook, Apple Calendar, or any app that exports iCalendar format. One-click export downloads all your events as calendar.ics.
A stats bar at the top of every tab keeps key numbers visible at all times.
- TypeScript (strict, ESM)
- Node.js + Koa.js + @koa/router
- DuckDB (file-based embedded database)
- Zod (request validation)
- Vanilla JS single-page UI (no build step, served by the API)
Prerequisites: Node.js LTS, npm.
git clone <repo>
cd CalendAPIr
npm installCopy the environment file and adjust as needed:
cp .env .env.localPORT=3000
DB_PATH=calendar.duckdb
Run in development:
npm run devServer starts at http://localhost:3000.
Schema changes: If you modify the database schema, delete
calendar.duckdbbefore restarting so DuckDB recreates the table.
All endpoints are prefixed with /api/v1/calendar. IDs are UUID strings.
| Method | Path | Description |
|---|---|---|
| GET | /events | List all events, ordered by date |
| POST | /events | Create a new event |
| PUT | /events/:id | Update an existing event |
| DELETE | /events/:id | Delete an event |
| POST | /events/import | Import events from an ICS file |
| GET | /events/export | Export all events as an ICS file |
| Method | Path | Description |
|---|---|---|
| GET | /stats | Aggregated stats: totals, durations, busy days |
| Field | Type | Required | Notes |
|---|---|---|---|
| title | string | yes | 3-100 characters |
| date | string | yes | YYYY-MM-DD |
| startTime | string | yes | HH:MM (24h) |
| endTime | string | yes | HH:MM (24h), must be > start |
| description | string | no | Max 500 characters |
| meetingLink | string | no | Must be a valid URL |
Create an event
curl -X POST http://localhost:3000/api/v1/calendar/events \
-H "Content-Type: application/json" \
-d '{
"title": "Project Review",
"date": "2026-06-10",
"startTime": "14:00",
"endTime": "15:30",
"description": "Quarterly milestone review",
"meetingLink": "https://meet.example.com/abc"
}'Import from Google Calendar / Outlook
Export your calendar as a .ics file from your calendar app, then:
curl -X POST http://localhost:3000/api/v1/calendar/events/import \
-H "Content-Type: text/calendar" \
--data-binary @my-calendar.icsExport to ICS (re-import anywhere)
curl http://localhost:3000/api/v1/calendar/events/export -o calendar.icsGet stats
curl http://localhost:3000/api/v1/calendar/statsReturns totals, upcoming count, events this week, average duration, hours scheduled this week, breakdown by day of week, and your five busiest days.
- CalDAV / iCal server — Expose the data as a CalDAV endpoint so standard clients (Apple Calendar, Thunderbird) can sync directly.
- Recurring events — Add RRULE support for weekly standups, habits, and repeating blocks.
- Timezone support — Store events with timezone info and handle conversions on read.
- SQL query endpoint — Expose a read-only
/queryendpoint that runs arbitrary DuckDB SQL against your events, making it a proper personal analytics tool. - Soft deletes — Add a
deletedAtcolumn so nothing is truly lost. - CLI companion — A small CLI that wraps common API calls for quick event creation from the terminal.