{"id":15083842,"url":"https://github.com/mystpi/dinostore","last_synced_at":"2025-04-10T22:54:35.035Z","repository":{"id":255436278,"uuid":"849974512","full_name":"MystPi/dinostore","owner":"MystPi","description":"🦕 Gleam bindings to the Deno KV database","archived":false,"fork":false,"pushed_at":"2024-08-30T16:48:24.000Z","size":12,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T22:54:29.913Z","etag":null,"topics":["bindings","database","deno","deno-kv","gleam","javascript"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/dinostore","language":"Gleam","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MystPi.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-08-30T16:14:03.000Z","updated_at":"2024-12-01T18:04:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"8cbc45fa-6c1d-43a4-92a4-4d6d24d16bfc","html_url":"https://github.com/MystPi/dinostore","commit_stats":{"total_commits":1,"total_committers":1,"mean_commits":1.0,"dds":0.0,"last_synced_commit":"5230c140bcb4f5a31fd154213d1201df95040055"},"previous_names":["mystpi/dinostore"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MystPi%2Fdinostore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MystPi%2Fdinostore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MystPi%2Fdinostore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MystPi%2Fdinostore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MystPi","download_url":"https://codeload.github.com/MystPi/dinostore/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248312172,"owners_count":21082638,"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":["bindings","database","deno","deno-kv","gleam","javascript"],"created_at":"2024-09-25T06:33:49.633Z","updated_at":"2025-04-10T22:54:35.004Z","avatar_url":"https://github.com/MystPi.png","language":"Gleam","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dinostore\n\nBindings to the [`Deno.Kv`](https://docs.deno.com/deploy/kv/manual/) database.\n\n[![Package Version](https://img.shields.io/hexpm/v/dinostore)](https://hex.pm/packages/dinostore)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/dinostore/)\n\n## Setup\n\n```sh\ngleam add dinostore\n\n# You will also need gleam_javascript if not already installed\ngleam add gleam_javascript\n```\n\nYour `gleam.toml` config will need the JavaScript runtime set to `deno`.\n\n```toml\ntarget = \"javascript\"\n\n[javascript]\nruntime = \"deno\"\n```\n\nMake sure that your `deno.json` also contains the following configuration:\n\n```json\n{\n  \"unstable\": [\"kv\"]\n}\n```\n\n## A note on serialization\n\nDeno KV uses the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to store values. This means that Gleam values such as custom types will be converted to JavaScript objects before being stored in the database. For example, consider the following custom type:\n\n```gleam\ntype Book {\n  Book(title: String, author: String)\n}\n```\n\nA constructed value such as `Book(title: \"foo\", author: \"bar\")` will be stored in the database as a JavaScript object when cloned:\n\n```json\n{ \"title\": \"foo\", \"author\": \"bar\" }\n```\n\n`Dynamic` decoders should be carefully written with this in mind. It may be helpful to inspect the value retrieved from the database before decoding it to see how it has changed.\n\nFor convenience, `List`s are automatically converted to JavaScript arrays before being stored in the database.\n\n## Example\n\nIn the following program, several `let assert`s are used when decoding `Dynamic` data. When creating wrapper functions for the database, you can be certain that the retrieved data is correctly structured, provided that these wrapper functions are consistently used instead of directly accessing the raw database. Ultimately, it's up to you to decide how to handle the case where parsing data fails.\n\n```gleam\nimport dinostore/atomic\nimport dinostore/key\nimport dinostore/kv\nimport gleam/dynamic\nimport gleam/io\nimport gleam/javascript/promise.{type Promise}\nimport gleam/option.{None}\n\ntype Cat =\n  #(String, Int)\n\nfn decode_cat(x: dynamic.Dynamic) {\n  dynamic.tuple2(dynamic.string, dynamic.int)(x)\n}\n\nfn add_cat(conn: kv.Connection, cat: Cat) -\u003e Promise(Result(String, Nil)) {\n  let primary_key = [key.s(\"cats\"), key.ulid()]\n  let by_name = [key.s(\"cats_by_name\"), key.s(cat.0)]\n\n  atomic.new(conn)\n  // Check that the key does not already exist\n  |\u003e atomic.check(by_name, None)\n  // Create the primary and secondary keys\n  |\u003e atomic.set(primary_key, cat, expiration: None)\n  |\u003e atomic.set(by_name, key.unwrap(primary_key), expiration: None)\n  // Attempt to commit the mutations\n  |\u003e atomic.commit\n}\n\nfn get_cat_by_name(\n  conn: kv.Connection,\n  name: String,\n) -\u003e Promise(Result(Cat, kv.GetError)) {\n  let secondary_key = [key.s(\"cats_by_name\"), key.s(name)]\n\n  use primary_key_entry \u003c- promise.try_await(kv.get(conn, secondary_key))\n  let assert Ok(primary_key) = key.decode(primary_key_entry.value)\n\n  use entry \u003c- promise.try_await(kv.get(conn, primary_key))\n  let assert Ok(cat) = decode_cat(entry.value)\n\n  promise.resolve(Ok(cat))\n}\n\npub fn main() {\n  use conn \u003c- promise.await(kv.open_with_path(\":memory:\"))\n\n  // Add two cats to the database. Note that here we are ignoring add_cat's\n  // results; in a more sophisticated program you would want to handle error\n  // cases appropriately.\n  use _ \u003c- promise.await(add_cat(conn, #(\"Fluffy\", 2)))\n  use _ \u003c- promise.await(add_cat(conn, #(\"Oreo\", 10)))\n\n  // Retrieve a cat by its name\n  use cat \u003c- promise.await(get_cat_by_name(conn, \"Oreo\"))\n\n  let _ = io.debug(cat)\n  //=\u003e #(\"Oreo\", 10)\n\n  promise.resolve(Nil)\n}\n```\n\nA more involved example can be found in [./test](https://github.com/MystPi/dinostore/tree/main/test).\n\n## Documentation\n\nFurther documentation can be found at \u003chttps://hexdocs.pm/dinostore\u003e.\n\n## Development\n\n```sh\ngleam run   # Run the project\ngleam test  # Run the test project\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmystpi%2Fdinostore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmystpi%2Fdinostore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmystpi%2Fdinostore/lists"}