@desmat/redis-store 1.0.0 → 1.0.1
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 +26 -1
- package/dist/example.d.ts +1 -0
- package/dist/example.js +93 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +22 -18
- package/dist/repl.d.ts +1 -0
- package/dist/repl.js +139 -0
- package/dist/simple-example.d.ts +1 -0
- package/dist/simple-example.js +94 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -2,4 +2,29 @@
|
|
|
2
2
|
A lightweight wrapper for using Redis as a data store
|
|
3
3
|
|
|
4
4
|
## To build
|
|
5
|
-
|
|
5
|
+
```
|
|
6
|
+
npm run build
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
File `./src/example.ts` demonstrates simple use of this library.
|
|
11
|
+
|
|
12
|
+
### To run:
|
|
13
|
+
|
|
14
|
+
Create `.env` file with:
|
|
15
|
+
```
|
|
16
|
+
KV_REST_API_URL=*****
|
|
17
|
+
KV_REST_API_TOKEN=****
|
|
18
|
+
```
|
|
19
|
+
*Note: Using Vercel KV environment variables enables this library to be used without friction on Vercel's platform, but can be overwritten programmatically.*
|
|
20
|
+
|
|
21
|
+
then:
|
|
22
|
+
```
|
|
23
|
+
npm run example
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Or alternatively:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
export KV_REST_API_URL=***** KV_REST_API_TOKEN=***** && npm run example
|
|
30
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/example.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("./index");
|
|
4
|
+
require('dotenv').config();
|
|
5
|
+
const ThingRecordOptions = {
|
|
6
|
+
lookups: {
|
|
7
|
+
user: "createdBy", // enable .find({ user: <USERID> })
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
const store = {
|
|
11
|
+
users: new index_1.RedisStore({
|
|
12
|
+
key: "user",
|
|
13
|
+
options: { hardDelete: false },
|
|
14
|
+
}),
|
|
15
|
+
things: new index_1.RedisStore({
|
|
16
|
+
key: "thing",
|
|
17
|
+
options: {
|
|
18
|
+
...ThingRecordOptions,
|
|
19
|
+
hardDelete: false
|
|
20
|
+
}
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
(async function () {
|
|
24
|
+
// create users
|
|
25
|
+
const users = await Promise.all([
|
|
26
|
+
store.users.create({ name: "User One" }),
|
|
27
|
+
store.users.create({ name: "User Two" }),
|
|
28
|
+
]);
|
|
29
|
+
// Redis commands executed:
|
|
30
|
+
// JSON.SET user:<UUID> $ '{ "id": "<UUID>", "createdAt": <TIMEINMILLIS>, "name": "User One" }'
|
|
31
|
+
// ZADD users <TIMEINMILLIS> <UUID>
|
|
32
|
+
// ...
|
|
33
|
+
console.log("users", users);
|
|
34
|
+
// users (2 entries): [
|
|
35
|
+
// { id: '<UUID>', createdAt: <TIMEINMILLIS>, name: 'User One' },
|
|
36
|
+
// ...
|
|
37
|
+
// ]
|
|
38
|
+
// create things
|
|
39
|
+
const things = await Promise.all([
|
|
40
|
+
store.things.create({
|
|
41
|
+
createdBy: users[0].id,
|
|
42
|
+
label: "A thing for user one",
|
|
43
|
+
}),
|
|
44
|
+
store.things.create({
|
|
45
|
+
createdBy: users[0].id,
|
|
46
|
+
label: "Another thing for user one",
|
|
47
|
+
}),
|
|
48
|
+
store.things.create({
|
|
49
|
+
createdBy: users[0].id,
|
|
50
|
+
label: "Yet another thing for user one",
|
|
51
|
+
}),
|
|
52
|
+
store.things.create({
|
|
53
|
+
createdBy: users[1].id,
|
|
54
|
+
label: "A thing for user two",
|
|
55
|
+
}),
|
|
56
|
+
]);
|
|
57
|
+
// JSON.SET thing:<UUID> $ '{ "id": "<UUID>", "createdAt": <TIMEINMILLIS>, "createdBy": "<USER_UUID>", "message": "Another thing for user one" }'
|
|
58
|
+
// ZADD things <TIMEINMILLIS> <UUID>
|
|
59
|
+
// ZADD things:user:<USER_UUID> <TIMEINMILLIS> <UUID>
|
|
60
|
+
// ...
|
|
61
|
+
console.log("things", things);
|
|
62
|
+
// things (4 entries): [
|
|
63
|
+
// {
|
|
64
|
+
// id: '<UUID>',
|
|
65
|
+
// createdAt: <TIMEINMILLIS>,
|
|
66
|
+
// createdBy: '<USER_UUID>',
|
|
67
|
+
// label: 'A thing for user one'
|
|
68
|
+
// },
|
|
69
|
+
// ...
|
|
70
|
+
// ]
|
|
71
|
+
// all things
|
|
72
|
+
const allThings = await store.things.find();
|
|
73
|
+
// ZRANGE things:user:<USER_UUID> 0 -1 REV
|
|
74
|
+
// JSON.MGET thing:<THING1_UUID> thing:<THING2_UUID> ...
|
|
75
|
+
console.log("allThings", allThings); // 4 entries
|
|
76
|
+
// latest things from first user
|
|
77
|
+
const latestUserThings = await store.things.find({ user: users[0].id });
|
|
78
|
+
// ZRANGE things:user:<USER_UUID> 0 -1 REV
|
|
79
|
+
// JSON.MGET thing:<THING1_UUID> thing:<THING2_UUID> thing:<THING3_UUID>
|
|
80
|
+
console.log("latestUserThings", latestUserThings); // 3 entries
|
|
81
|
+
// cleanup from this session (soft delete by default)
|
|
82
|
+
await Promise.all([
|
|
83
|
+
...users.map((user) => store.users.delete(user.id)),
|
|
84
|
+
...things.map((thing) => store.things.delete(thing.id)),
|
|
85
|
+
]);
|
|
86
|
+
// JSON.SET user:<UUID> $.deletedAt <TIMEINMILLIS>
|
|
87
|
+
// ZREM users <UUID>
|
|
88
|
+
// ...
|
|
89
|
+
// JSON.SET thing:<UUID> $.deletedAt <TIMEINMILLIS>
|
|
90
|
+
// ZREM things <UUID>
|
|
91
|
+
// ZREM testthings:user:<USER_UUID> <UUID>
|
|
92
|
+
// ...
|
|
93
|
+
})();
|
package/dist/index.d.ts
CHANGED
|
@@ -10,23 +10,23 @@ export declare class RedisStore<T extends RedisStoreRecord> {
|
|
|
10
10
|
key: string;
|
|
11
11
|
setKey: string;
|
|
12
12
|
valueKey: (id: string) => string;
|
|
13
|
-
|
|
13
|
+
options: any;
|
|
14
14
|
debug: boolean;
|
|
15
|
-
constructor({ url, token, key, setKey,
|
|
16
|
-
url: string;
|
|
17
|
-
token: string;
|
|
15
|
+
constructor({ url, token, key, setKey, options, debug, }: {
|
|
18
16
|
key: string;
|
|
19
17
|
setKey?: string;
|
|
20
|
-
|
|
18
|
+
options?: any;
|
|
19
|
+
url?: string;
|
|
20
|
+
token?: string;
|
|
21
21
|
debug?: boolean;
|
|
22
22
|
});
|
|
23
23
|
lookupKeys(value: any, options?: any): any[][];
|
|
24
24
|
exists(id: string): Promise<boolean>;
|
|
25
|
-
get(id: string): Promise<T | undefined>;
|
|
25
|
+
get(id: string, options?: any): Promise<T | undefined>;
|
|
26
26
|
scan(query?: any): Promise<Set<string>>;
|
|
27
27
|
ids(query?: any): Promise<Set<string>>;
|
|
28
28
|
find(query?: any): Promise<T[]>;
|
|
29
29
|
create(value: any, options?: any): Promise<T>;
|
|
30
30
|
update(value: any, options?: any): Promise<T>;
|
|
31
|
-
delete(id: string, options?: any): Promise<T>;
|
|
31
|
+
delete(id: string, options?: any): Promise<T | undefined>;
|
|
32
32
|
}
|
package/dist/index.js
CHANGED
|
@@ -24,16 +24,19 @@ const moment_1 = __importDefault(require("moment"));
|
|
|
24
24
|
const utils_1 = require("@desmat/utils");
|
|
25
25
|
const redis_1 = require("@upstash/redis");
|
|
26
26
|
class RedisStore {
|
|
27
|
-
constructor({ url, token, key, setKey,
|
|
28
|
-
this.redis = new redis_1.Redis({
|
|
27
|
+
constructor({ url, token, key, setKey, options, debug, }) {
|
|
28
|
+
this.redis = new redis_1.Redis({
|
|
29
|
+
url: url || process.env.KV_REST_API_URL,
|
|
30
|
+
token: token || process.env.KV_REST_API_TOKEN
|
|
31
|
+
});
|
|
29
32
|
this.key = key;
|
|
30
33
|
this.setKey = setKey || key + "s";
|
|
31
34
|
this.valueKey = (id) => `${key}:${id}`;
|
|
32
|
-
this.
|
|
35
|
+
this.options = options;
|
|
33
36
|
this.debug = !!debug;
|
|
34
37
|
}
|
|
35
38
|
lookupKeys(value, options) {
|
|
36
|
-
options = { ...this.
|
|
39
|
+
options = { ...this.options, ...options };
|
|
37
40
|
this.debug && console.log(`RedisStore.lookupKeys<${this.key}>.lookupKeys`, { value, options });
|
|
38
41
|
/*
|
|
39
42
|
create index and lookup sets based on options.lookups
|
|
@@ -82,19 +85,20 @@ class RedisStore {
|
|
|
82
85
|
this.debug && console.log(`RedisStore<${this.key}>.exists`, { response });
|
|
83
86
|
return response > 0;
|
|
84
87
|
}
|
|
85
|
-
async get(id) {
|
|
88
|
+
async get(id, options) {
|
|
86
89
|
this.debug && console.log(`RedisStore<${this.key}>.get`, { id });
|
|
87
90
|
const response = await this.redis.json.get(this.valueKey(id), "$");
|
|
88
91
|
this.debug && console.log(`RedisStore<${this.key}>.get`, { response });
|
|
89
92
|
let value;
|
|
90
|
-
if (response && response[0] && !response[0].deletedAt) {
|
|
93
|
+
if (response && response[0] && !(response[0].deletedAt && !(options === null || options === void 0 ? void 0 : options.deleted))) {
|
|
91
94
|
value = response[0];
|
|
92
95
|
}
|
|
93
96
|
return value;
|
|
94
97
|
}
|
|
95
98
|
async scan(query = {}) {
|
|
96
99
|
this.debug && console.log(`RedisStore<${this.key}>.scan`, { query });
|
|
97
|
-
|
|
100
|
+
!query.count && console.warn(`RedisStore.RedisStore<${this.key}>.find WARNING: scan command with no count provided: setting count at 999`);
|
|
101
|
+
const count = query.count || 999;
|
|
98
102
|
const match = this.valueKey(query.scan);
|
|
99
103
|
let keys = new Set();
|
|
100
104
|
let nextCursor = "0";
|
|
@@ -170,7 +174,7 @@ class RedisStore {
|
|
|
170
174
|
this.debug && console.log(`RedisStore<${this.key}>.find`, { keys });
|
|
171
175
|
}
|
|
172
176
|
// don't mget too many at once otherwise 💥
|
|
173
|
-
const blockSize =
|
|
177
|
+
const blockSize = 256;
|
|
174
178
|
const blocks = keys && keys.length && Array
|
|
175
179
|
.apply(null, Array(Math.ceil(keys.length / blockSize)))
|
|
176
180
|
.map((v, block) => (keys || [])
|
|
@@ -187,9 +191,9 @@ class RedisStore {
|
|
|
187
191
|
return values;
|
|
188
192
|
}
|
|
189
193
|
async create(value, options) {
|
|
190
|
-
this.debug && console.log(`RedisStore<${this.key}>.create`, { value, options,
|
|
194
|
+
this.debug && console.log(`RedisStore<${this.key}>.create`, { value, options, this_options: this.options });
|
|
191
195
|
const now = (0, moment_1.default)().valueOf();
|
|
192
|
-
options = { ...this.
|
|
196
|
+
options = { ...this.options, ...options };
|
|
193
197
|
const createdValue = {
|
|
194
198
|
id: value.id || (0, utils_1.uuid)(),
|
|
195
199
|
createdAt: value.createdAt || now,
|
|
@@ -217,7 +221,7 @@ class RedisStore {
|
|
|
217
221
|
throw `Cannot update ${this.key}: does not exist: ${value.id}`;
|
|
218
222
|
}
|
|
219
223
|
const now = (0, moment_1.default)().valueOf();
|
|
220
|
-
options = { ...this.
|
|
224
|
+
options = { ...this.options, ...options };
|
|
221
225
|
const updatedValue = {
|
|
222
226
|
...value,
|
|
223
227
|
updatedAt: now,
|
|
@@ -252,23 +256,23 @@ class RedisStore {
|
|
|
252
256
|
if (!id) {
|
|
253
257
|
throw `Cannot delete ${this.key}: null id`;
|
|
254
258
|
}
|
|
255
|
-
options = { ...this.
|
|
256
|
-
const value = await this.get(id);
|
|
259
|
+
options = { ...this.options, ...options };
|
|
260
|
+
const value = await this.get(id, { deleted: true });
|
|
257
261
|
if (!value) {
|
|
258
|
-
|
|
262
|
+
console.warn(`RedisStore<${this.key}>.delete WARNING: does not exist: ${id}`);
|
|
259
263
|
}
|
|
260
|
-
const lookupKeys = this.lookupKeys(value, options);
|
|
264
|
+
const lookupKeys = value && this.lookupKeys(value, options);
|
|
261
265
|
this.debug && console.log(`RedisStore<${this.key}>.delete`, { lookupKeys });
|
|
262
|
-
|
|
266
|
+
const deletedAt = (0, moment_1.default)().valueOf();
|
|
263
267
|
const response = await Promise.all([
|
|
264
268
|
options.hardDelete
|
|
265
269
|
? this.redis.json.del(this.valueKey(id), "$")
|
|
266
|
-
: this.redis.json.set(this.valueKey(id), "
|
|
270
|
+
: this.redis.json.set(this.valueKey(id), "$.deletedAt", deletedAt),
|
|
267
271
|
this.redis.zrem(this.setKey, id),
|
|
268
272
|
...(lookupKeys ? lookupKeys.map((lookupKey) => this.redis.zrem(lookupKey[0], lookupKey[1])) : []),
|
|
269
273
|
]);
|
|
270
274
|
this.debug && console.log(`RedisStore<${this.key}>.delete`, { response });
|
|
271
|
-
return value;
|
|
275
|
+
return value ? { ...value, deletedAt } : undefined;
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
278
|
exports.RedisStore = RedisStore;
|
package/dist/repl.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/repl.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const moment_1 = __importDefault(require("moment"));
|
|
7
|
+
const index_1 = require("./index");
|
|
8
|
+
const KV_REST_API_URL = "https://close-ringtail-23695.upstash.io";
|
|
9
|
+
const KV_REST_API_TOKEN = "AVyPAAIjcDE4ZDhiZjAxNjU3NjI0NGU1YjIwMzAzNzkzMWRkMDU2ZHAxMA";
|
|
10
|
+
const debug = false;
|
|
11
|
+
const UserRecordOptions = {
|
|
12
|
+
lookups: {
|
|
13
|
+
// user: "createdBy",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const PostRecordOptions = {
|
|
17
|
+
lookups: {
|
|
18
|
+
user: "createdBy",
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const UserPostRecordOptions = {
|
|
22
|
+
lookups: {
|
|
23
|
+
user: "userId",
|
|
24
|
+
post: "postId",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const store = {
|
|
28
|
+
users: new index_1.RedisStore({
|
|
29
|
+
url: KV_REST_API_URL,
|
|
30
|
+
token: KV_REST_API_TOKEN,
|
|
31
|
+
key: "testuser",
|
|
32
|
+
setKey: "testusers",
|
|
33
|
+
recordOptions: UserRecordOptions,
|
|
34
|
+
debug,
|
|
35
|
+
}),
|
|
36
|
+
posts: new index_1.RedisStore({
|
|
37
|
+
url: KV_REST_API_URL,
|
|
38
|
+
token: KV_REST_API_TOKEN,
|
|
39
|
+
key: "testpost",
|
|
40
|
+
setKey: "testposts",
|
|
41
|
+
recordOptions: PostRecordOptions,
|
|
42
|
+
debug,
|
|
43
|
+
}),
|
|
44
|
+
userPosts: new index_1.RedisStore({
|
|
45
|
+
url: KV_REST_API_URL,
|
|
46
|
+
token: KV_REST_API_TOKEN,
|
|
47
|
+
key: "testuserpost",
|
|
48
|
+
setKey: "testuserposts",
|
|
49
|
+
recordOptions: UserPostRecordOptions,
|
|
50
|
+
debug,
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
(async function () {
|
|
54
|
+
// console.log("Hello!");
|
|
55
|
+
// new user signs up
|
|
56
|
+
const users = await Promise.all([
|
|
57
|
+
store.users.create({ name: "User One" }),
|
|
58
|
+
store.users.create({ name: "User Two" }),
|
|
59
|
+
store.users.create({ name: "User Three" }),
|
|
60
|
+
]);
|
|
61
|
+
// users creates posts
|
|
62
|
+
const posts = await Promise.all([
|
|
63
|
+
store.posts.create({
|
|
64
|
+
createdBy: users[0].id,
|
|
65
|
+
message: "A post by user one",
|
|
66
|
+
}),
|
|
67
|
+
store.posts.create({
|
|
68
|
+
createdBy: users[0].id,
|
|
69
|
+
message: "Another post by user one",
|
|
70
|
+
}),
|
|
71
|
+
store.posts.create({
|
|
72
|
+
createdBy: users[0].id,
|
|
73
|
+
message: "Yet another post by user one",
|
|
74
|
+
}),
|
|
75
|
+
store.posts.create({
|
|
76
|
+
createdBy: users[1].id,
|
|
77
|
+
message: "A post by user two",
|
|
78
|
+
}),
|
|
79
|
+
]);
|
|
80
|
+
// show latest posts from first user
|
|
81
|
+
const latestPosts = await store.posts.find({ user: users[0].id, count: 3 });
|
|
82
|
+
console.log("=== Latest posts ===");
|
|
83
|
+
latestPosts.forEach((post) => {
|
|
84
|
+
console.log(`[post ${post.id}]: ${post.message}`);
|
|
85
|
+
});
|
|
86
|
+
// users views posts
|
|
87
|
+
const userPosts = await Promise.all([
|
|
88
|
+
store.userPosts.create({
|
|
89
|
+
id: `${users[1].id}:${posts[0].id}`,
|
|
90
|
+
userId: users[1].id,
|
|
91
|
+
postId: posts[0].id,
|
|
92
|
+
viewedAt: (0, moment_1.default)().valueOf(),
|
|
93
|
+
}),
|
|
94
|
+
store.userPosts.create({
|
|
95
|
+
id: `${users[1].id}:${posts[1].id}`,
|
|
96
|
+
userId: users[1].id,
|
|
97
|
+
postId: posts[1].id,
|
|
98
|
+
viewedAt: (0, moment_1.default)().valueOf(),
|
|
99
|
+
}),
|
|
100
|
+
store.userPosts.create({
|
|
101
|
+
id: `${users[2].id}:${posts[1].id}`,
|
|
102
|
+
userId: users[2].id,
|
|
103
|
+
postId: posts[1].id,
|
|
104
|
+
viewedAt: (0, moment_1.default)().valueOf(),
|
|
105
|
+
}),
|
|
106
|
+
]);
|
|
107
|
+
// a user likes a post
|
|
108
|
+
userPosts[2] = await store.userPosts.update({
|
|
109
|
+
...userPosts[2],
|
|
110
|
+
likedAt: (0, moment_1.default)().valueOf(),
|
|
111
|
+
});
|
|
112
|
+
// load all of a user's viewed posts
|
|
113
|
+
console.log("=== A user's viewed posts ===");
|
|
114
|
+
const userViewedPosts = await store.userPosts.find({ user: users[1].id });
|
|
115
|
+
userViewedPosts.forEach((userPost) => {
|
|
116
|
+
console.log(`[user ${userPost.userId}]: ${userPost.id}`);
|
|
117
|
+
});
|
|
118
|
+
// load a post's count of views and likes
|
|
119
|
+
console.log("=== A post's views and likes ===");
|
|
120
|
+
const postViews = await store.userPosts.find({ post: posts[1].id });
|
|
121
|
+
console.log(`[post ${posts[1].id}]: ${postViews.filter((userPost) => userPost.viewedAt).length} view(s) and ${postViews.filter((userPost) => userPost.likedAt).length} like(s)`);
|
|
122
|
+
// cleanup from this session
|
|
123
|
+
// await Promise.all([
|
|
124
|
+
// ...users.map((user: User) => store.users.delete(user.id, { hardDelete: true })),
|
|
125
|
+
// ...posts.map((post: Post) => store.posts.delete(post.id, { hardDelete: true })),
|
|
126
|
+
// ...userPosts.map((userPost: UserPost) => store.userPosts.delete(userPost.id, { hardDelete: true })),
|
|
127
|
+
// ]);
|
|
128
|
+
// cleanup all from previous session
|
|
129
|
+
const [allUsers, allPosts, allUserPosts] = await Promise.all([
|
|
130
|
+
store.users.ids({ scan: "*", count: 999 }),
|
|
131
|
+
store.posts.ids({ scan: "*", count: 999 }),
|
|
132
|
+
store.userPosts.ids({ scan: "*", count: 999 }),
|
|
133
|
+
]);
|
|
134
|
+
await Promise.all([
|
|
135
|
+
...Array.from(allUsers).map((id) => store.users.delete(id, { hardDelete: true })),
|
|
136
|
+
...Array.from(allPosts).map((id) => store.posts.delete(id, { hardDelete: true })),
|
|
137
|
+
...Array.from(allUserPosts).map((id) => store.userPosts.delete(id, { hardDelete: true })),
|
|
138
|
+
]);
|
|
139
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("./index");
|
|
4
|
+
const KV_REST_API_URL = "https://close-ringtail-23695.upstash.io";
|
|
5
|
+
const KV_REST_API_TOKEN = "AVyPAAIjcDE4ZDhiZjAxNjU3NjI0NGU1YjIwMzAzNzkzMWRkMDU2ZHAxMA";
|
|
6
|
+
const debug = false;
|
|
7
|
+
const PostRecordOptions = {
|
|
8
|
+
lookups: {
|
|
9
|
+
user: "createdBy",
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
const store = {
|
|
13
|
+
users: new index_1.RedisStore({
|
|
14
|
+
url: KV_REST_API_URL,
|
|
15
|
+
token: KV_REST_API_TOKEN,
|
|
16
|
+
key: "testuser",
|
|
17
|
+
debug,
|
|
18
|
+
}),
|
|
19
|
+
posts: new index_1.RedisStore({
|
|
20
|
+
url: KV_REST_API_URL,
|
|
21
|
+
token: KV_REST_API_TOKEN,
|
|
22
|
+
key: "testpost",
|
|
23
|
+
// recordOptions: PostRecordOptions,
|
|
24
|
+
debug,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
(async function () {
|
|
28
|
+
// create users
|
|
29
|
+
const users = await Promise.all([
|
|
30
|
+
store.users.create({ name: "User One" }),
|
|
31
|
+
store.users.create({ name: "User Two" }),
|
|
32
|
+
]);
|
|
33
|
+
// JSON.SET user:<UUID> $ '{ "id": "<UUID>", "createdAt": <TIMEINMILLIS>, "name": "User One" }'
|
|
34
|
+
// ZADD users <TIMEINMILLIS> <UUID>
|
|
35
|
+
// ...
|
|
36
|
+
console.log("users", users);
|
|
37
|
+
// users [
|
|
38
|
+
// { id: '<UUID>', createdAt: <TIMEINMILLIS>, name: 'User One' },
|
|
39
|
+
// ...
|
|
40
|
+
// ]
|
|
41
|
+
// create posts
|
|
42
|
+
const posts = await Promise.all([
|
|
43
|
+
store.posts.create({
|
|
44
|
+
createdBy: users[0].id,
|
|
45
|
+
message: "A post by user one",
|
|
46
|
+
}),
|
|
47
|
+
store.posts.create({
|
|
48
|
+
createdBy: users[0].id,
|
|
49
|
+
message: "Another post by user one",
|
|
50
|
+
}),
|
|
51
|
+
store.posts.create({
|
|
52
|
+
createdBy: users[0].id,
|
|
53
|
+
message: "Yet another post by user one",
|
|
54
|
+
}),
|
|
55
|
+
store.posts.create({
|
|
56
|
+
createdBy: users[1].id,
|
|
57
|
+
message: "A post by user two",
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
// JSON.SET post:<UUID> $ '{ "id": "<UUID>", "createdAt": <TIMEINMILLIS>, "createdBy": "<USER_UUID>", "message": "Another post by user one" }'
|
|
61
|
+
// ZADD posts <TIMEINMILLIS> <UUID>
|
|
62
|
+
// ZADD posts:user:<USER_UUID> <TIMEINMILLIS> <POST_UUID>
|
|
63
|
+
// ...
|
|
64
|
+
console.log("posts", posts);
|
|
65
|
+
// posts [
|
|
66
|
+
// {
|
|
67
|
+
// id: '<UUID>',
|
|
68
|
+
// createdAt: <TIMEINMILLIS>,
|
|
69
|
+
// createdBy: '<USER_UUID>',
|
|
70
|
+
// message: 'A post by user one'
|
|
71
|
+
// },
|
|
72
|
+
// ...
|
|
73
|
+
// ]
|
|
74
|
+
// all posts
|
|
75
|
+
const allPosts = await store.posts.find();
|
|
76
|
+
// ZRANGE posts:user:<USER_UUID> 0 -1 REV
|
|
77
|
+
// JSON.MGET post:<POST1_UUID> post:<POST2_UUID> ...
|
|
78
|
+
// latest posts from first user
|
|
79
|
+
const latestUserPosts = await store.posts.find({ user: users[0].id, count: 3 });
|
|
80
|
+
// ZRANGE posts:user:<USER_UUID> 0 2 REV
|
|
81
|
+
// JSON.MGET post:<POST1_UUID> post:<POST2_UUID> post:<POST3_UUID>
|
|
82
|
+
// cleanup from this session (soft delete by default)
|
|
83
|
+
await Promise.all([
|
|
84
|
+
...users.map((user) => store.users.delete(user.id)),
|
|
85
|
+
...posts.map((post) => store.posts.delete(post.id)),
|
|
86
|
+
]);
|
|
87
|
+
// JSON.SET user:<UUID> $.deletedAt <TIMEINMILLIS>
|
|
88
|
+
// ZREM users <UUID>
|
|
89
|
+
// ...
|
|
90
|
+
// JSON.SET post:<POST_UUID> $.deletedAt <TIMEINMILLIS>
|
|
91
|
+
// ZREM posts <POST_UUID>
|
|
92
|
+
// ZREM testposts:user:<USER_UUID> <POST_UUID>
|
|
93
|
+
// ...
|
|
94
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desmat/redis-store",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "tsc"
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"example": "ts-node --skipProject ./src/example.ts",
|
|
17
|
+
"repl": "ts-node --skipProject -i -e 'console.log(\"Exec `.load ./scratchpad.ts` to run load a starter environment; Ctrl-D or Ctrl-C twice to exit.\")'",
|
|
18
|
+
"scratchpad": "ts-node --skipProject ./scratchpad.ts"
|
|
16
19
|
},
|
|
17
20
|
"repository": {
|
|
18
21
|
"type": "git",
|
|
@@ -28,9 +31,11 @@
|
|
|
28
31
|
"license": "MIT",
|
|
29
32
|
"dependencies": {
|
|
30
33
|
"@desmat/utils": "^1.0.0",
|
|
31
|
-
"@upstash/redis": "^1.34.3"
|
|
34
|
+
"@upstash/redis": "^1.34.3",
|
|
35
|
+
"dotenv": "^16.4.5"
|
|
32
36
|
},
|
|
33
37
|
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.8.4",
|
|
34
39
|
"typescript": "^4.0.0"
|
|
35
40
|
}
|
|
36
|
-
}
|
|
41
|
+
}
|