A compact KOSync backend written in TypeScript for KOReader. Runs on Deno + Hono and can be deployed 100% free using Deno Deploy + Supabase (free).
- Runtime/server: Deno+Hono
- Validation/OpenAPI: zod+@hono/zod-openapi
- Storage: KV abstraction with multiple drivers (SQLite,Deno KV,Supabase,Redis,Postgres,MongoDB,DuckDB)
- API base path: /v1
- User sign-up with username/password stored in a KV store
- Lightweight auth via x-auth-userandx-auth-keyheaders
- Persist reading progress per document and retrieve the latest record
- Healthcheck endpoint
- Register Supabase
- Create project Supabase
- Create table kv_store(or table name your set in envTABLE_NAME) withkeyisTEXTandUNIQUE,valueisTEXT
- Get SUPABASE_URLandSUPABASE_KEY
- Deploy to Deno Deploy
- Set env get from 4)to settings project
- Add your server to Kindlewith url<base url>/v1. Example:https://kosync-typescript.tachibana-shin.deno.net/v1
- Enjoy
In addition, you can use my server has been deployed at https: //kosync-typescript.tachibana-shin.deno.net/v1 but I do not guarantee your data always online
- main.ts: bootstraps Hono app, middleware, routes, and Deno.serve
- constants.ts: constants, error codes and- raiseErrorhelper
- middleware/authorize.ts: reads headers, validates against KV, attaches- userto context
- logic/valid.ts: simple input validators
- kv/: KV abstraction (KvBase) and drivers- kv/index.ts: selects the KV driver used at runtime
- kv/drivers/*: KV drivers (sqlite, deno-kv, supabase, redis, postgres, mongodb, duckdb)
 
- v1/: API routes- healthcheck.ts
- users/- auth.get.ts
- create.post.ts
 
- syncs/progress/- index.put.ts
- [document].get.ts
 
 
- deno.json: tasks and import map
- main_test.ts: integration tests (expects server running)
Requirements: Deno v1.41+ (npm compatibility enabled).
By default the project uses the SQLite driver (see kv/index.ts) in test. You can:
- Keep SQLite for local development, or
- Switch to Deno KV for a zero-setup local store
Start the server:
- Dev (watch): deno task dev
- Local prod-like: deno task start
The server listens on http://localhost:8000 and exposes the API under /v1.
Notes about KV drivers locally:
- SQLite (default): a sqlite.dbfile is created in the project directory.
- Deno KV: switch to DenoKvinkv/index.tsif you prefer no SQLite dependency (see "Choose a KV driver").
Base URL: http://localhost:8000/v1 (or your Deno Deploy URL + /v1)
- Healthcheck
- GET /healthcheck
- Response: 200 { "state": "OK" }
- Create user
- POST /users/create
- JSON body: { "username": string(min 3), "password": string(min 6) }
- Response: 201 { "username": "..." }
- Errors:
- 402 { "code": 2002, "message": "Username is already registered." }
- 403 { "code": 2003, "message": "Invalid request" }
 
Example:
curl -X POST http://localhost:8000/v1/users/create \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"secret123"}'
- Check authorization
- GET /users/auth
- Required headers:
- x-auth-user: username
- x-auth-key: password used at sign-up
 
- Responses:
- 200 { "authorized": "OK" }
- 401 { "code": 2001, "message": "Unauthorized" }
 
Example:
curl -X GET http://localhost:8000/v1/users/auth \
  -H "x-auth-user: alice" \
  -H "x-auth-key: secret123"
- Update reading progress
- PUT /syncs/progress
- Required headers: x-auth-user,x-auth-key
- JSON body:
{ "document": "string (min 3)", "percentage": 0-100, "progress": "string", "device": "string", "device_id": "string (optional)" }
- Response: 200 { "document": "...", "timestamp": number }
- Errors: 401 Unauthorized,403 Invalid fields,500 Internal
Example:
curl -X PUT http://localhost:8000/v1/syncs/progress \
  -H "Content-Type: application/json" \
  -H "x-auth-user: alice" \
  -H "x-auth-key: secret123" \
  -d '{
        "document": "book_1984",
        "percentage": 42,
        "progress": "page_124",
        "device": "KOReader"
      }'
- Get reading progress by document
- GET /syncs/progress/{document}
- Required headers: x-auth-user,x-auth-key
- Success responses:
- 200 {}(if no data)
- 200 { document, percentage?, progress?, device?, device_id?, timestamp? }
 
Example:
curl -X GET http://localhost:8000/v1/syncs/progress/book_1984 \
  -H "x-auth-user: alice" \
  -H "x-auth-key: secret123"
Default: kv/index.ts uses SQLite. You can switch to any of the provided drivers depending on your environment:
- Deno KV (zero config; great for local or Deno Deploy with KV enabled)
- Supabase (ideal for free deployment with Deno Deploy + Supabase)
- Redis, Postgres, MongoDB, DuckDB (require extra setup/services)
How to switch:
- Open kv/index.tsand replace thekvinitialization with the desired driver.
- Supabase example:
- Set env vars SUPABASE_URLandSUPABASE_KEY
- Create table kv_storeon your Supabase Postgres (see below)
- Use the SupabaseKvdriver
 
- Set env vars 
Example (illustrative):
import { SupabaseKv } from "./drivers/supabase-kv.ts"
export const kv = new SupabaseKv()Deno KV example:
import { DenoKv } from "./drivers/deno-kv.ts"
export const kv = new DenoKv()Make sure await kv.init() is called before serving requests (already done in main.ts).
Goal: run the app on Deno Deploy and store data in Supabase (free tier).
Step 1: Prepare Supabase (free tier)
- Create a project at https://supabase.com/ (free tier)
- Obtain the following:
- SUPABASE_URL: Project URL
- SUPABASE_KEY: Service role key or anon key. For server-only access, service role key is recommended.
 
- Create the kv_storetable in your Database via SQL Editor:
create table if not exists kv_store (
  key text primary key,
  value jsonb
);
Notes:
- If Row Level Security (RLS) is enabled, using the Service Role key allows unrestricted access from server-side. Do not expose it to the client.
Step 2: Switch the KV driver to Supabase
- Edit kv/index.tsand useSupabaseKvas shown above.
Step 3: Push source to GitHub
- Push this repository to your own GitHub account
Step 4: Create a project on Deno Deploy
- Go to https://dash.deno.com/ → New Project → Link your GitHub repo
- Select main.tsas the entrypoint
- Set Environment Variables:
- SUPABASE_URL= your Supabase project URL
- SUPABASE_KEY= your Supabase service role key (recommended)
 
- Deploy
Step 5: Verify
- Healthcheck: https://<your-app>.deno.dev/v1/healthcheck
- Create user, auth, update/get progress as in the API section
Security considerations:
- This demo stores passwords in plain text in KV. For real use, hash passwords (e.g., bcrypt/argon2) before storing.
- Consider rate limiting, stricter auth, and CORS rules for production.
- Server base URL: https://<your-app>.deno.dev/v1
- Create an account via /users/create
- Client must send headers:
- x-auth-user: username
- x-auth-key: password
 
- Update progress via PUT /syncs/progresswith the schema above.
Depending on KOReader version/config, you may need a plugin or custom configuration to send custom headers.
Integration tests live in main_test.ts and exercise the HTTP API.
- Terminal A: start the local server
- deno task start
 
- Terminal B: run tests with full permissions (HTTP calls to localhost)
- deno test -A main_test.ts
 
With the SQLite driver, tests operate on sqlite.db in the repo directory.
See the LICENSE file for details.
