├── .gitignore ├── .dockerignore ├── docs ├── deno-dash.png └── fly-dash.png ├── deno.jsonc ├── .vscode └── settings.json ├── fly.toml ├── Dockerfile ├── LICENSE ├── server.tsx ├── README.md └── deno.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | fly.toml 2 | -------------------------------------------------------------------------------- /docs/deno-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/deno-flyio/main/docs/deno-dash.png -------------------------------------------------------------------------------- /docs/fly-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/deno-flyio/main/docs/fly-dash.png -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "start": "deno run -A --unstable --watch server.tsx" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true, 5 | "editor.defaultFormatter": "denoland.vscode-deno", 6 | "editor.formatOnSave": true 7 | } -------------------------------------------------------------------------------- /fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml app configuration file generated for deno-flyio on 2023-09-13T11:55:56-05:00 2 | # 3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 | # 5 | 6 | app = "deno-flyio3" 7 | primary_region = "dfw" 8 | 9 | [build] 10 | 11 | [env] 12 | PORT = "8000" 13 | 14 | [processes] 15 | app = "run -A --unstable ./server.tsx" 16 | 17 | [http_service] 18 | internal_port = 8000 19 | force_https = true 20 | auto_stop_machines = true 21 | auto_start_machines = true 22 | min_machines_running = 0 23 | processes = ["app"] 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/denoland/deno_docker/blob/main/alpine.dockerfile 2 | 3 | ARG DENO_VERSION=1.36.4 4 | ARG BIN_IMAGE=denoland/deno:bin-${DENO_VERSION} 5 | FROM ${BIN_IMAGE} AS bin 6 | 7 | FROM frolvlad/alpine-glibc:alpine-3.13 8 | 9 | RUN apk --no-cache add ca-certificates 10 | 11 | RUN addgroup --gid 1000 deno \ 12 | && adduser --uid 1000 --disabled-password deno --ingroup deno \ 13 | && mkdir /deno-dir/ \ 14 | && chown deno:deno /deno-dir/ 15 | 16 | ENV DENO_DIR /deno-dir/ 17 | ENV DENO_INSTALL_ROOT /usr/local 18 | 19 | ARG DENO_VERSION 20 | ENV DENO_VERSION=${DENO_VERSION} 21 | COPY --from=bin /deno /bin/deno 22 | 23 | WORKDIR /deno-dir 24 | COPY . . 25 | 26 | ENTRYPOINT ["/bin/deno"] 27 | CMD ["run", "-A --unstable", "https://deno.land/std/examples/echo_server.ts"] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 the Deno authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { Hono } from "https://deno.land/x/hono@v3.5.6/mod.ts"; 3 | import { jsx } from "https://deno.land/x/hono@v3.5.6/middleware.ts"; 4 | 5 | interface Todo { 6 | text: string; 7 | } 8 | 9 | const kv = await Deno.openKv(); 10 | 11 | // Connect to a Deno Deploy KV database 12 | // const dbId = "7dd0d727-3fb0-4c06-84ff-a7062921f940"; 13 | // const dbUrl = `https://api.deno.com/databases/${dbId}/connect`; 14 | // const kv = await Deno.openKv(dbUrl); 15 | 16 | const app = new Hono(); 17 | 18 | app.get("/", async (c) => { 19 | const iter = await kv.list({ prefix: ["todos"] }); 20 | const todos = []; 21 | for await (const res of iter) todos.push(res.value); 22 | 23 | return c.html( 24 | 25 | 26 | Deno KV TODOs 27 | 28 | 29 |
30 |

TODO List

31 |
32 | 33 | 36 |
37 | 38 | {todos.map((todo) => ( 39 |
40 |
45 | 46 | 47 |
48 | {todo.text} 49 |
50 | ))} 51 |
52 | 53 | , 54 | ); 55 | }); 56 | 57 | app.post("/todo", async (c) => { 58 | const body = await c.req.parseBody<{ todo: string }>(); 59 | const todo: Todo = { 60 | text: body.todo, 61 | }; 62 | 63 | await kv.set(["todos", todo.text], todo); 64 | 65 | return c.redirect("/", 303); 66 | }); 67 | 68 | app.post("/todo/delete", async (c) => { 69 | const body = await c.req.parseBody<{ todo: string }>(); 70 | 71 | await kv.delete(["todos", body.todo]); 72 | 73 | return c.redirect("/", 303); 74 | }); 75 | 76 | Deno.serve(app.fetch); 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deno KV on fly.io 2 | 3 | This example application demonstrates how to run a Deno server on 4 | [fly.io](https://fly.io) that uses [Deno KV](https://www.deno.com/kv) for 5 | persistence thanks to 6 | [KV remote connection strings](https://deno.com/deploy/docs/kv#connect-to-managed-databases-from-outside-of-deno-deploy). 7 | 8 | ## Local development 9 | 10 | The server is set up by default to load a KV database in the default location 11 | for local development. You can run the server with: 12 | 13 | ```bash 14 | deno task start 15 | ``` 16 | 17 | ## Deployment 18 | 19 | To deploy this application to [fly.io](https://fly.io), you will need to: 20 | 21 | - [Sign up for fly.io and install the `flyctl` command line utility](https://fly.io/docs/hands-on/install-flyctl/) 22 | - [Sign up for Deno Deploy and create a blank project (we'll just use KV)](https://dash.deno.com/signin) 23 | 24 | After signing up for both services, edit the code in `server.tsx` to use a 25 | remote KV database instead of a local one. Comment out this line on line 9 near 26 | the top of the file: 27 | 28 | ```ts 29 | // const kv = await Deno.openKv(); 30 | ``` 31 | 32 | Then, uncomment these three lines beneath it around line 12: 33 | 34 | ```ts 35 | const dbId = "7dd0d727-3fb0-4c06-84ff-a7062921f940"; 36 | const dbUrl = `https://api.deno.com/databases/${dbId}/connect`; 37 | const kv = await Deno.openKv(dbUrl); 38 | ``` 39 | 40 | Replace the `dbId` variable with the ID for a KV database in your Deno Deploy 41 | project. This ID can be found on a project dashboard under the "KV" tab. 42 | 43 | ![Deno KV UUID string location](/docs/deno-dash.png?raw=true "Deno KV UUID string location") 44 | 45 | After configuring the database connection URL, you'll also need to export an 46 | environment variable: 47 | 48 | ```bash 49 | export DENO_KV_ACCESS_TOKEN=xxxxxxxxx 50 | ``` 51 | 52 | This access token will be used by the Deno runtime to authenticate your 53 | connection to the database on Deno Deploy. You can create an access token in the 54 | Deno dashboard [here](https://dash.deno.com/account#access-tokens). 55 | 56 | After making this change to your code, run it locally to ensure it's properly 57 | connected to and interacting with your remote KV database. 58 | 59 | Once things are looking good with your app code, you can optionally deploy it to 60 | fly.io to see how it works in a non-Deno Deploy hosting environment. 61 | 62 | ### Deploying to fly.io 63 | 64 | [Create an application](https://fly.io/docs/flyctl/apps-create/) on fly.io. 65 | Replace the name of your application in line 6 of `fly.toml`. 66 | 67 | ```toml 68 | app = "deno-flyio3" # your app ID here 69 | primary_region = "dfw" 70 | ``` 71 | 72 | After creating the application, you'll need to configure a 73 | `DENO_KV_ACCESS_TOKEN` environment variable, just as we did above. You can use 74 | the same access token as before, or a different one. Environment variables are 75 | defined under the "Secrets" tab of your fly.io application. 76 | 77 | ![Configure a fly.io env variable](/docs/fly-dash.png?raw=true "Configure a fly.io env variable") 78 | 79 | The `Dockerfile` for this project should be fine without further editing. With 80 | your app ID and Deno Deploy access token configured, you should be able to run: 81 | 82 | ```bash 83 | fly deploy 84 | ``` 85 | 86 | This will send your app to the cloud, where it should happily connect to 87 | Deno KV. 88 | 89 | ## License 90 | 91 | MIT 92 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://deno.land/x/hono@v3.5.6/adapter/deno/serve-static.ts": "ba10cf6aaf39da942b0d49c3b9877ddba69d41d414c6551d890beb1085f58eea", 5 | "https://deno.land/x/hono@v3.5.6/client/client.ts": "e56bef190db745f83b3560562e4ef3716df372b416c6a05c2a8038b854fd2eb3", 6 | "https://deno.land/x/hono@v3.5.6/client/index.ts": "3ff4cf246f3543f827a85a2c84d66a025ac350ee927613629bda47e854bfb7ba", 7 | "https://deno.land/x/hono@v3.5.6/client/utils.ts": "053273c002963b549d38268a1b423ac8ca211a8028bdab1ed0b781a62aa5e661", 8 | "https://deno.land/x/hono@v3.5.6/compose.ts": "e55ed7be2134f363ff3c8e8e2f520ff682c6a11a47d7189100ed69704ce10b9e", 9 | "https://deno.land/x/hono@v3.5.6/context.ts": "87b2017a3396e1d84c91229ad7c3016670425508572c9855ec6c70fc4b9d1b84", 10 | "https://deno.land/x/hono@v3.5.6/helper/cookie/index.ts": "cef46836d186d9d920f620519f7e96fbe7326c19576b86e1e555ed2229476091", 11 | "https://deno.land/x/hono@v3.5.6/helper/html/index.ts": "a5028d8170dcc030d003749e743213e6532ff65798b741b81220207abc9af82d", 12 | "https://deno.land/x/hono@v3.5.6/hono-base.ts": "f7b5848f72de985b7cce9061526eba51c7cbe944f46d8e5dc5d09b21a20dbbd8", 13 | "https://deno.land/x/hono@v3.5.6/hono.ts": "4bb8574697129ccab8b3a0e457cdc78468c31e3330fece8a7d8850e9314bf5ef", 14 | "https://deno.land/x/hono@v3.5.6/http-exception.ts": "6071df078b5f76d279684d52fe82a590f447a64ffe1b75eb5064d0c8a8d2d676", 15 | "https://deno.land/x/hono@v3.5.6/jsx/index.ts": "7c49f4d770c837a0e8c18e76994553b8ea0e8b746240044cb5395c6ebf87fd6e", 16 | "https://deno.land/x/hono@v3.5.6/middleware.ts": "21032c8d8468435b4268accd540b469cd764edfa745b5e36244a76f9eb898f0c", 17 | "https://deno.land/x/hono@v3.5.6/middleware/basic-auth/index.ts": "5505288ccf9364f56f7be2dfac841543b72e20656e54ac646a1a73a0aa853261", 18 | "https://deno.land/x/hono@v3.5.6/middleware/bearer-auth/index.ts": "11d4ead9b57f5bcb2b6b4bf27076871f15da0e1e8828b2b79d90c15423357b47", 19 | "https://deno.land/x/hono@v3.5.6/middleware/cache/index.ts": "9e5d31d33206bb5dba46dde16ed606dd2cb361d75c26b02e02c72bd1fb6fe53e", 20 | "https://deno.land/x/hono@v3.5.6/middleware/compress/index.ts": "85d315c9a942d7758e5c524dc94b736124646a56752e56c6e4284f3989b4692a", 21 | "https://deno.land/x/hono@v3.5.6/middleware/cors/index.ts": "c227e20bdd6481120e39a222e8dcec24ab129714748f68f3ddc859ef6855fe0f", 22 | "https://deno.land/x/hono@v3.5.6/middleware/etag/index.ts": "2d71b48e74d80e899d5a34cbf900496dd732a2255740a77c41bdf2d723e3ed39", 23 | "https://deno.land/x/hono@v3.5.6/middleware/jwt/index.ts": "6a8fa9ca42e49756dd1b8db6f0abe5b6684681ece8fc7f65a4892f7a82000236", 24 | "https://deno.land/x/hono@v3.5.6/middleware/logger/index.ts": "c139f372f482baeffbad68b14bef990e011fe8df578dcee71fb612ffad7fe748", 25 | "https://deno.land/x/hono@v3.5.6/middleware/powered-by/index.ts": "c36b7a3d1322c6a37f3d1510f7ff04a85aa6cacfac2173e5f1913eb16c3cc869", 26 | "https://deno.land/x/hono@v3.5.6/middleware/pretty-json/index.ts": "f6967ceecdb42c95ddd5e2e7bc8545d3e8bda111fa659f3f1336b2e6fe6b0bb0", 27 | "https://deno.land/x/hono@v3.5.6/middleware/secure-headers/index.ts": "22ee5b8b436d6b3d46a6ce4387cd987f1affce25e813ade9ec371f156a16a871", 28 | "https://deno.land/x/hono@v3.5.6/middleware/timing/index.ts": "d6976a07d9d51a7b26dae1311fe51d0744f7d234498bac3fe024ec7088c0ca47", 29 | "https://deno.land/x/hono@v3.5.6/mod.ts": "eee0818e5c557b72863abdd68dcf6fd5277e6e85851a685c7398566f2c12947c", 30 | "https://deno.land/x/hono@v3.5.6/request.ts": "77d67266e47fbaf0145f069e2fb02799b8d54504bb8c945c8b65a513bdd11aa9", 31 | "https://deno.land/x/hono@v3.5.6/router.ts": "d03e2a9e862c898e8555b451ba80c65b1f39d487230815229e1f046dbd8546cb", 32 | "https://deno.land/x/hono@v3.5.6/router/linear-router/index.ts": "8a2a7144c50b1f4a92d9ee99c2c396716af144c868e10608255f969695efccd0", 33 | "https://deno.land/x/hono@v3.5.6/router/linear-router/router.ts": "90d4afc052b72f53dafbcf97fd32f24299b985f8a35dbdc70b28048201b3dcbc", 34 | "https://deno.land/x/hono@v3.5.6/router/pattern-router/index.ts": "304a66c50e243872037ed41c7dd79ed0c89d815e17e172e7ad7cdc4bc07d3383", 35 | "https://deno.land/x/hono@v3.5.6/router/pattern-router/router.ts": "8107b92cb713ba3727639c76fab44fd26883d5f2fa7dd39242150a0f269d4835", 36 | "https://deno.land/x/hono@v3.5.6/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db", 37 | "https://deno.land/x/hono@v3.5.6/router/reg-exp-router/node.ts": "8006b5bccb83d9fc98e0562a5545f6dd0be639ce445b089a6171c9c617aa8693", 38 | "https://deno.land/x/hono@v3.5.6/router/reg-exp-router/router.ts": "aeddd88e16a7ec3cf088a13ee952cfcb3319f97559767253e1a600f72fc70f27", 39 | "https://deno.land/x/hono@v3.5.6/router/reg-exp-router/trie.ts": "567493b301c44174f0895aedb8d055bbecf88f8a25626fa8ca61333bbd0c882c", 40 | "https://deno.land/x/hono@v3.5.6/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef", 41 | "https://deno.land/x/hono@v3.5.6/router/smart-router/router.ts": "38209165dadea4182b42807a0a6c84a9258532a07ebf87262dc2b5b09d25a2a2", 42 | "https://deno.land/x/hono@v3.5.6/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41", 43 | "https://deno.land/x/hono@v3.5.6/router/trie-router/node.ts": "a52111dea7436d6817ffca04018eb65e552e9d7b15fba11b367016334dcb1b0f", 44 | "https://deno.land/x/hono@v3.5.6/router/trie-router/router.ts": "ad0b3fdabc33abd11a9f5819734aec743602a743cfc9f90ddad73787cd5e7727", 45 | "https://deno.land/x/hono@v3.5.6/types.ts": "aea3f80e883fbfa45c71d7fd4ac6f7f0cd37e1e25a54bb056f9aa9e3830ec9ab", 46 | "https://deno.land/x/hono@v3.5.6/utils/body.ts": "bf2b7732c5941a1024ed5a6017c37ac4edc341379a6d93233f05faa0b37bbff0", 47 | "https://deno.land/x/hono@v3.5.6/utils/buffer.ts": "d28ab08d2571e890ec2ad7ce4c0318a503094f8403eac3d5eb18a8e5c23b29b2", 48 | "https://deno.land/x/hono@v3.5.6/utils/cookie.ts": "3585fd451de104e7e090d5b4d685f71b528873b21a634cb96b19fed622fe4384", 49 | "https://deno.land/x/hono@v3.5.6/utils/crypto.ts": "bda0e141bbe46d3a4a20f8fbcb6380d473b617123d9fdfa93e4499410b537acc", 50 | "https://deno.land/x/hono@v3.5.6/utils/encode.ts": "3b7c7d736123b5073542b34321700d4dbf5ff129c138f434bb2144a4d425ee89", 51 | "https://deno.land/x/hono@v3.5.6/utils/filepath.ts": "0059e4d88e62c3630aa67bd51eca0a6d4767c7e5d472d0ea2e1fec691008d152", 52 | "https://deno.land/x/hono@v3.5.6/utils/html.ts": "9dac0ef4014170bbd387c20913e60d6f506ecd95ba931c0e5a86e50962c118fe", 53 | "https://deno.land/x/hono@v3.5.6/utils/jwt/index.ts": "5e4b82a42eb3603351dfce726cd781ca41cb57437395409d227131aec348d2d5", 54 | "https://deno.land/x/hono@v3.5.6/utils/jwt/jwt.ts": "e822e0c183cdc77709ccfba7b527f624f695c149425a00602d314165a87ee1bf", 55 | "https://deno.land/x/hono@v3.5.6/utils/jwt/types.ts": "16d7fa878a875eddd0049f0aa0192075058f19d51816a5d484d9b80e4d27abea", 56 | "https://deno.land/x/hono@v3.5.6/utils/mime.ts": "0105d2b5e8e91f07acc70f5d06b388313995d62af23c802fcfba251f5a744d95", 57 | "https://deno.land/x/hono@v3.5.6/utils/url.ts": "5fc3307ef3cb2e6f34ec2a03e3d7f2126c6a9f5f0eab677222df3f0e40bd7567", 58 | "https://deno.land/x/hono@v3.5.6/validator/index.ts": "6c986e8b91dcf857ecc8164a506ae8eea8665792a4ff7215471df669c632ae7c", 59 | "https://deno.land/x/hono@v3.5.6/validator/validator.ts": "0b0ed07a8a8e3ff98cbf20f185e332f70d353c696f16e75dd1a665e2762a2ecb" 60 | } 61 | } 62 | --------------------------------------------------------------------------------