Skip to content

Commit

Permalink
feat: add upstash driver (#500)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Kane <m@mk.gg>
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
3 people authored Dec 6, 2024
1 parent 88135c1 commit 6caa7d6
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_UPSTASH_REDIS_REST_URL=
VITE_UPSTASH_REDIS_REST_TOKEN=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ __*
.vercel
.netlify
test/fs-storage/**
.env
49 changes: 49 additions & 0 deletions docs/2.drivers/upstash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
icon: simple-icons:upstash
---

# Upstash

> Store data in an Upstash Redis database.
## Usage

::read-more{to="https://upstash.com/"}
Learn more about Upstash.
::

::note
Unstorage uses [`@upstash/redis`](https://github.com/upstash/upstash-redis) internally to connect to Upstash Redis.
::

To use it, you will need to install `@upstash/redis` in your project:

:pm-install{name="@upstash/redis"}

Usage with Upstash Redis:

```js
import { createStorage } from "unstorage";
import upstashDriver from "unstorage/drivers/upstash";

const storage = createStorage({
driver: upstashDriver({
base: "unstorage",
// url: "", // or set UPSTASH_REDIS_REST_URL env
// token: "", // or set UPSTASH_REDIS_REST_TOKEN env
}),
});
```

**Options:**

- `base`: Optional prefix to use for all keys. Can be used for namespacing.
- `url`: The REST URL for your Upstash Redis database. Find it in [the Upstash Redis console](https://console.upstash.com/redis/). Driver uses `UPSTASH_REDIS_REST_URL` environment by default.
- `token`: The REST token for authentication with your Upstash Redis database. Find it in [the Upstash Redis console](https://console.upstash.com/redis/). Driver uses `UPSTASH_REDIS_REST_TOKEN` environment by default.
- `ttl`: Default TTL for all items in **seconds**.

See [@upstash/redis documentation](https://upstash.com/docs/redis/sdks/ts/overview) for all available options.

**Transaction options:**

- `ttl`: Supported for `setItem(key, value, { ttl: number /* seconds */ })`
69 changes: 69 additions & 0 deletions src/drivers/upstash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { type RedisConfigNodejs, Redis } from "@upstash/redis";
import { defineDriver, normalizeKey, joinKeys } from "./utils";

export interface UpstashOptions extends Partial<RedisConfigNodejs> {
/**
* Optional prefix to use for all keys. Can be used for namespacing.
*/
base?: string;

/**
* Default TTL for all items in seconds.
*/
ttl?: number;
}

const DRIVER_NAME = "upstash";

export default defineDriver<UpstashOptions, Redis>(
(options: UpstashOptions = {}) => {
const base = normalizeKey(options?.base);
const r = (...keys: string[]) => joinKeys(base, ...keys);

let redisClient: Redis;
const getClient = () => {
if (redisClient) {
return redisClient;
}
const url =
options.url || globalThis.process?.env?.UPSTASH_REDIS_REST_URL;
const token =
options.token || globalThis.process?.env?.UPSTASH_REDIS_REST_TOKEN;
redisClient = new Redis({ url, token, ...options });
return redisClient;
};
return {
name: DRIVER_NAME,
getInstance: getClient,
hasItem(key) {
return getClient().exists(r(key)).then(Boolean);
},
getItem(key) {
return getClient().get(r(key));
},
setItem(key, value, tOptions) {
const ttl = tOptions?.ttl || options.ttl;
return getClient()
.set(r(key), value, ttl ? { ex: ttl } : undefined)
.then(() => {});
},
removeItem(key) {
return getClient()
.del(r(key))
.then(() => {});
},
getKeys(base) {
return getClient().keys(r(base, "*"));
},
async clear(base) {
const keys = await getClient().keys(r(base, "*"));
if (keys.length === 0) {
return;
}
return getClient()
.del(...keys)
.then(() => {});
},
};
}
);
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const builtinDrivers = {
planetscale: "unstorage/drivers/planetscale",
redis: "unstorage/drivers/redis",
sessionStorage: "unstorage/drivers/session-storage",
upstash: "unstorage/drivers/upstash",
vercelKV: "unstorage/drivers/vercel-kv",

/** @deprecated */
Expand Down
14 changes: 14 additions & 0 deletions test/drivers/upstash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe } from "vitest";
import { testDriver } from "./utils";
import driver from "../../src/drivers/upstash";

const url = process.env.VITE_UPSTASH_REDIS_REST_URL;
const token = process.env.VITE_UPSTASH_REDIS_REST_TOKEN;

describe.skipIf(!url || !token)("drivers: upstash", async () => {
process.env.UPSTASH_REDIS_REST_URL = url;
process.env.UPSTASH_REDIS_REST_TOKEN = token;
testDriver({
driver: driver({}),
});
});

0 comments on commit 6caa7d6

Please sign in to comment.