nurev 0.0.9 → 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/README.md +52 -0
- package/package.json +1 -1
- package/templates/base/frontend/server/plugins/pocketbase.ts +4 -1
- package/templates/pocketbase/.env.example +1 -1
- package/templates/pocketbase/README.md +8 -8
- package/templates/pocketbase-bun/Makefile +4 -4
- package/utils/sqlite.js +1 -1
- package/workflows/pocketbase/index.js +26 -1
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Template generator for [Nuxt](https://nuxt.com/) configured with “on-demand re
|
|
|
12
12
|
|
|
13
13
|
## How to use it?
|
|
14
14
|
|
|
15
|
+
1. Run the script
|
|
16
|
+
|
|
15
17
|
```bash
|
|
16
18
|
bunx nurev
|
|
17
19
|
```
|
|
@@ -23,3 +25,53 @@ npx nurev
|
|
|
23
25
|
```bash
|
|
24
26
|
pnpm dlx nurev
|
|
25
27
|
```
|
|
28
|
+
|
|
29
|
+
2. After the script completes, start the backend in dev mode
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
make backend-dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Then start frontend in dev mode
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
make frontend-dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## How it works?
|
|
42
|
+
|
|
43
|
+
1. **Interactive prompts**: Asks you to choose a package manager (`bun`, `npm`, or `pnpm`) and a backend (`PocketBase`)
|
|
44
|
+
2. **Template copying**: Copies the base Nuxt template, backend-specific files, and package-manager-specific configuration to your current directory
|
|
45
|
+
3. **Installation & setup**: Runs `make install` to install dependencies and `make setup` to initialize the project
|
|
46
|
+
4. **Backend configuration**: Applies backend-specific setup (e.g., PocketBase configuration)
|
|
47
|
+
5. **Ready to use**: Your Nuxt project with on-demand revalidation is ready
|
|
48
|
+
|
|
49
|
+
## On-Demand Revalidation
|
|
50
|
+
|
|
51
|
+
The caching system uses a webhook pattern between PocketBase and Nuxt:
|
|
52
|
+
|
|
53
|
+
1. **Cache storage**: Nuxt routes use `defineCachedEventHandler` with `swr: true` (stale-while-revalidate) and `maxAge: 604800` (1 week). Route rules set `prerender: true` for `/` and `swr: true` for `/posts/**`
|
|
54
|
+
|
|
55
|
+
2. **Backend hooks**: Backend (PocketBase) registers hooks on `OnRecordAfterCreateSuccess`, `OnRecordAfterUpdateSuccess`, `OnRecordAfterDeleteSuccess`, `OnBackupCreate` and `OnBackupRestore` for collections defined in `TABLES_TRIGGER`
|
|
56
|
+
|
|
57
|
+
3. **Webhook notification**: When a record changes, PocketBase sends an authenticated POST (JWT Bearer token) to `/api/private/reloadcache` with the table name and record ID
|
|
58
|
+
|
|
59
|
+
4. **Selective invalidation**: The Nuxt endpoint validates the JWT and uses `useStorage("cache")` to remove only cache entries matching the changed table and record ID. Backup events trigger a full cache clear
|
|
60
|
+
|
|
61
|
+
5. **Next request**: Subsequent requests fetch fresh data and repopulate the cache
|
|
62
|
+
|
|
63
|
+
## Pros and Cons
|
|
64
|
+
|
|
65
|
+
### Pros
|
|
66
|
+
|
|
67
|
+
- ⚡ **Performance**: Cached responses are served instantly without hitting the database on each request
|
|
68
|
+
- 📉 **Reduced backend load**: Fewer API calls to PocketBase, lowering server resource usage
|
|
69
|
+
- 🔄 **Fresh data on demand**: Cache is invalidated only when data actually changes, avoiding stale content
|
|
70
|
+
- 🚀 **Scalability**: SWR (stale-while-revalidate) serves cached content while refreshing in the background, keeping response times low under high traffic
|
|
71
|
+
|
|
72
|
+
### Cons
|
|
73
|
+
|
|
74
|
+
- 🔗 **Tight coupling**: The backend must know the frontend URL and send webhooks, creating a dependency between services
|
|
75
|
+
- 🌐 **Network reliability**: If the webhook fails to reach Nuxt (network issues, downtime), the cache won't be invalidated and stale data persists
|
|
76
|
+
- 🖥️ **Single-server limitation**: Cache is stored in-memory/local storage, so this approach doesn't work well with multi-instance deployments without a shared cache layer (e.g., Redis)
|
|
77
|
+
- ⚙️ **Complexity**: Requires configuring JWT secrets, environment variables, and ensuring both services are running and communicating correctly
|
package/package.json
CHANGED
|
@@ -7,7 +7,10 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
7
7
|
try {
|
|
8
8
|
await pb
|
|
9
9
|
.collection("users")
|
|
10
|
-
.authWithPassword(
|
|
10
|
+
.authWithPassword(
|
|
11
|
+
config.pocketbaseUser,
|
|
12
|
+
config.pocketbasePassword.toString(),
|
|
13
|
+
);
|
|
11
14
|
|
|
12
15
|
nitroApp.hooks.hook("request", (event) => {
|
|
13
16
|
event.context.pocketbase = pb;
|
|
@@ -53,20 +53,20 @@ Tech stack:
|
|
|
53
53
|
make backend-dev
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
3.
|
|
57
|
-
|
|
58
|
-
4. Create a new post
|
|
59
|
-
|
|
60
|
-
5. Start frontend dev mode
|
|
56
|
+
3. Start frontend dev mode
|
|
61
57
|
|
|
62
58
|
```bash
|
|
63
59
|
make frontend-dev
|
|
64
60
|
```
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
4. Copy a post ID from the `posts` collection
|
|
63
|
+
|
|
64
|
+
5. Open `http://localhost:3000/posts/[id]` and replace `[id]` with the copied post ID
|
|
65
|
+
|
|
66
|
+
6. Verify caching by checking the `ETag` header in the response (Network tab in browser dev tools). The `ETag` will change when you modify post records or create a backup
|
|
67
67
|
|
|
68
|
-
##
|
|
68
|
+
## More commands
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
make
|
|
71
|
+
make help
|
|
72
72
|
```
|
|
@@ -38,19 +38,19 @@ frontend-setup:
|
|
|
38
38
|
|
|
39
39
|
frontend-install:
|
|
40
40
|
@echo "Installing frontend dependencies..."
|
|
41
|
-
cd frontend && bun
|
|
41
|
+
cd frontend && bun install
|
|
42
42
|
|
|
43
43
|
frontend-dev: frontend-install
|
|
44
44
|
@echo "Starting frontend development server..."
|
|
45
|
-
cd frontend && bun
|
|
45
|
+
cd frontend && bun run dev
|
|
46
46
|
|
|
47
47
|
frontend-build: frontend-install
|
|
48
48
|
@echo "Building frontend for production..."
|
|
49
|
-
cd frontend && bun
|
|
49
|
+
cd frontend && bun run build
|
|
50
50
|
|
|
51
51
|
frontend-preview: frontend-build
|
|
52
52
|
@echo "Previewing frontend production build..."
|
|
53
|
-
cd frontend && bun
|
|
53
|
+
cd frontend && bun run preview
|
|
54
54
|
|
|
55
55
|
install: backend-update frontend-install
|
|
56
56
|
@echo "Dependencies installed"
|
package/utils/sqlite.js
CHANGED
|
@@ -9,7 +9,7 @@ export class SQLiteManager {
|
|
|
9
9
|
return new Promise(async (resolve, reject) => {
|
|
10
10
|
try {
|
|
11
11
|
this.db.exec(
|
|
12
|
-
`CREATE TABLE IF NOT
|
|
12
|
+
`CREATE TABLE IF NOT EXISTS ${name} (${params.toString()})`,
|
|
13
13
|
);
|
|
14
14
|
resolve();
|
|
15
15
|
} catch (error) {
|
|
@@ -77,7 +77,32 @@ export function setup(paths) {
|
|
|
77
77
|
|
|
78
78
|
console.log(`\nUser API created\n`);
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
await db.insert("_collections", {
|
|
81
|
+
created: `'${date}'`,
|
|
82
|
+
fields: `'[{"autogeneratePattern":"[a-z0-9]{15}","hidden":false,"id":"text3208210256","max":15,"min":15,"name":"id","pattern":"^[a-z0-9]+$","presentable":false,"primaryKey":true,"required":true,"system":true,"type":"text"},{"hidden":false,"id":"autodate2990389176","name":"created","onCreate":true,"onUpdate":false,"presentable":false,"system":false,"type":"autodate"},{"hidden":false,"id":"autodate3332085495","name":"updated","onCreate":true,"onUpdate":true,"presentable":false,"system":false,"type":"autodate"}]'`,
|
|
83
|
+
indexes: `'[]'`,
|
|
84
|
+
name: `'posts'`,
|
|
85
|
+
options: `'{}'`,
|
|
86
|
+
system: false,
|
|
87
|
+
updated: `'${date}'`,
|
|
88
|
+
type: `'base'`,
|
|
89
|
+
listRule: `'@request.auth.id != ""'`,
|
|
90
|
+
viewRule: `'@request.auth.id != ""'`,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await db.create("posts", [
|
|
94
|
+
`created TEXT DEFAULT '' NOT NULL`,
|
|
95
|
+
`id TEXT PRIMARY KEY DEFAULT ('r'||lower(hex(randomblob(7)))) NOT NULL`,
|
|
96
|
+
`updated TEXT DEFAULT '' NOT NULL`,
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
await db.insert("posts", {
|
|
100
|
+
created: `'${date}'`,
|
|
101
|
+
updated: `'${date}'`,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(`\nPosts example created\n`);
|
|
105
|
+
|
|
81
106
|
resolve();
|
|
82
107
|
} catch (error) {
|
|
83
108
|
reject(error);
|