Skip to content

Commit

Permalink
Merge pull request #29 from denoland/kv_content
Browse files Browse the repository at this point in the history
quick start changes
  • Loading branch information
kwhinnery authored Sep 4, 2023
2 parents 1627806 + bca8954 commit 113e07d
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 142 deletions.
297 changes: 183 additions & 114 deletions kv/manual/index.md
Original file line number Diff line number Diff line change
@@ -1,165 +1,234 @@
# About Deno KV
# Deno KV Quick Start

Since version 1.32, Deno has a built in key-value store that durably persists
data on disk, allowing for data storage and access across service and system
restarts.
**Deno KV** is a
[key-value database](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)
built directly into the Deno runtime, available in the
[`Deno.Kv` namespace](https://deno.land/api?unstable&s=Deno.Kv). It can be used
for many kinds of data storage use cases, but excels at storing simple data
structures that benefit from very fast reads and writes. Deno KV is available in
the Deno CLI and on [Deno Deploy](./on_deploy.md).

The key-value store is designed to be fast and easy to use. Keys are sequences
(arrays) of JavaScript types like `string`, `number`, `bigint`, `boolean`, and
`Uint8Array`. Values are arbitrary JavaScript primitives, objects, and arrays.
:::caution Deno KV is currently in beta

The store supports seven different operations that can be composed together to
support many use-cases and enable persistence for most common patterns in modern
web applications. Atomic operations are available that allow grouping of any
number of modification operations into a single atomic transaction.
Deno KV is currently **experimental** and **subject to change**. While we do our
best to ensure data durability, data loss is possible, especially around Deno
updates.

All data in the KV store is versioned, which allows atomic operations to be
conditional on versions in storage matching the value that user code expected.
This enables optimistic locking, enabling virtual asynchronous transactions.
Excuting Deno programs that use KV currently requires the `--unstable` flag, as
below:

All writes to the KV store are strongly consistent and immediately durably
persisted. Reads are strongly consistent by default, but alternative consistency
modes are available to enable different performance tradeoffs.
```sh
deno run -A --unstable my_kv_code.ts
```

> ⚠️ Deno KV is currently **experimental** and **subject to change**. While we do
> our best to ensure data durability, data loss is possible, especially around
> Deno updates. We recommend that you backup your data regularly and consider
> storing data in a secondary store for the time being.
:::

## Getting started
Let's walk through the key features of Deno KV.

> ⚠️ Because Deno KV is currently **experimental** and **subject to change**, it
> is only available when running with `--unstable` flag in Deno CLI.
## Opening a database

All operations on the key-value store are performed via the `Deno.Kv` API.
In your Deno program, you can get a reference to a KV database using
[`Deno.openKv()`](https://deno.land/api?unstable=&s=Deno.openKv). You may pass
in an optional file system path to where you'd like to store your database,
otherwise one will be created for you based on the current working directory of
your script.

A database can be opened using the `Deno.openKv()` function. This function
optionally takes a database path on disk as the first argument. If no path is
specified, the database is persisted in a global directory, bound to the script
that `Deno.openKv()` was called from. Future invocations of the same script will
use the same database.
```ts
const kv = await Deno.openKv();
```

Operations can be called on the `Deno.Kv`. The three primary operations on the
database are `get`, `set`, and `delete`. These allow reading, writing, and
deleting individual keys.
## Creating, updating, and reading a key-value pair

```tsx
// Open the default database for the script.
Data in Deno KV is stored as key-value pairs, much like properties of a
JavaScript object literal or a
[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map).
[Keys](./key_space.md) are represented as an array of JavaScript types, like
`string`, `number`, `bigint`, or `boolean`. Values can be arbitrary JavaScript
objects. In this example, we create a key-value pair representing a user's UI
preferences, and save it with
[`kv.set()`](https://deno.land/api?s=Deno.Kv&unstable=&p=prototype.set).

```ts
const kv = await Deno.openKv();

// Persist an object at the users/alice key.
await kv.set(["users", "alice"], { name: "Alice" });
const prefs = {
username: "ada",
theme: "dark",
language: "en-US",
};

// Read back this key.
const res = await kv.get(["users", "alice"]);
console.log(res.key); // [ "users", "alice" ]
console.log(res.value); // { name: "Alice" }
const result = await kv.set(["preferences", "ada"], prefs);
```

// Delete the key.
await kv.delete(["users", "alice"]);
Once a key-value pair is set, you can read it from the database with
[`kv.get()`](https://deno.land/api?s=Deno.Kv&unstable=&p=prototype.get):

// Reading back the key now returns null.
const res2 = await kv.get(["users", "alice"]);
console.log(res2.key); // [ "users", "alice" ]
console.log(res2.value); // null
```ts
const entry = await kv.get(["preferences", "ada"]);
console.log(savedPrefs.key);
console.log(savedPrefs.value);
console.log(savedPrefs.versionstamp);
```

The `list` operation can be used to list out all keys matching a specific
selector. In the below example all keys starting with some prefix are selected.
Both `get` and `list` [operations](./operations.md) return a
[KvEntry](https://deno.land/api?s=Deno.KvEntry&unstable=) object with the
following properties:

- `key` - the array key you used to set the value
- `value` - the JavaScript object you set for this key
- `versionstamp` - a generated value used to determine if a key has been
updated.

The `set` operation is also used to update objects that already exist for a
given key. When a key's value is updated, its `versionstamp` will change to a
new generated value.

## Listing several key-value pairs

To get values for a finite number of keys, you may use
[`kv.getMany()`](https://deno.land/api?s=Deno.Kv&unstable=&p=prototype.getMany).
Pass in several keys as arguments, and you'll receive an array of values for
each key. Note that **values and versionstamps can be `null`** if no value
exists for the given key(s).

```tsx,ignore
await kv.set(["users", "alice"], { birthday: "January 1, 1990" });
await kv.set(["users", "sam"], { birthday: "February 14, 1985" });
await kv.set(["users", "taylor"], { birthday: "December 25, 1970" });
```ts
const kv = await Deno.openKv();
const result = await db.getMany([
["preferences", "ada"],
["preferences", "grace"],
]);
result[0].key; // ["preferences", "ada"]
result[0].value; // { ... }
result[0].versionstamp; // "00000000000000010000"
result[1].key; // ["preferences", "grace"]
result[1].value; // null
result[1].versionstamp; // null
```

Often, it is useful to retrieve a list of key-value pairs from all keys that
share a given prefix. This type of operation is possible using
[`kv.list()`](https://deno.land/api?s=Deno.Kv&unstable=&p=prototype.list). In
this example, we get a list of key-value pairs that share the `"preferences"`
prefix.

// List out all entries with keys starting with `["users"]`
for await (const entry of kv.list({ prefix: ["users"] })) {
console.log(entry.key);
console.log(entry.value);
```ts
const kv = await Deno.openKv();
const entries = db.list({ prefix: ["preferences"] });
for await (const entry of entries) {
console.log(entry.key); // ["preferences", "ada"]
console.log(entry.value); // { ... }
console.log(entry.versionstamp); // "00000000000000010000"
}
```

> Note: in addition to prefix selectors, range selectors, and constrained prefix
> selectors are also available.
Returned keys are ordered lexicographically based on the next component of the
key after the prefix. So KV pairs with these keys:

- `["preferences", "ada"]`
- `["preferences", "bob"]`
- `["preferences", "cassie"]`

In addition to individual `get`, `set`, and `delete` operations, the key-value
store supports `atomic` operations that allow multiple modifications to take
place at once, optionally conditional on the existing data in the store.
Will be returned in that order by `kv.list()`.

In the below example, we insert a new user only if it does not yet exist by
performing an atomic operation that has a check that there is no existing value
for the given key:
Read operations can either be performed in
[**strong or eventual consistency mode**](./operations.md). Strong consistency
mode guarantees that the read operation will return the most recently written
value. Eventual consistency mode may return a stale value, but is faster. By
contrast, writes are always performed in strong consistency mode.

## Deleting key-value pairs

You can delete a key from the database using
[`kv.delete()`](https://deno.land/api?s=Deno.Kv&unstable=&p=prototype.delete).
No action is taken if no value is found for the given key.

```ts
const kv = await Deno.openKv();
await db.delete(["preferences", "alan"]);
```

## Atomic transactions

Deno KV is capable of executing [atomic transactions](./transactions.md), which
enables you to conditionally execute one or many data manipulation operations at
once. In the following example, we create a new preferences object only if it
hasn't been created already.

```ts
const kv = await Deno.openKv();

const key = ["preferences", "alan"];
const value = {
username: "alan",
theme: "light",
language: "en-GB",
};

```tsx,ignore
const key = ["users", "alice"];
const value = { birthday: "January 1, 1990" };
const res = await kv.atomic()
.check({ key, versionstamp: null }) // `null` versionstamps mean 'no value'
.set(key, value)
.commit();
if (res.ok) {
console.log("User did not yet exist. Inserted!");
console.log("Preferences did not yet exist. Inserted!");
} else {
console.log("User already exists.");
console.error("Preferences already exist.");
}
```

## Reference

- [API Reference](https://deno.land/api?unstable&s=Deno.Kv)
- [Key Space](./key_space.md)
- [Operations](./operations.md)

## Patterns

- [Transactions](./transactions.md)
- [Secondary Indexes](./secondary_indexes.md)
Learn more about transactions in Deno KV [here](./transactions.md).

<!--
## Improve querying with secondary indexes

## Patterns
- Real-time data (TODO)
- Counters (TODO)
[Secondary indexes](./secondary_indexes.md) store the same data by multiple
keys, allowing for simpler quries of the data you need. Let's say that we need
to be able to access user preferences by both username AND email. To enable
this, you could provide a function that wraps the logic to save the preferences
to create two indexes.

### Pagination
- How to use cursors
- Size limits on cursors
### Real-time data
- BroadcastChannel
- Versionstamp can be compared for staleness
```ts
const kv = await Deno.openKv();

### Counters
async function savePreferences(prefs) {
const key = ["preferences", prefs.username];

- Using KvU64 to count things
// Set the primary key
const r = await kv.set(key, prefs);

## Reference
// Set the secondary key's value to be the primary key
await kv.set(["preferencesByEmail", prefs.email], key);

### Consistency
return r;
}

- What is eventual vs strong consistency
- When to use which
async function getByUsername(username) {
// Use as before...
const r = await kv.get(["preferences", username]);
return r;
}

### Limits
async function getByEmail(email) {
// Look up the key by email, then second lookup for actual data
const r1 = await kv.get(["preferencesByEmail", email]);
const r2 = await kv.get(r1.value);
return r2;
}
```

- Key size limits
- Value size limits
- Transaction size limits
- Throughput limits
- Database size limits on Deploy
Learn more about [secondary indexes in the manual here](./secondary_indexes.md).

### Performance
## Production usage

- Latency (consistency)
- Max throughput per key
Deno KV is available for use in live applications on
[Deno Deploy](./on_deploy.md). In production, Deno KV is backed by
[FoundationDB](https://www.foundationdb.org/), the open source key-value store
created by Apple.

### Backups
**No additional configuration is necessary** to run your Deno programs that use
KV on Deploy - a new Deploy database will be provisioned for you when required
by your code. Learn more about Deno KV on Deno Deploy [here](./on_deploy.md).

- For CLI:
- uses sqlite
- where is this stored
- how to restore
## Next steps

-->
At this point, you're just beginning to scratch the surface with Deno KV. Be
sure to check out our guide on the [Deno KV key space](./key_space.md), and a
collection of [tutorials and example applications](../tutorials/index.md) here.
18 changes: 14 additions & 4 deletions kv/manual/key_space.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# Key Space

> ⚠️ Deno KV is currently **experimental** and **subject to change**. While we do
> our best to ensure data durability, data loss is possible, especially around
> Deno updates. We recommend that you backup your data regularly and consider
> storing data in a secondary store for the time being.
:::caution Deno KV is currently in beta

Deno KV is currently **experimental** and **subject to change**. While we do our
best to ensure data durability, data loss is possible, especially around Deno
updates.

Excuting Deno programs that use KV currently requires the `--unstable` flag, as
below:

```sh
deno run -A --unstable my_kv_code.ts
```

:::

Deno KV is a key value store. The key space is a flat namespace of
key+value+versionstamp pairs. Keys are sequences of key parts, which allow
Expand Down
15 changes: 15 additions & 0 deletions kv/manual/on_deploy.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# KV on Deno Deploy

:::caution Deno KV is currently in beta

Deno KV is currently **experimental** and **subject to change**. While we do our
best to ensure data durability, data loss is possible, especially around Deno
updates.

Excuting Deno programs that use KV currently requires the `--unstable` flag, as
below:

```sh
deno run -A --unstable my_kv_code.ts
```

:::

Deno Deploy now offers a built-in serverless key-value database called Deno KV,
which is currently in closed beta testing. To join the waitlist for this
exclusive beta, please [sign up here.](https://dash.deno.com/kv). While in
Expand Down
Loading

0 comments on commit 113e07d

Please sign in to comment.