Skip to content

Commit

Permalink
feat: add vercel-kv driver (#213)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
danielroe and pi0 authored May 5, 2023
1 parent ee98e50 commit 622011d
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 6 deletions.
38 changes: 38 additions & 0 deletions docs/content/6.drivers/vercel-kv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Vercel KV

Store data in a [Vercel KV Store](https://vercel.com/docs/storage/vercel-kv).

```js
import { createStorage } from "unstorage";
import vercelKVDriver from "unstorage/drivers/vercel-kv";

const storage = createStorage({
driver: vercelKVDriver({
// url: "https://<your-project-slug>.kv.vercel-storage.com", // KV_REST_API_URL
// token: "<your secret token>", // KV_REST_API_TOKEN
// base: "test",
// env: "KV",
}),
});
```

To use, you will need to install `@vercel/kv` dependency in your project:

```json
{
"dependencies": {
"@vercel/kv": "latest"
}
}
```

**Note:** For driver options type support, you might need to install `@upstash/redis` dev dependency as well.

**Options:**

- `url`: Rest API URL to use for connecting to your Vercel KV store. Default is `KV_REST_API_URL`.
- `token`: Rest API Token to use for connecting to your Vercel KV store. Default is `KV_REST_API_TOKEN`.
- `base`: [optional] Prefix to use for all keys. Can be used for namespacing.
- `env`: [optional] Flag to customzize environment variable prefix (Default is `KV`). Set to `false` to disable env inference for `url` and `token` options.

See [@upstash/redis](https://docs.upstash.com/redis/sdks/javascriptsdk/advanced) for all available options.
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
"@types/jsdom": "^21.1.1",
"@types/mri": "^1.1.1",
"@types/node": "^18.16.3",
"@upstash/redis": "^1.20.5",
"@vercel/kv": "^0.1.1",
"@vitejs/plugin-vue": "^4.2.1",
"@vitest/coverage-c8": "^0.31.0",
"@vue/compiler-sfc": "^3.2.47",
Expand Down Expand Up @@ -97,7 +99,9 @@
"@azure/identity": "^3.1.4",
"@azure/keyvault-secrets": "^4.7.0",
"@azure/storage-blob": "^12.14.0",
"@planetscale/database": "^1.7.0"
"@planetscale/database": "^1.7.0",
"@upstash/redis": "^1.20.5",
"@vercel/kv": "^0.1.1"
},
"peerDependenciesMeta": {
"@azure/app-configuration": {
Expand All @@ -120,7 +124,13 @@
},
"@planetscale/database": {
"optional": true
},
"@upstash/redis": {
"optional": true
},
"@vercel/kv": {
"optional": true
}
},
"packageManager": "pnpm@8.4.0"
}
}
58 changes: 54 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions src/drivers/vercel-kv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { createClient } from "@vercel/kv";
import type { VercelKV } from "@vercel/kv";
import type { RedisConfigNodejs } from "@upstash/redis";

import { defineDriver, normalizeKey, joinKeys } from "./utils";

export interface VercelKVOptions extends Partial<RedisConfigNodejs> {
base?: string;
env?: false | string;
}

export default defineDriver<VercelKVOptions>((opts) => {
const base = normalizeKey(opts?.base);
const r = (...keys: string[]) => joinKeys(base, ...keys);

let _client: VercelKV;
const getClient = () => {
if (!_client) {
const envPrefix =
typeof process !== "undefined" && opts.env !== false
? `${opts.env || "KV"}_`
: "";
if (!opts.url) {
const envName = envPrefix + "REST_API_URL";
if (envPrefix && process.env[envName]) {
opts.url = process.env[envName];
} else {
throw new Error(
`[unstorage] [vercel-kv] missing required 'url' option or '${envName}' env.`
);
}
}
if (!opts.token) {
const envName = envPrefix + "REST_API_TOKEN";
if (envPrefix && process.env[envName]) {
opts.token = process.env[envName];
} else {
throw new Error(
`[unstorage] [vercel-kv] missing required 'token' option or '${envName}' env.`
);
}
}
_client = createClient(opts);
}
return _client;
};

return {
hasItem(key) {
return getClient().exists(r(key)).then(Boolean);
},
getItem(key) {
return getClient().get(r(key));
},
setItem(key, value) {
return getClient()
.set(r(key), value)
.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 @@ -23,6 +23,7 @@ export const builtinDrivers = {
redis: "unstorage/drivers/redis",
azureKeyVault: "unstorage/drivers/azure-key-vault",
sessionStorage: "unstorage/drivers/session-storage",
vercelKV: "unstorage/drivers/vercel-kv",
};

export type BuiltinDriverName = keyof typeof builtinDrivers;

0 comments on commit 622011d

Please sign in to comment.