{"id":21558649,"url":"https://github.com/ghostzero/kv","last_synced_at":"2025-06-12T03:09:25.144Z","repository":{"id":228257001,"uuid":"773506775","full_name":"ghostzero/kv","owner":"ghostzero","description":"Simple Key-Value Client","archived":false,"fork":false,"pushed_at":"2024-10-03T12:05:28.000Z","size":43,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-18T03:43:43.112Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://jsr.io/@gz/kv","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ghostzero.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-17T20:55:55.000Z","updated_at":"2024-10-04T20:00:46.000Z","dependencies_parsed_at":"2024-03-17T22:26:38.680Z","dependency_job_id":"3aa46b33-65ff-4c08-a1a4-33ec91dff5f2","html_url":"https://github.com/ghostzero/kv","commit_stats":null,"previous_names":["ghostzero/kv"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ghostzero/kv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostzero%2Fkv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostzero%2Fkv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostzero%2Fkv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostzero%2Fkv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghostzero","download_url":"https://codeload.github.com/ghostzero/kv/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostzero%2Fkv/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259388078,"owners_count":22849754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-24T08:15:29.031Z","updated_at":"2025-06-12T03:09:25.102Z","avatar_url":"https://github.com/ghostzero.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Key-Value Client\n\n## Installation\n\nTo install the key-value store client, run the following command:\n\n```bash\n# deno\ndeno add jsr:@gz/kv\n\n# npm (use any of npx, yarn dlx, pnpm dlx, or bunx)\nnpx jsr add @gz/kv\n```\n\n## Opening the Key-Value Store\n\nTo open the key-value store, run the following command:\n\n```ts\nimport { connect } from \"@gz/kv\";\n\nconst kv = await connect({\n    bucket: '9d1cb4c7-c683-4fa9-bc5f-13f5ad1ba745',\n    accessToken: '9b9634a1-1655-4baf-bdf5-c04feffc68bd',\n    region: 'eu-central-1'\n});\n```\n\n### Environment Variables\n\nYou can also use environment variables to configure the key-value store. The key-value store client will automatically\nuse the environment variables if they are set.\n\n- `KV_ACCESS_TOKEN` - The access token for the key-value store.\n- `KV_ENDPOINT` - The endpoint for the key-value store.\n- `KV_BUCKET` - The bucket for the key-value store.\n- `KV_REGION` - The region for the key-value store.\n- `KV_ENCRYPTION_KEY` - The encryption key for the key-value store.\n\nIf you want to ignore your environment variables, you can pass the `ignoreEnv` option to the `connect()` function. This\nwill prevent the key-value store client from using these environment variables.\n\n## Creating a User interface\n\nSince we're going to use TypeScript, we can create an interface for our User object. So it's easier to work with.\nHere we define a User interface with a name and an email for example.\n\n```typescript\ninterface User {\n    id: string;\n    name: string;\n    email: string;\n}\n```\n\n## Creating, updating, and reading a key-value pair\n\nNow, we can create our first key-value pair. We use the `set()` method to create a new key-value pair. The key is an\narray of strings and the value is the value you want to store. Internally, the key-array is joined with a separator to\ncreate a unique key.\n\n```typescript\nconst key = ['users', '1'];\nconst value: User = {id: '1', name: 'GhostZero', email: 'example@example.com'};\nawait kv.set(key, value);\n```\n\nOnce the key-value pair is created, you can read it back using the `get()` method. The `get()` method returns an object\nwith the `key`, `value`, and `version`.\n\n```typescript\nconst key = ['users', '1'];\nconst entry = await kv.get\u003cUser\u003e(key);\n\nconsole.log(entry.key);\nconsole.log(entry.value);\nconsole.log(entry.version);\n```\n\n## Deleting a key-value pair\n\nYou can delete a key-value pair using the `delete()` method. The `delete()` method returns a boolean indicating if the\nkey-value pair was deleted.\n\n```typescript\nconst key = ['users', '1'];\nawait kv.delete(key);\n```\n\n## Atomic transactions\n\nThe Key-Value Store supports atomic transactions. This means that you can perform multiple operations in a single\ntransaction. If any of the operations fail, the entire transaction is rolled back.\n\n```typescript\nconst key = ['users', '1'];\nconst value: User = {id: '1', name: 'GhostZero', email: 'example@example.com'};\n\nconst res = await kv.atomic()\n    .check({key, version: null /* or a version */})\n    .set(key, value)\n    .commit();\n\nif (res.ok) {\n    console.log('Entry did not exist and was created');\n} else {\n    console.log('Entry already exist. No changes were made');\n}\n```\n\n## Improve querying with secondary indexes\n\nWith the Key-Value Store, you can only query by the key. If you want to query by a different field, you can create a\nsecondary index. A secondary index is a key-value pair where the key is the field you want to query by and the value is\nthe primary key.\n\n```typescript\nasync function saveUser(user: User) {\n    const key = ['users', user.id]\n\n    // set the primary key\n    const r = await kv.set(key, user)\n\n    // set the secondary key's value to be the primary key\n    await kv.set(['users_by_email', user.email], key)\n\n    return r\n}\n\nasync function getById(id) {\n    // use as usual\n    return await kv.get\u003cUser\u003e(['users', id])\n}\n\nasync function getByEmail(email) {\n    // lookup the primary key by the secondary key\n    const r1 = await kv.get\u003carray[]\u003e(['users_by_email', email])\n    const r2 = await kv.get\u003cUser\u003e(r1.value)\n    return r2\n}\n```\n\n## Client-side Encryption\n\nThe Key-Value Store supports client-side encryption. This means that the data is encrypted before it is sent to the\nserver. The server only sees the encrypted data and cannot decrypt it. The encryption key is stored on the client-side\nand is never sent to the server.\n\n### Generating a key\n\nTo generate a new encryption key, you can use the `generateCryptoKey()` function. The `generateCryptoKey()` function\nreturns a new encryption key. You can then export the key using the `exportCryptoKey()` function.\n\nThe exported key is a Base64-encoded string that you can store in a secure location and import later.\n\n\u003e [!CAUTION]\n\u003e Make sure to store the encryption key in a secure location. If you lose the encryption key, you will not be able to\n\u003e decrypt the encrypted data. The Key-Value Store does not store the encryption key!\n\n```typescript\nimport { generateCryptoKey, exportCryptoKey } from \"@gz/kv\";\n\n// generate a new encryption key\nconst cryptoKey = await generateCryptoKey();\nconst exportedKey = await exportCryptoKey(cryptoKey);\n\n// you can store the exported key in a secure location\nconsole.log(exportedKey);\n```\n\n### Using the key\n\nTo use the encryption key, you need to use the `KeyManager` class. The `KeyManager` class manages the encryption keys\nand allows you to import multiple keys (which can be useful for key rotation).\n\n\u003e [!IMPORTANT]\n\u003e Client-side encryption can be enabled anytime by passing the `KeyManager` to the `connect()` function. If you\n\u003e disable client-side encryption, new data will not be encrypted, but existing data will remain encrypted.\n\n\u003e [!IMPORTANT]\n\u003e The KeyManager utilizes only active keys for encryption. You can add multiple keys to the KeyManager, but only one key\n\u003e can be designated as active at a time. The active key is defined as the most recently added key with the active flag\n\u003e set to true. If there is no active key, the KeyManager will refrain from encrypting data and will use the keys solely\n\u003e for decryption.\n\n```typescript\nimport { connect, KeyManager } from \"@gz/kv\";\n\n// example 1: import the key from environment variables\nconst keyManager = await new KeyManager().fromEnv();\n\n// example 2: import the key from a Base64-encoded string\nconst keyManager = new KeyManager();\nawait keyManager.addKey(exportedKey, true);\n\n// connect to the key-value store with the encryption key\nconst kv = await connect({\n    bucket: '9d1cb4c7-c683-4fa9-bc5f-13f5ad1ba745',\n    accessToken: '9b9634a1-1655-4baf-bdf5-c04feffc68bd',\n    region: 'eu-central-1',\n    keyManager\n});\n```\n\n### Manage what data is encrypted\n\nBy default, all data is encrypted. If you want to disable encryption for a specific key-value pair, you can use the\n`addOnlyKvKeys([...])` or `addExceptKvKeys([...])` methods on the `KeyManager` class.\n\n\u003e [!IMPORTANT]\n\u003e When using `*` as a key, it acts as a wildcard and matches all keys after it. For example, `['users', '*']` will match\n\u003e all keys that start with `users`.\n\n**Example: Encrypt only the `users` key**\n\n```typescript\nconst keyManager = new KeyManager();\n\n// only encrypt the 'users' key\nkeyManager.addOnlyKvKeys([['users', '*']]);\n```\n\n**Example: Encrypt all keys except the `users` key**\n\n```typescript\nconst keyManager = new KeyManager();\n\n// encrypt all keys except the 'users' key\nkeyManager.addExceptKvKeys([['users', '*']]);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghostzero%2Fkv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghostzero%2Fkv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghostzero%2Fkv/lists"}