12## Serving assets to the frontend
13
14This backend HTTP server is responsible for serving all static assets to the browser to render the app, including HTML, JavaScript (including all client-side React), CSS, and even the favicon SVG.
15
16In a normal server environment, you would likely use a middleware [like this one](https://hono.dev/docs/getting-started/nodejs#serve-static-files) to serve static files. Some frameworks or deployment platforms automatically make any content inside a `public/` folder public.
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`. This routes are called from the React app to refresh and update data.
29
30## Errors
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.
1import { sqlite } from "https://esm.town/v/stevekrouse/sqlite";
2
3export const tableName = "reactHonoStarter_messages";
4
5export async function createTables() {
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React from "https://esm.sh/react@18.2.0";
3import type { Message } from "../shared/types.ts";
4
5export function MessageInput({ onSubmit }: { onSubmit: () => void }) {
6 const [message, setMessage] = React.useState("");
7
8 const handleSubmit = async (e: React.FormEvent) => {
9 e.preventDefault();
10 if (!message.trim()) return;
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
3import { type Message } from "../shared/utils.ts";
4import { App } from "./components/App.tsx";
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>React Hono Val Town Starter</title>
7 <link rel="stylesheet" href="/public/style.css">
8 <link rel="icon" href="/public/favicon.svg" sizes="any" type="image/svg+xml">
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React from "https://esm.sh/react@18.2.0";
3import { type Message, MESSAGE_LIMIT } from "../../shared/utils.ts";
4import { MessageInput } from "./MessageInput.tsx";
7 { initialMessages = [], thisProjectURL }: { initialMessages?: Message[]; thisProjectURL?: string },
8) {
9 const [messages, setMessages] = React.useState(initialMessages);
10
11 const fetchMessages = async () => {
1/** @jsxImportSource https://esm.sh/preact */
2import { makeBadge } from "npm:badge-maker";
3import { render } from "npm:preact-render-to-string";
4import prettyMilliseconds from "npm:pretty-ms";
5
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
3import React, { useState } from "https://esm.sh/react@18.2.0";
4
5const NO_PHRASES = [
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import Markdown from "https://esm.sh/react-markdown@9?deps=react@18.2.0";
3import { getProjectFiles, readFile } from "https://esm.town/v/stevekrouse/utils@196-main/serve-public/index.ts";
4import { renderToString } from "npm:react-dom@18.2.0/server";
5import { Layout } from "./Layout.tsx";
6
44}
45
46function html(children: React.ReactNode) {
47 return new Response(
48 renderToString(