Skip to content

Instantly share code, notes, and snippets.

@laiso

laiso/client.tsx Secret

Last active March 17, 2024 02:12
Show Gist options
  • Save laiso/fe4ddd71f3f041db47daddc35b31c5c2 to your computer and use it in GitHub Desktop.
Save laiso/fe4ddd71f3f041db47daddc35b31c5c2 to your computer and use it in GitHub Desktop.
Prisma D1 Todo App https://prisma-d1.pages.dev/
import { createRoot } from "react-dom/client";
import { InferRequestType, hc } from "hono/client";
import { AppType } from ".";
import useSWR from "swr";
import { useState } from "react";
interface TodoListProps {}
const client = hc<AppType>("/");
function TodoList(props: TodoListProps) {
const [title, setTitle] = useState("");
const $get = client.todos.$get;
const fetcher = (arg: InferRequestType<typeof $get>) => async () => {
const res = await $get(arg);
return await res.json();
};
const { data, error, isLoading, mutate } = useSWR(
"/todos",
fetcher({
query: {
name: "SWR",
},
})
);
if (error) return <div>failed to load</div>;
if (isLoading) return <div>loading...</div>;
return (
<div>
<table>
<thead>
<tr>
<th>Title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{data.todos.map((todo: { id: number; title: string }) => (
<tr key={todo.id}>
<td>{todo.title}</td>
<td>
<button
onClick={async () => {
const response = await client.todos[":id"].$delete({
param: {
id: todo.id,
},
});
mutate(response.todos);
}}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
<table>
<tr>
<td>
<input
type="text"
name="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</td>
<td>
<button
type="submit"
onClick={async () => {
setTitle("");
const response = await client.todos.$post({
form: {
title,
},
});
mutate(response.todos);
}}
>
Add
</button>
</td>
</tr>
</table>
</div>
);
}
const domNode = document.getElementById("root")!;
const root = createRoot(domNode);
root.render(<TodoList />);
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link rel="stylesheet" href="src/styles.css" />
<script type="module" src="src/client.tsx"></script>
<title>Prisma D1 Todo App</title>
</head>
<body>
<h1>Prisma D1 Todo App</h1>
<p>
<a href="https://www.npmjs.com/package/@prisma/adapter-d1?activeTab=readme">
Prisma driver adapter for Cloudflare D1.
</a>
</p>
<div id="root"></div>
<footer><a href="https://gist.github.com/laiso/fe4ddd71f3f041db47daddc35b31c5c2">source code(gist.github.com)</a>
</footer>
</body>
</html>
import { Hono } from "hono";
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
import { type D1Database } from "@cloudflare/workers-types";
type Bindings = {
MY_DATABASE: D1Database;
ASSETS: any;
};
function createPrismaClient(env: Bindings) {
const adapter = new PrismaD1(env.MY_DATABASE);
return new PrismaClient({ adapter });
}
const app = new Hono<{
Bindings: Bindings;
}>();
const schema = z.object({
title: z.string().min(1),
});
app.get("/todos", async (c) => {
const prisma = createPrismaClient(c.env);
const todos = await prisma.todo.findMany();
return c.json({ todos });
});
app.post("/todos", zValidator("form", schema), async (c) => {
const { title } = c.req.valid("form");
const prisma = createPrismaClient(c.env);
const todo = await prisma.todo.create({
data: {
title,
},
});
const todos = await prisma.todo.findMany();
return c.json({ todos });
});
app.delete("/todos/:id", async (c) => {
const id = c.req.param("id");
const prisma = createPrismaClient(c.env);
await prisma.todo.delete({
where: {
id: Number(id),
},
});
const todos = await prisma.todo.findMany();
return c.json({ todos });
});
app.get("/*", async (ctx) => {
return await ctx.env.ASSETS.fetch(ctx.req.raw);
});
export type AppType = typeof app;
export default app;
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
model Todo {
id Int @id @default(autoincrement())
title String
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
max-width: 500px;
}
#root {
max-width: 600px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
box-sizing: border-box;
}
button {
padding: 8px 12px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 3px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment