-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4da2655
commit 9ada6af
Showing
1 changed file
with
40 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,123 +1,54 @@ | ||
# plainstack | ||
|
||
plainstack is a web framework for TypeScript that allows you to build production-ready apps in a single file. | ||
Plainstack is a web framework where you start with a single-file. It extends [Hono](https://github.com/honojs/hono) with full-stack concerns like database migrations, background jobs, scheduled jobs, and more. | ||
|
||
## Example App | ||
## Features | ||
|
||
```typescript | ||
import { Hono } from "hono"; | ||
import { jsxRenderer } from "hono/jsx-renderer"; | ||
import { logger } from "hono/logger"; | ||
import { form, store } from "plainstack"; | ||
import { bunSqlite, secret } from "plainstack/bun"; | ||
import { session } from "plainstack/session"; | ||
- Small API in the spirit of [Hono](https://github.com/honojs/hono) | ||
- Database migrations | ||
- Automatic CRUD operations and zod schema for entities | ||
- Fully typed SQL queries | ||
- Zod-based form validation | ||
- Authentication helpers that go well with Hono's OAuth providers | ||
- Cookie-based session management | ||
- `<Toast />` component for flash messages | ||
- Background jobs with parallel workers | ||
- Scheduled jobs to run code at a specific time or interval (cron syntax) | ||
- JSX client components | ||
|
||
interface Items { | ||
content: string; | ||
createdAt: number; | ||
id: string; | ||
} | ||
## Getting Started | ||
|
||
interface DB { | ||
items: Items; | ||
} | ||
Clone the [starter](https://github.com/justplainstuff/starter) repo to get a plainstack app with Tailwind, OAuth and more: | ||
|
||
const { database, migrate } = bunSqlite<DB>(); | ||
|
||
await migrate(({ schema }) => { | ||
return schema | ||
.createTable("items") | ||
.addColumn("id", "text", (col) => col.primaryKey().notNull()) | ||
.addColumn("content", "text", (col) => col.notNull()) | ||
.addColumn("created_at", "integer", (col) => col.notNull()) | ||
.execute(); | ||
}); | ||
|
||
const entities = await store(database); | ||
```bash | ||
git clone [email protected]:justplainstuff/starter.git | ||
``` | ||
|
||
const app = new Hono(); | ||
```bash | ||
bun dev | ||
``` | ||
|
||
app.use(logger()); | ||
app.use(session({ encryptionKey: await secret() })); | ||
or install `plainstack` manually: | ||
|
||
app.get( | ||
"*", | ||
jsxRenderer(({ children }) => { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<meta name="color-scheme" content="light dark" /> | ||
<link | ||
rel="stylesheet" | ||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" | ||
/> | ||
<link | ||
rel="stylesheet" | ||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.colors.min.css" | ||
/> | ||
<title>So many todos</title> | ||
</head> | ||
<body> | ||
<main class="container"> | ||
<h1>{children}</h1> | ||
</main> | ||
</body> | ||
</html> | ||
); | ||
}) | ||
); | ||
```bash | ||
bun install plainstack | ||
``` | ||
|
||
app.get("/", async (c) => { | ||
const info = c.var.session.get("info"); | ||
const items = await entities("items").all(); | ||
return c.render( | ||
<div> | ||
<h1>Todo App</h1> | ||
{info && <article>{info}</article>} | ||
<ul class="items-list"> | ||
{items.map((item) => ( | ||
<li key={item.id}> | ||
<div class="grid"> | ||
{item.content}{" "} | ||
<form | ||
style={{ display: "inline" }} | ||
method="post" | ||
action={`/delete/${item.id}`} | ||
> | ||
<button type="submit">Delete</button> | ||
</form> | ||
</div> | ||
</li> | ||
))} | ||
</ul> | ||
<form method="post" action="/add"> | ||
<input | ||
type="text" | ||
name="content" | ||
placeholder="Enter todo item" | ||
required | ||
/> | ||
<button type="submit">Add</button> | ||
</form> | ||
</div> | ||
); | ||
}); | ||
## Documentation [WIP] | ||
|
||
app.post("/add", form(entities("items").zod), async (c) => { | ||
const submission = c.req.valid("form"); | ||
const data = submission.value; | ||
await entities("items").create(data); | ||
c.var.session.flash("info", "Item added"); | ||
return c.redirect("/"); | ||
}); | ||
- Database migrations | ||
- Create entity | ||
- Update entity | ||
- Query entity | ||
- SQL queries | ||
- Form validation | ||
- Sessions | ||
- Authentication | ||
- Background jobs | ||
- Scheduled jobs | ||
- `<Toast />` | ||
- Client Components | ||
|
||
app.post("/delete/:id", async (c) => { | ||
await entities("items").delete(c.req.param("id")); | ||
c.var.session.flash("info", "Item deleted"); | ||
return c.redirect("/"); | ||
}); | ||
## Examples | ||
|
||
export default app; | ||
``` | ||
Check out the [full list of examples](https://github.com/justplainstuff/plainstack/tree/main/example). |