12const app = new Hono();
13
14// Endpoint to get all pages from a specified database
15app.get("/:id", async (c) => {
16 try {
39 return c.html(html);
40 } catch (error) {
41 console.error("Error querying database:", error);
42 return c.html(
43 `
45 <body>
46 <h1>Error</h1>
47 <p>Failed to query database: ${error.message}</p>
48 </body>
49 </html>
57app.get("/:id/filtered", async (c) => {
58 try {
59 const databaseId = c.req.param("id");
60 const statusValue = c.req.query("status") || "Embed ready";
61
62 if (!databaseId) {
63 return c.html(
64 `
66 <body>
67 <h1>Error</h1>
68 <p>Missing required parameter: database ID</p>
69 </body>
70 </html>
74 }
75
76 // Query the database with a filter for the specified status
77 // Using select type for the Status property
78 const response = await notion.databases.query({
79 database_id: databaseId,
80 filter: {
81 property: "Status",
101 return c.html(html);
102 } catch (error) {
103 console.error("Error querying database:", error);
104 return c.html(
105 `
107 <body>
108 <h1>Error</h1>
109 <p>Failed to query database: ${error.message}</p>
110 </body>
111 </html>
122 <body>
123 <h1>Showcase HTML API</h1>
124 <p>Use /showcase-html/:database_id to get all pages from a database as HTML</p>
125 </body>
126 </html>
10// update the cache that was saved to blob storage by the /cache route
11export default async function(interval: Interval) {
12 // build key fragment to find the blob with the database cache in it
13 const blobKey = [
14 "cache", // this matches the path that sets the blob and tells us what the purpose of this blob is
15 "db", // we added this bit to the key when we saved it so that we know is a database cache when we look at blob storage
16 ].join("--");
17 // get blob with database JSON
18 const blobKeys = await blob.list(blobKey); // array of keys that start with the blobKey fragment
19 // console.log(blobKeys[0]);
20 const databaseId = blobKeys[0].key.split("--").pop();
21 // console.log(databaseId);
22 // Query the database without any filters to get all pages
23 const response = await notion.databases.query({
24 database_id: databaseId,
25 filter: {
26 property: "Publication status",
10// update the cache that was saved to blob storage by the /cache route
11export default async function(interval: Interval) {
12 // build key fragment to find the blob with the database cache in it
13 const blobKey = [
14 "cache", // this matches the path that sets the blob and tells us what the purpose of this blob is
15 "db", // we added this bit to the key when we saved it so that we know is a database cache when we look at blob storage
16 ].join("--");
17 // get blob with database JSON
18 const blobKeys = await blob.list(blobKey); // array of keys that start with the blobKey fragment
19 // console.log(blobKeys[0]);
20 const databaseId = blobKeys[0].key.split("--").pop();
21 // console.log(databaseId);
22 // Query the database without any filters to get all pages
23 const response = await notion.databases.query({
24 database_id: databaseId,
25 filter: {
26 property: "Publication status",
4
5* `index.ts` - this is the **entrypoint** for this whole project
6* `database/` - this contains the code for interfacing with the app's SQLite database table
7
8## Hono
26## CRUD API Routes
27
28This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
29
30## Errors
1# Database
2
3This app uses [Val Town SQLite](https://docs.val.town/std/sqlite/) to manage data. Every Val Town account comes with a free SQLite database, hosted on [Turso](https://turso.tech/). This folder is broken up into two files:
4
5* `migrations.ts` - code to set up the database tables the app needs
6* `queries.ts` - functions to run queries against those tables, which are imported and used in the main Hono server in `/backend/index.ts`
7
8## Migrations
9
10In `backend/database/migrations.ts`, this app creates a new SQLite table `reactHonoStarter_messages` to store messages.
11
12This "migration" runs once on every app startup because it's imported in `index.ts`. You can comment this line out for a slight (30ms) performance improvement on cold starts. It's left in so that users who fork this project will have the migration run correctly.
13
14SQLite has much more limited support for altering existing tables as compared to other databases. Often it's easier to create new tables with the schema you want, and then copy the data over. Happily LLMs are quite good at those sort of database operations, but please reach out in the [Val Town Discord](https://discord.com/invite/dHv45uN5RY) if you need help.
15
16## Queries
17
18The queries file is where running the migrations happen in this app. It'd also be reasonable for that to happen in index.ts, or as is said above, for that line to be commented out, and only run when actual changes are made to your database schema.
19
20The queries file exports functions to get and write data. It relies on shared types and data imported from the `/shared` directory.
3import { createTables, tableName } from "./migrations.ts";
4
5// This will create the database table if it doesn't exist.
6// This will run every time the app starts up. You can
7// comment out this line for a modest (30ms) perforamnce improvement
8// on cold starts. It's left in to ensure the database tables are
9// automatically set up correctly for users who fork this app.
10await createTables();
13
14 However, you should know that SQLite has much more limited
15 support for altering existing tables as compared to other databases.
16 Often it's easier to create new tables with the schema you want, and then
17 copy the data over. */
1import { parseProject, readFile, serveFile } from "https://esm.town/v/std/utils/index.ts";
2import { Hono } from "npm:hono";
3import { getMessages, insertMessage } from "./database/queries.ts";
4
5const app = new Hono();
9- The **client-side entrypoint** is `/frontend/index.html`, which in turn imports `/frontend/index.tsx`, which in turn imports the React app from `/frontend/components/App.tsx`.
10
11[React Hono Example](https://www.val.town/x/stevekrouse/reactHonoExample) is a fuller featured example project, with a SQLite database table, queries, client-side CSS and a favicon, and some shared code that runs on both client and server.
75app.get("/:id/filtered", async (c) => {
76 try {
77 const databaseId = c.req.param("id");
78 const statusValue = c.req.query("status") || "Embed ready";
79
80 if (!databaseId) {
81 return c.json({
82 success: false,
83 message: "Missing required parameter: database ID",
84 }, 400);
85 }
86
87 // Query the database with a filter for the specified status
88 // Using select type for the Status property
89 const response = await notion.databases.query({
90 database_id: databaseId,
91 filter: {
92 property: "Status",
116 });
117 } catch (error) {
118 console.error("Error querying database:", error);
119 return c.json({
120 success: false,
121 message: "Failed to query database",
122 error: error.message,
123 }, 500);
127// Simple test endpoint
128// app.get("/", async (c) => {
129// return c.text("Showcase API - Use /showcase/:database_id to get all pages from a database");
130// });
131