@oncely/upstash 1.0.1 → 1.0.2
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 +71 -114
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,164 +1,121 @@
|
|
|
1
1
|
# @oncely/upstash
|
|
2
2
|
|
|
3
|
-
Upstash
|
|
3
|
+
Upstash Redis adapter for oncely. HTTP-based, perfect for serverless and edge.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@oncely/upstash)
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
9
|
+
```bash
|
|
7
10
|
npm install @oncely/core @oncely/upstash
|
|
11
|
+
```
|
|
8
12
|
|
|
9
|
-
##
|
|
13
|
+
## Quick Start
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
```typescript
|
|
16
|
+
import { upstash } from '@oncely/upstash';
|
|
12
17
|
import { next } from '@oncely/next';
|
|
13
18
|
|
|
14
19
|
// From environment variables
|
|
15
|
-
const
|
|
20
|
+
export const POST = next({ storage: upstash() })(handler);
|
|
21
|
+
```
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
const storage = upstash({
|
|
19
|
-
url: 'https://...',
|
|
20
|
-
token: '...',
|
|
21
|
-
});
|
|
23
|
+
## Configuration
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
```typescript
|
|
26
|
+
import { upstash } from '@oncely/upstash';
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
// From environment variables (ONCELY_UPSTASH_REST_URL, ONCELY_UPSTASH_REST_TOKEN)
|
|
29
|
+
const storage = upstash();
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
// With explicit credentials
|
|
32
|
+
const storage = upstash({
|
|
33
|
+
url: 'https://your-redis.upstash.io',
|
|
34
|
+
token: 'your-rest-token',
|
|
35
|
+
});
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
// With options
|
|
38
|
+
const storage = upstash({
|
|
39
|
+
url: process.env.UPSTASH_REDIS_REST_URL,
|
|
40
|
+
token: process.env.UPSTASH_REDIS_REST_TOKEN,
|
|
41
|
+
keyPrefix: 'myapp:idem:',
|
|
42
|
+
});
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
await storage.clear(); // void
|
|
44
|
+
// With existing @upstash/redis client
|
|
45
|
+
import { Redis } from '@upstash/redis';
|
|
46
|
+
const client = new Redis({ url: '...', token: '...' });
|
|
47
|
+
const storage = upstash({ client });
|
|
48
|
+
```
|
|
39
49
|
|
|
40
|
-
|
|
50
|
+
## Environment Variables
|
|
41
51
|
|
|
42
|
-
|
|
52
|
+
| Variable | Description |
|
|
53
|
+
| --------------------------- | ------------------------------------------------------ |
|
|
54
|
+
| `ONCELY_UPSTASH_REST_URL` | Upstash REST endpoint (e.g., `https://xyz.upstash.io`) |
|
|
55
|
+
| `ONCELY_UPSTASH_REST_TOKEN` | Upstash REST token |
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
## Usage with Next.js App Router
|
|
45
58
|
|
|
46
59
|
```typescript
|
|
47
60
|
// app/api/orders/route.ts
|
|
48
|
-
import {
|
|
61
|
+
import { next } from '@oncely/next';
|
|
49
62
|
import { upstash } from '@oncely/upstash';
|
|
50
63
|
|
|
51
|
-
export const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
storage: upstash(),
|
|
55
|
-
ttl: 60000,
|
|
64
|
+
export const POST = next({ storage: upstash() })(async (req) => {
|
|
65
|
+
const order = await createOrder(await req.json());
|
|
66
|
+
return Response.json(order, { status: 201 });
|
|
56
67
|
});
|
|
68
|
+
```
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
return handler(request, async (req) => {
|
|
60
|
-
const body = await req.json();
|
|
61
|
-
const order = await createOrder(body);
|
|
62
|
-
|
|
63
|
-
return Response.json(order, { status: 201 });
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
````
|
|
67
|
-
|
|
68
|
-
### Vercel Serverless Function
|
|
70
|
+
## Usage with Next.js Pages Router
|
|
69
71
|
|
|
70
72
|
```typescript
|
|
71
|
-
// api/
|
|
72
|
-
import
|
|
73
|
-
import { oncely } from '@oncely/core';
|
|
73
|
+
// pages/api/orders.ts
|
|
74
|
+
import { pages } from '@oncely/next/pages';
|
|
74
75
|
import { upstash } from '@oncely/upstash';
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!idempotencyKey) {
|
|
82
|
-
return res.status(400).json({ error: 'Idempotency-Key required' });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const result = await storage.acquire(idempotencyKey, null, 60000);
|
|
86
|
-
|
|
87
|
-
if (result.status === 'hit') {
|
|
88
|
-
res.setHeader('Idempotency-Replay', 'true');
|
|
89
|
-
return res.json(result.response.data);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (result.status === 'conflict') {
|
|
93
|
-
return res.status(409).json({ error: 'Request in progress' });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const data = await processCheckout(req.body);
|
|
98
|
-
await storage.save(idempotencyKey, {
|
|
99
|
-
data,
|
|
100
|
-
createdAt: Date.now(),
|
|
101
|
-
hash: null,
|
|
102
|
-
});
|
|
103
|
-
return res.json(data);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
await storage.release(idempotencyKey);
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
77
|
+
export default pages({ storage: upstash() })(async (req, res) => {
|
|
78
|
+
const order = await createOrder(req.body);
|
|
79
|
+
res.status(201).json(order);
|
|
80
|
+
});
|
|
109
81
|
```
|
|
110
82
|
|
|
111
|
-
|
|
83
|
+
## Pre-configured Factory
|
|
112
84
|
|
|
113
85
|
```typescript
|
|
114
|
-
|
|
86
|
+
// lib/idempotency.ts
|
|
87
|
+
import { configure } from '@oncely/next';
|
|
115
88
|
import { upstash } from '@oncely/upstash';
|
|
116
89
|
|
|
117
|
-
export
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
token: env.ONCELY_UPSTASH_REST_TOKEN,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const handler = oncely.handler({
|
|
130
|
-
storage,
|
|
131
|
-
ttl: 60000,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
return handler(request, async (req) => {
|
|
135
|
-
// Your handler logic
|
|
136
|
-
return new Response(JSON.stringify({ ok: true }));
|
|
137
|
-
});
|
|
138
|
-
},
|
|
139
|
-
};
|
|
90
|
+
export const idempotent = configure({
|
|
91
|
+
storage: upstash(),
|
|
92
|
+
ttl: '1h',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// app/api/orders/route.ts
|
|
96
|
+
import { idempotent } from '@/lib/idempotency';
|
|
97
|
+
|
|
98
|
+
export const POST = idempotent()(handler);
|
|
140
99
|
```
|
|
141
100
|
|
|
142
|
-
|
|
101
|
+
## Vercel KV
|
|
143
102
|
|
|
144
|
-
|
|
103
|
+
Vercel KV is compatible with Upstash. Use the Vercel KV environment variables:
|
|
145
104
|
|
|
146
105
|
```typescript
|
|
147
106
|
const storage = upstash({
|
|
148
|
-
url: process.env.
|
|
149
|
-
token: process.env.
|
|
107
|
+
url: process.env.KV_REST_API_URL,
|
|
108
|
+
token: process.env.KV_REST_API_TOKEN,
|
|
150
109
|
});
|
|
151
110
|
```
|
|
152
111
|
|
|
153
|
-
##
|
|
112
|
+
## Key Prefix
|
|
113
|
+
|
|
114
|
+
All keys are prefixed with `oncely:` by default. Customize with:
|
|
154
115
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
| Connection pooling | ✅ Not needed | ⚠️ Required |
|
|
159
|
-
| Cold start | ✅ Instant | ⚠️ Connection overhead |
|
|
160
|
-
| Scaling | ✅ Auto | Manual |
|
|
161
|
-
| Global | ✅ Multi-region | Manual setup |
|
|
116
|
+
```typescript
|
|
117
|
+
const storage = upstash({ keyPrefix: 'myapp:idem:' });
|
|
118
|
+
```
|
|
162
119
|
|
|
163
120
|
## License
|
|
164
121
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oncely/upstash",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Upstash Redis adapter for oncely idempotency",
|
|
5
5
|
"author": "stacks0x",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,19 +37,19 @@
|
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@upstash/redis": "^1.28.0",
|
|
40
|
-
"@oncely/core": "0.2.
|
|
40
|
+
"@oncely/core": "0.2.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@upstash/redis": "^1.28.0",
|
|
44
44
|
"tsup": "^8.0.1",
|
|
45
45
|
"typescript": "^5.3.3",
|
|
46
|
-
"@oncely/core": "0.2.
|
|
46
|
+
"@oncely/core": "0.2.2"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
|
-
"node": ">=
|
|
52
|
+
"node": ">=20.0.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsup",
|