@basicbenframework/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +35 -0
- package/README.md +588 -0
- package/bin/cli.js +8 -0
- package/create-basicben-app/index.js +205 -0
- package/create-basicben-app/package.json +30 -0
- package/create-basicben-app/template/.env.example +24 -0
- package/create-basicben-app/template/README.md +59 -0
- package/create-basicben-app/template/basicben.config.js +33 -0
- package/create-basicben-app/template/index.html +54 -0
- package/create-basicben-app/template/migrations/001_create_users.js +15 -0
- package/create-basicben-app/template/migrations/002_create_posts.js +18 -0
- package/create-basicben-app/template/public/.gitkeep +0 -0
- package/create-basicben-app/template/seeds/01_users.js +29 -0
- package/create-basicben-app/template/seeds/02_posts.js +43 -0
- package/create-basicben-app/template/src/client/components/Alert.jsx +11 -0
- package/create-basicben-app/template/src/client/components/Avatar.jsx +11 -0
- package/create-basicben-app/template/src/client/components/BackLink.jsx +10 -0
- package/create-basicben-app/template/src/client/components/Button.jsx +19 -0
- package/create-basicben-app/template/src/client/components/Card.jsx +10 -0
- package/create-basicben-app/template/src/client/components/Empty.jsx +6 -0
- package/create-basicben-app/template/src/client/components/Input.jsx +12 -0
- package/create-basicben-app/template/src/client/components/Loading.jsx +6 -0
- package/create-basicben-app/template/src/client/components/Logo.jsx +40 -0
- package/create-basicben-app/template/src/client/components/Nav/DarkModeToggle.jsx +23 -0
- package/create-basicben-app/template/src/client/components/Nav/DesktopNav.jsx +32 -0
- package/create-basicben-app/template/src/client/components/Nav/MobileNav.jsx +107 -0
- package/create-basicben-app/template/src/client/components/NavLink.jsx +10 -0
- package/create-basicben-app/template/src/client/components/PageHeader.jsx +8 -0
- package/create-basicben-app/template/src/client/components/PostCard.jsx +19 -0
- package/create-basicben-app/template/src/client/components/Textarea.jsx +12 -0
- package/create-basicben-app/template/src/client/components/ThemeContext.jsx +5 -0
- package/create-basicben-app/template/src/client/contexts/ToastContext.jsx +94 -0
- package/create-basicben-app/template/src/client/layouts/AppLayout.jsx +60 -0
- package/create-basicben-app/template/src/client/layouts/AuthLayout.jsx +33 -0
- package/create-basicben-app/template/src/client/layouts/DocsLayout.jsx +60 -0
- package/create-basicben-app/template/src/client/layouts/RootLayout.jsx +25 -0
- package/create-basicben-app/template/src/client/pages/Auth.jsx +55 -0
- package/create-basicben-app/template/src/client/pages/Authentication.jsx +236 -0
- package/create-basicben-app/template/src/client/pages/Database.jsx +426 -0
- package/create-basicben-app/template/src/client/pages/Feed.jsx +34 -0
- package/create-basicben-app/template/src/client/pages/FeedPost.jsx +37 -0
- package/create-basicben-app/template/src/client/pages/GettingStarted.jsx +136 -0
- package/create-basicben-app/template/src/client/pages/Home.jsx +206 -0
- package/create-basicben-app/template/src/client/pages/PostForm.jsx +69 -0
- package/create-basicben-app/template/src/client/pages/Posts.jsx +59 -0
- package/create-basicben-app/template/src/client/pages/Profile.jsx +68 -0
- package/create-basicben-app/template/src/client/pages/Routing.jsx +207 -0
- package/create-basicben-app/template/src/client/pages/Testing.jsx +251 -0
- package/create-basicben-app/template/src/client/pages/Validation.jsx +210 -0
- package/create-basicben-app/template/src/controllers/AuthController.js +81 -0
- package/create-basicben-app/template/src/controllers/HomeController.js +17 -0
- package/create-basicben-app/template/src/controllers/PostController.js +86 -0
- package/create-basicben-app/template/src/controllers/ProfileController.js +66 -0
- package/create-basicben-app/template/src/helpers/api.js +24 -0
- package/create-basicben-app/template/src/main.jsx +9 -0
- package/create-basicben-app/template/src/middleware/auth.js +16 -0
- package/create-basicben-app/template/src/models/Post.js +63 -0
- package/create-basicben-app/template/src/models/User.js +42 -0
- package/create-basicben-app/template/src/routes/App.jsx +38 -0
- package/create-basicben-app/template/src/routes/api/auth.js +7 -0
- package/create-basicben-app/template/src/routes/api/posts.js +15 -0
- package/create-basicben-app/template/src/routes/api/profile.js +8 -0
- package/create-basicben-app/template/src/server/index.js +16 -0
- package/create-basicben-app/template/vite.config.js +18 -0
- package/database.sqlite +0 -0
- package/my-test-app/.env.example +24 -0
- package/my-test-app/README.md +59 -0
- package/my-test-app/basicben.config.js +33 -0
- package/my-test-app/database.sqlite-shm +0 -0
- package/my-test-app/database.sqlite-wal +0 -0
- package/my-test-app/index.html +54 -0
- package/my-test-app/migrations/001_create_users.js +15 -0
- package/my-test-app/migrations/002_create_posts.js +18 -0
- package/my-test-app/package-lock.json +2160 -0
- package/my-test-app/package.json +29 -0
- package/my-test-app/public/.gitkeep +0 -0
- package/my-test-app/seeds/01_users.js +29 -0
- package/my-test-app/seeds/02_posts.js +43 -0
- package/my-test-app/src/client/components/Alert.jsx +11 -0
- package/my-test-app/src/client/components/Avatar.jsx +11 -0
- package/my-test-app/src/client/components/BackLink.jsx +10 -0
- package/my-test-app/src/client/components/Button.jsx +19 -0
- package/my-test-app/src/client/components/Card.jsx +10 -0
- package/my-test-app/src/client/components/Empty.jsx +6 -0
- package/my-test-app/src/client/components/Input.jsx +12 -0
- package/my-test-app/src/client/components/Loading.jsx +6 -0
- package/my-test-app/src/client/components/Logo.jsx +40 -0
- package/my-test-app/src/client/components/Nav/DarkModeToggle.jsx +23 -0
- package/my-test-app/src/client/components/Nav/DesktopNav.jsx +32 -0
- package/my-test-app/src/client/components/Nav/MobileNav.jsx +107 -0
- package/my-test-app/src/client/components/NavLink.jsx +10 -0
- package/my-test-app/src/client/components/PageHeader.jsx +8 -0
- package/my-test-app/src/client/components/PostCard.jsx +19 -0
- package/my-test-app/src/client/components/Textarea.jsx +12 -0
- package/my-test-app/src/client/components/ThemeContext.jsx +5 -0
- package/my-test-app/src/client/contexts/AppContext.jsx +13 -0
- package/my-test-app/src/client/contexts/ToastContext.jsx +94 -0
- package/my-test-app/src/client/layouts/AppLayout.jsx +60 -0
- package/my-test-app/src/client/layouts/AuthLayout.jsx +33 -0
- package/my-test-app/src/client/layouts/DocsLayout.jsx +60 -0
- package/my-test-app/src/client/layouts/RootLayout.jsx +25 -0
- package/my-test-app/src/client/pages/Auth.jsx +55 -0
- package/my-test-app/src/client/pages/Authentication.jsx +236 -0
- package/my-test-app/src/client/pages/Database.jsx +426 -0
- package/my-test-app/src/client/pages/Feed.jsx +34 -0
- package/my-test-app/src/client/pages/FeedPost.jsx +37 -0
- package/my-test-app/src/client/pages/GettingStarted.jsx +136 -0
- package/my-test-app/src/client/pages/Home.jsx +206 -0
- package/my-test-app/src/client/pages/PostForm.jsx +69 -0
- package/my-test-app/src/client/pages/Posts.jsx +59 -0
- package/my-test-app/src/client/pages/Profile.jsx +68 -0
- package/my-test-app/src/client/pages/Routing.jsx +207 -0
- package/my-test-app/src/client/pages/Testing.jsx +251 -0
- package/my-test-app/src/client/pages/Validation.jsx +210 -0
- package/my-test-app/src/controllers/AuthController.js +81 -0
- package/my-test-app/src/controllers/HomeController.js +17 -0
- package/my-test-app/src/controllers/PostController.js +86 -0
- package/my-test-app/src/controllers/ProfileController.js +66 -0
- package/my-test-app/src/helpers/api.js +24 -0
- package/my-test-app/src/main.jsx +9 -0
- package/my-test-app/src/middleware/auth.js +16 -0
- package/my-test-app/src/models/Post.js +63 -0
- package/my-test-app/src/models/User.js +42 -0
- package/my-test-app/src/routes/App.jsx +38 -0
- package/my-test-app/src/routes/api/auth.js +7 -0
- package/my-test-app/src/routes/api/posts.js +15 -0
- package/my-test-app/src/routes/api/profile.js +8 -0
- package/my-test-app/src/server/index.js +16 -0
- package/my-test-app/vite.config.js +18 -0
- package/package.json +61 -0
- package/scripts/test-app.sh +59 -0
- package/src/auth/jwt.js +195 -0
- package/src/auth/password.js +132 -0
- package/src/cli/colors.js +31 -0
- package/src/cli/dispatcher.js +168 -0
- package/src/cli/parser.js +91 -0
- package/src/client/context.js +4 -0
- package/src/client/hooks.js +50 -0
- package/src/client/index.js +3 -0
- package/src/client/router.js +184 -0
- package/src/commands/build.js +155 -0
- package/src/commands/dev.js +206 -0
- package/src/commands/help.js +84 -0
- package/src/commands/make-controller.js +36 -0
- package/src/commands/make-middleware.js +44 -0
- package/src/commands/make-migration.js +51 -0
- package/src/commands/make-model.js +38 -0
- package/src/commands/make-route.js +36 -0
- package/src/commands/make-seed.js +38 -0
- package/src/commands/migrate-fresh.js +32 -0
- package/src/commands/migrate-rollback.js +30 -0
- package/src/commands/migrate-status.js +41 -0
- package/src/commands/migrate.js +30 -0
- package/src/commands/seed.js +47 -0
- package/src/commands/start.js +69 -0
- package/src/commands/test.js +46 -0
- package/src/db/Grammar.js +125 -0
- package/src/db/QueryBuilder.js +476 -0
- package/src/db/adapters/neon.js +170 -0
- package/src/db/adapters/planetscale.js +146 -0
- package/src/db/adapters/postgres.js +166 -0
- package/src/db/adapters/sqlite.js +125 -0
- package/src/db/adapters/turso.js +165 -0
- package/src/db/index.js +156 -0
- package/src/db/migrator.js +250 -0
- package/src/db/seeder.js +124 -0
- package/src/index.js +12 -0
- package/src/scaffolding/index.js +152 -0
- package/src/server/body-parser.js +159 -0
- package/src/server/cors.js +63 -0
- package/src/server/default-entry.js +13 -0
- package/src/server/http.js +221 -0
- package/src/server/index.js +168 -0
- package/src/server/loader.js +128 -0
- package/src/server/router.js +281 -0
- package/src/server/static.js +139 -0
- package/src/validation/index.js +436 -0
- package/src/vite/config.js +49 -0
- package/stubs/controller.stub +48 -0
- package/stubs/middleware-auth.stub +29 -0
- package/stubs/middleware.stub +9 -0
- package/stubs/migration.stub +17 -0
- package/stubs/model.stub +77 -0
- package/stubs/route.stub +13 -0
- package/stubs/seed.stub +16 -0
- package/stubs/vite.config.stub +18 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write # Required for Trusted Publishing
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '24'
|
|
21
|
+
registry-url: 'https://registry.npmjs.org'
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: npm test
|
|
25
|
+
|
|
26
|
+
- name: Publish @basicbenframework/core
|
|
27
|
+
run: npm publish --access public --provenance
|
|
28
|
+
env:
|
|
29
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
30
|
+
|
|
31
|
+
- name: Publish create-basicben-app
|
|
32
|
+
working-directory: ./create-basicben-app
|
|
33
|
+
run: npm publish --access public --provenance
|
|
34
|
+
env:
|
|
35
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
# BasicBen
|
|
2
|
+
|
|
3
|
+
> Ship faster with less. A full-stack framework for React. Zero runtime dependencies.
|
|
4
|
+
|
|
5
|
+
BasicBen gives you a productive, convention-driven structure for building React apps with a Node.js backend — without pulling in a bloated framework or locking you into vendor ecosystems.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why BasicBen?
|
|
10
|
+
|
|
11
|
+
Most JS frameworks make one of two mistakes: they do too much (Next.js, Remix) or they do nothing and leave you to wire everything yourself. BasicBen sits in the middle — conventions when you want them, escape hatches when you don't.
|
|
12
|
+
|
|
13
|
+
| Framework | Trade-off |
|
|
14
|
+
|-----------|-----------|
|
|
15
|
+
| Next.js / Remix | Too much magic, vendor lock-in |
|
|
16
|
+
| Express + Vite | Wire everything yourself |
|
|
17
|
+
| **BasicBen** | ✅ Conventions + control |
|
|
18
|
+
|
|
19
|
+
### Core Principles
|
|
20
|
+
|
|
21
|
+
- **Zero runtime dependencies** — HTTP server, router, JWT auth, validation — all written from scratch using Node.js built-ins
|
|
22
|
+
- **Laravel-inspired DX** — migrations, controllers, models, and scaffolding commands that feel familiar
|
|
23
|
+
- **No lock-in** — just React, Node.js, and Vite. Eject anytime — your code is still your code
|
|
24
|
+
- **Escape hatches** — every convention can be overridden via `basicben.config.js`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Node.js 24.14+
|
|
31
|
+
- npm 9+
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx create-basicben-app my-app
|
|
39
|
+
cd my-app
|
|
40
|
+
npm install
|
|
41
|
+
npx basicben migrate
|
|
42
|
+
npx basicben dev
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Your app is running at `http://localhost:3000` with a fully functional blog app — user auth, posts, and profiles included.
|
|
46
|
+
|
|
47
|
+
### Local Development
|
|
48
|
+
|
|
49
|
+
To develop against a local copy of the framework:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx create-basicben-app my-app --local
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This sets the `basicben` dependency to `file:../basicben-framework` instead of fetching from npm.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Project Structure
|
|
60
|
+
|
|
61
|
+
A new BasicBen project looks like this:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
my-app/
|
|
65
|
+
├── index.html # Vite entry point
|
|
66
|
+
├── src/
|
|
67
|
+
│ ├── main.jsx # React entry point
|
|
68
|
+
│ ├── routes/
|
|
69
|
+
│ │ ├── App.jsx # Client routes
|
|
70
|
+
│ │ └── api/ # Auto-loaded API routes
|
|
71
|
+
│ │ ├── auth.js
|
|
72
|
+
│ │ ├── posts.js
|
|
73
|
+
│ │ └── profile.js
|
|
74
|
+
│ ├── controllers/ # Business logic
|
|
75
|
+
│ │ ├── AuthController.js
|
|
76
|
+
│ │ ├── PostController.js
|
|
77
|
+
│ │ └── ProfileController.js
|
|
78
|
+
│ ├── models/ # DB query wrappers
|
|
79
|
+
│ │ ├── User.js
|
|
80
|
+
│ │ └── Post.js
|
|
81
|
+
│ ├── middleware/ # Route middleware
|
|
82
|
+
│ │ └── auth.js
|
|
83
|
+
│ ├── helpers/ # Utility functions
|
|
84
|
+
│ │ └── api.js # Fetch wrapper with auth
|
|
85
|
+
│ └── client/ # React frontend
|
|
86
|
+
│ ├── layouts/ # Layout components
|
|
87
|
+
│ │ ├── AppLayout.jsx
|
|
88
|
+
│ │ ├── AuthLayout.jsx
|
|
89
|
+
│ │ └── DocsLayout.jsx
|
|
90
|
+
│ ├── pages/ # Page components
|
|
91
|
+
│ │ ├── Home.jsx
|
|
92
|
+
│ │ ├── Auth.jsx
|
|
93
|
+
│ │ ├── Feed.jsx
|
|
94
|
+
│ │ ├── Posts.jsx
|
|
95
|
+
│ │ ├── Profile.jsx
|
|
96
|
+
│ │ └── ...
|
|
97
|
+
│ └── components/ # Reusable UI components
|
|
98
|
+
│ ├── Button.jsx
|
|
99
|
+
│ ├── Card.jsx
|
|
100
|
+
│ ├── Input.jsx
|
|
101
|
+
│ └── ...
|
|
102
|
+
├── migrations/
|
|
103
|
+
│ ├── 001_create_users.js
|
|
104
|
+
│ └── 002_create_posts.js
|
|
105
|
+
├── public/
|
|
106
|
+
└── basicben.config.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Routes, middleware, and models are loaded automatically — no manual imports needed.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Starter Features
|
|
114
|
+
|
|
115
|
+
Every new BasicBen project includes a fully functional blog app:
|
|
116
|
+
|
|
117
|
+
### Authentication
|
|
118
|
+
- User registration and login with JWT
|
|
119
|
+
- Protected routes with auth middleware
|
|
120
|
+
- Password hashing with `node:crypto`
|
|
121
|
+
|
|
122
|
+
### User Profile
|
|
123
|
+
- View and edit profile (name, email)
|
|
124
|
+
- Change password
|
|
125
|
+
|
|
126
|
+
### Blog Posts
|
|
127
|
+
- Create, edit, delete posts
|
|
128
|
+
- Publish/draft toggle
|
|
129
|
+
- List your own posts
|
|
130
|
+
|
|
131
|
+
### Public Feed
|
|
132
|
+
- View all published posts
|
|
133
|
+
- Single post view with author info
|
|
134
|
+
|
|
135
|
+
### React Components
|
|
136
|
+
The frontend uses reusable components:
|
|
137
|
+
- `Button`, `Input`, `Textarea`, `Card` — form elements
|
|
138
|
+
- `Alert`, `Loading`, `Empty` — feedback states
|
|
139
|
+
- `PageHeader`, `BackLink`, `Avatar` — layout helpers
|
|
140
|
+
- `ThemeContext` — light/dark mode support
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## CLI
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Development
|
|
148
|
+
basicben dev # Start Vite + Node dev server
|
|
149
|
+
basicben build # Bundle client + server for production
|
|
150
|
+
basicben build --static # Build client only (for static hosts)
|
|
151
|
+
basicben start # Run production server
|
|
152
|
+
basicben test # Run tests with Vitest
|
|
153
|
+
|
|
154
|
+
# Scaffolding
|
|
155
|
+
basicben make:controller <name> # Generate a controller
|
|
156
|
+
basicben make:route <name> # Generate a route file
|
|
157
|
+
basicben make:model <name> # Generate a model
|
|
158
|
+
basicben make:migration <name> # Generate a migration file
|
|
159
|
+
basicben make:middleware <name> # Generate middleware (auth template if name is 'auth')
|
|
160
|
+
|
|
161
|
+
# Database
|
|
162
|
+
basicben migrate # Run all pending migrations
|
|
163
|
+
basicben migrate:rollback # Roll back the last batch
|
|
164
|
+
basicben migrate:fresh # Drop everything and re-run all
|
|
165
|
+
basicben migrate:status # Show which migrations have run
|
|
166
|
+
|
|
167
|
+
# Help
|
|
168
|
+
basicben help # Show all commands
|
|
169
|
+
basicben help <command> # Show help for a specific command
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Routing
|
|
175
|
+
|
|
176
|
+
### API Routes
|
|
177
|
+
|
|
178
|
+
Create a file in `src/routes/api/` and export a default function that receives the router:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
// src/routes/api/users.js
|
|
182
|
+
import { UserController } from '../../controllers/UserController.js'
|
|
183
|
+
|
|
184
|
+
export default (router) => {
|
|
185
|
+
router.get('/api/users', UserController.index)
|
|
186
|
+
router.get('/api/users/:id', UserController.show)
|
|
187
|
+
router.post('/api/users', UserController.create)
|
|
188
|
+
router.put('/api/users/:id', UserController.update)
|
|
189
|
+
router.delete('/api/users/:id', UserController.destroy)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
All files in `src/routes/api/` are registered automatically on startup.
|
|
194
|
+
|
|
195
|
+
### Client Routes
|
|
196
|
+
|
|
197
|
+
Client-side routing is configured in `src/routes/App.jsx`:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
// src/routes/App.jsx
|
|
201
|
+
import { createClientApp } from '@basicbenframework/core/client'
|
|
202
|
+
import { AppLayout } from '../client/layouts/AppLayout'
|
|
203
|
+
import { Home } from '../client/pages/Home'
|
|
204
|
+
import { Posts } from '../client/pages/Posts'
|
|
205
|
+
|
|
206
|
+
export default createClientApp({
|
|
207
|
+
layout: AppLayout,
|
|
208
|
+
routes: {
|
|
209
|
+
'/': Home,
|
|
210
|
+
'/posts': { component: Posts, auth: true },
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Controllers
|
|
218
|
+
|
|
219
|
+
Generate one with:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
basicben make:controller UserController
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
// src/controllers/UserController.js
|
|
227
|
+
import { User } from '../models/User.js'
|
|
228
|
+
|
|
229
|
+
export const UserController = {
|
|
230
|
+
index: async (req, res) => {
|
|
231
|
+
const users = await User.all()
|
|
232
|
+
res.json(users)
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
show: async (req, res) => {
|
|
236
|
+
const user = await User.find(req.params.id)
|
|
237
|
+
if (!user) return res.status(404).json({ error: 'Not found' })
|
|
238
|
+
res.json(user)
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
create: async (req, res) => {
|
|
242
|
+
const user = await User.create(req.body)
|
|
243
|
+
res.status(201).json(user)
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
update: async (req, res) => {
|
|
247
|
+
const user = await User.update(req.params.id, req.body)
|
|
248
|
+
res.json(user)
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
destroy: async (req, res) => {
|
|
252
|
+
await User.destroy(req.params.id)
|
|
253
|
+
res.status(204).send()
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Models
|
|
261
|
+
|
|
262
|
+
Generate one with:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
basicben make:model User
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Models are thin wrappers around raw DB queries — no ORM, no magic.
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
// src/models/User.js
|
|
272
|
+
import { db } from '../db/index.js'
|
|
273
|
+
|
|
274
|
+
export const User = {
|
|
275
|
+
all: () => db.all(`SELECT * FROM users`),
|
|
276
|
+
find: (id) => db.get(`SELECT * FROM users WHERE id = ?`, id),
|
|
277
|
+
create: (data) => db.run(`INSERT INTO users (name, email) VALUES (?, ?)`, [data.name, data.email]),
|
|
278
|
+
update: (id, data) => db.run(`UPDATE users SET name = ?, email = ? WHERE id = ?`, [data.name, data.email, id]),
|
|
279
|
+
destroy: (id) => db.run(`DELETE FROM users WHERE id = ?`, id)
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Migrations
|
|
286
|
+
|
|
287
|
+
Generate a migration with:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
basicben make:migration create_users
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This creates a timestamped file in `migrations/`:
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
// migrations/001_create_users.js
|
|
297
|
+
export const up = (db) => {
|
|
298
|
+
db.run(`
|
|
299
|
+
CREATE TABLE users (
|
|
300
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
301
|
+
name TEXT NOT NULL,
|
|
302
|
+
email TEXT UNIQUE NOT NULL,
|
|
303
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
304
|
+
)
|
|
305
|
+
`)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export const down = (db) => {
|
|
309
|
+
db.run(`DROP TABLE users`)
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Then run:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
basicben migrate
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
BasicBen tracks which migrations have run in a `_migrations` table. Running `migrate` again is always safe.
|
|
320
|
+
|
|
321
|
+
### Rolling back
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
basicben migrate:rollback # Undo the last batch
|
|
325
|
+
basicben migrate:fresh # Drop everything and start over
|
|
326
|
+
basicben migrate:status # See what's run and what hasn't
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Middleware
|
|
332
|
+
|
|
333
|
+
Create a file in `src/middleware/` and export a default function. Middleware is loaded automatically before routes, in filename order.
|
|
334
|
+
|
|
335
|
+
```js
|
|
336
|
+
// src/middleware/auth.js
|
|
337
|
+
export default (req, res, next) => {
|
|
338
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
339
|
+
if (!token) return res.status(401).json({ error: 'Unauthorized' })
|
|
340
|
+
// verify token...
|
|
341
|
+
next()
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Validation
|
|
348
|
+
|
|
349
|
+
BasicBen includes a lightweight validation system with 20+ built-in rules:
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
import { validate, rules } from 'basicben/validation'
|
|
353
|
+
|
|
354
|
+
const result = await validate(req.body, {
|
|
355
|
+
email: [rules.required, rules.email],
|
|
356
|
+
password: [rules.required, rules.min(8)],
|
|
357
|
+
age: [rules.optional, rules.integer, rules.between(18, 120)]
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
if (result.fails()) {
|
|
361
|
+
return res.status(422).json({ errors: result.errors })
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// result.data contains validated data
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Built-in Rules
|
|
368
|
+
|
|
369
|
+
`required`, `optional`, `string`, `numeric`, `integer`, `boolean`, `array`, `email`, `url`, `min`, `max`, `between`, `in`, `notIn`, `regex`, `confirmed`, `different`, `length`, `alpha`, `alphanumeric`, `date`, `before`, `after`
|
|
370
|
+
|
|
371
|
+
### Custom Rules
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
const uniqueEmail = async (value) => {
|
|
375
|
+
const exists = await db.get('SELECT 1 FROM users WHERE email = ?', value)
|
|
376
|
+
return exists ? 'Email already exists' : null
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
await validate(req.body, {
|
|
380
|
+
email: [rules.required, rules.email, uniqueEmail]
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Authentication
|
|
387
|
+
|
|
388
|
+
BasicBen provides JWT helpers using Node's built-in `crypto` module — no `jsonwebtoken` dependency:
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
import { signJwt, verifyJwt } from 'basicben/auth'
|
|
392
|
+
|
|
393
|
+
// Sign a token
|
|
394
|
+
const token = signJwt({ userId: 1 }, process.env.APP_KEY, { expiresIn: '7d' })
|
|
395
|
+
|
|
396
|
+
// Verify a token
|
|
397
|
+
const payload = verifyJwt(token, process.env.APP_KEY)
|
|
398
|
+
if (!payload) {
|
|
399
|
+
// Invalid or expired
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The starter template includes a complete auth system:
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
// src/middleware/auth.js
|
|
407
|
+
import { verifyJwt } from 'basicben/auth'
|
|
408
|
+
|
|
409
|
+
export const auth = async (req, res, next) => {
|
|
410
|
+
const token = req.headers.authorization?.replace('Bearer ', '')
|
|
411
|
+
if (!token) return res.json({ error: 'Unauthorized' }, 401)
|
|
412
|
+
|
|
413
|
+
const payload = verifyJwt(token, process.env.APP_KEY)
|
|
414
|
+
if (!payload) return res.json({ error: 'Invalid token' }, 401)
|
|
415
|
+
|
|
416
|
+
req.userId = payload.userId
|
|
417
|
+
next()
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Use it in your routes:
|
|
422
|
+
|
|
423
|
+
```js
|
|
424
|
+
// src/routes/api/posts.js
|
|
425
|
+
import { auth } from '../../middleware/auth.js'
|
|
426
|
+
import { PostController } from '../../controllers/PostController.js'
|
|
427
|
+
|
|
428
|
+
export default (router) => {
|
|
429
|
+
router.get('/api/posts', auth, PostController.index)
|
|
430
|
+
router.post('/api/posts', auth, PostController.store)
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Testing
|
|
437
|
+
|
|
438
|
+
BasicBen uses Vitest for application tests:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
basicben test # Run once
|
|
442
|
+
basicben test --watch # Watch mode
|
|
443
|
+
basicben test --coverage # With coverage report
|
|
444
|
+
basicben test --ui # Open Vitest UI
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Create test files with `.test.js` or `.spec.js` suffix:
|
|
448
|
+
|
|
449
|
+
```js
|
|
450
|
+
// src/controllers/UserController.test.js
|
|
451
|
+
import { describe, it, expect } from 'vitest'
|
|
452
|
+
import { UserController } from './UserController.js'
|
|
453
|
+
|
|
454
|
+
describe('UserController', () => {
|
|
455
|
+
it('returns users list', async () => {
|
|
456
|
+
const res = { json: vi.fn() }
|
|
457
|
+
await UserController.index({}, res)
|
|
458
|
+
expect(res.json).toHaveBeenCalled()
|
|
459
|
+
})
|
|
460
|
+
})
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Environment Variables
|
|
466
|
+
|
|
467
|
+
BasicBen uses Node 20's built-in `--env-file` support. No `dotenv` required.
|
|
468
|
+
|
|
469
|
+
Create a `.env` file at your project root:
|
|
470
|
+
|
|
471
|
+
```env
|
|
472
|
+
PORT=3000
|
|
473
|
+
DATABASE_URL=./database.sqlite
|
|
474
|
+
APP_KEY=your-secret-key
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
A `.env.example` is included in every new project. Commit that, not `.env`.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Configuration
|
|
482
|
+
|
|
483
|
+
Override defaults in `basicben.config.js` at your project root:
|
|
484
|
+
|
|
485
|
+
```js
|
|
486
|
+
// basicben.config.js
|
|
487
|
+
export default {
|
|
488
|
+
// Server port (API)
|
|
489
|
+
port: 3001,
|
|
490
|
+
|
|
491
|
+
// CORS settings
|
|
492
|
+
cors: {
|
|
493
|
+
origin: '*',
|
|
494
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|
495
|
+
credentials: true
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
// Body parser
|
|
499
|
+
bodyParser: {
|
|
500
|
+
limit: '1mb'
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// Static files
|
|
504
|
+
static: {
|
|
505
|
+
dir: 'public'
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
// Database
|
|
509
|
+
db: {
|
|
510
|
+
driver: 'sqlite', // or 'postgres'
|
|
511
|
+
url: process.env.DATABASE_URL || './data.db'
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
// Auto-load routes from src/routes (default: true)
|
|
515
|
+
autoloadRoutes: true,
|
|
516
|
+
|
|
517
|
+
// Auto-load middleware from src/middleware (default: true)
|
|
518
|
+
autoloadMiddleware: true
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Dependencies
|
|
525
|
+
|
|
526
|
+
BasicBen has **zero runtime dependencies**:
|
|
527
|
+
|
|
528
|
+
```json
|
|
529
|
+
{
|
|
530
|
+
"dependencies": {},
|
|
531
|
+
"peerDependencies": {
|
|
532
|
+
"react": ">=18",
|
|
533
|
+
"react-dom": ">=18",
|
|
534
|
+
"vite": ">=7",
|
|
535
|
+
"@vitejs/plugin-react": ">=5"
|
|
536
|
+
},
|
|
537
|
+
"optionalDependencies": {
|
|
538
|
+
"better-sqlite3": ">=12",
|
|
539
|
+
"pg": ">=8"
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Everything is written from scratch:**
|
|
545
|
+
|
|
546
|
+
- HTTP server (uses node:http)
|
|
547
|
+
- CLI argument parser (no Commander)
|
|
548
|
+
- Router with groups, middleware, named routes
|
|
549
|
+
- Validation (no Zod/Joi)
|
|
550
|
+
- JWT auth (no jsonwebtoken, uses node:crypto)
|
|
551
|
+
- Migrations (no Knex/Sequelize)
|
|
552
|
+
- Environment variables (uses Node's built-in --env-file)
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Guiding Principles
|
|
557
|
+
|
|
558
|
+
1. **Write it yourself before adding a dependency** — if it's under 200 lines, own it
|
|
559
|
+
2. **Conventions over configuration** — sensible defaults, optional overrides
|
|
560
|
+
3. **Error messages are features** — tell you exactly what went wrong and how to fix it
|
|
561
|
+
4. **Stay boring** — resist clever abstractions until they're obviously needed
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Contributing
|
|
566
|
+
|
|
567
|
+
BasicBen is early. Contributions, issues, and ideas are welcome.
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
git clone https://github.com/BasicBenFramework/basicben-framework
|
|
571
|
+
cd basicben-framework
|
|
572
|
+
npm install
|
|
573
|
+
npm run dev
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Please read `CONTRIBUTING.md` before opening a PR.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## Inspiration
|
|
581
|
+
|
|
582
|
+
BasicBen takes cues from Laravel's developer experience — migrations, controllers, models, and scaffolding commands that feel familiar to PHP developers. If you've used Laravel and wished the JS ecosystem felt that good, this is for you.
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## License
|
|
587
|
+
|
|
588
|
+
MIT
|