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-user
andx-auth-key
headers - 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
) withkey
isTEXT
andUNIQUE
,value
isTEXT
- Get
SUPABASE_URL
andSUPABASE_KEY
- Deploy to Deno Deploy
- Set env get from
4)
to settings project - Add your server to
Kindle
with 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 andraiseError
helpermiddleware/authorize.ts
: reads headers, validates against KV, attachesuser
to 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.ts
users/
auth.get.ts
create.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.db
file is created in the project directory. - Deno KV: switch to
DenoKv
inkv/index.ts
if 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.ts
and replace thekv
initialization with the desired driver. - Supabase example:
- Set env vars
SUPABASE_URL
andSUPABASE_KEY
- Create table
kv_store
on your Supabase Postgres (see below) - Use the
SupabaseKv
driver
- 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_store
table 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.ts
and useSupabaseKv
as 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.ts
as 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/progress
with 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.