Readme

Create an API from a lowdb blob

This val exports a function that takes a lowdb instance and returns a Hono router that can be used to interact with the data. This is the beginning of an implementation of something like json-server for Val Town.

The resulting server also comes with a frontend at /. The code for the frontend can be found here.

See this val for an example.

Things I'd like to implement

  • All HTTP methods
  • Custom route definitions (like in json-server)
  • Custom frontends
  • Filtering, sorting, pagination, etc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/** @jsxImportSource https://esm.sh/hono@3.9.2/jsx **/
import { Homepage } from "https://esm.town/v/nbbaier/dbToAPIFrontend";
import { checkResource, getResources, type Options, validate } from "https://esm.town/v/nbbaier/dbToApiHelpers";
import { Hono } from "npm:hono";
// TODO: implement options
export async function createServer(db, options: Options & { auth?: { username: string; password: string } } = {}) {
const app = new Hono();
if (options.auth) {
app.use(
"*",
);
}
// Make sure our db contains "data"
if (!validate(db)) {
app.get("/", c => c.text("Wrong data shape"));
return app;
}
// Extract information about the resources in the db
const resources = getResources(db);
// Get / => returns home
app.get("/", c => c.html(<Homepage resources={resources} />));
// GET /db => returns all db data
app.get("/db", c => c.json(db.data));
// GET /:resource => if :resource is valid, returns db.data[resource]
app.get("/:resource", c => {
const { resource } = c.req.param();
const queries = c.req.queries();
if (!checkResource(resource, resources)) {
return c.text(`The requested resource (${resource}) is not available`);
}
let q;
let chain = db.data[resource];
if (queries.q) {
q = queries.q[0];
chain = chain.filter(obj => {
obj.name === q;
});
}
return c.json({ data: chain });
});
// GET /:resource/:id => if :resource is valid, returns db.data[resource]
app.get("/:resource/:id", c => {
const { resource, id } = c.req.param();
if (!checkResource(resource, resources)) {
return c.text(`The requested resource (${resource}) is not available`);
}
const items = db.data[resource] as { id: number }[];
return c.json(items.find(item => item.id === parseInt(id)));
});
// TODO POST, PUT, PATCH, DELETE /:resource
app.on(["POST", "PATCH", "PUT", "DELETE"], "/:resource", (c) => {
const { method } = c.req;
const { resource } = c.req.param();
if (!checkResource(resource, resources)) {
return c.text(`The requested resource (${resource}) is not available`);
}
return c.text(`${method} /${resource}`);
});
return app;
}
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.