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.serveconstants.ts: constants, error codes andraiseErrorhelpermiddleware/authorize.ts: reads headers, validates against KV, attachesuserto contextlogic/valid.ts: simple input validatorskv/: KV abstraction (KvBase) and driverskv/index.ts: selects the KV driver used at runtimekv/drivers/*: KV drivers (sqlite, deno-kv, supabase, redis, postgres, mongodb, duckdb)
v1/: API routeshealthcheck.tsusers/auth.get.tscreate.post.ts
syncs/progress/index.put.ts[document].get.ts
deno.json: tasks and import mapmain_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: usernamex-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 URLSUPABASE_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 URLSUPABASE_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: usernamex-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.
