💭

Cloudflare Workers+Hono を使って76行で短縮URLサービスを作る

2023/12/21に公開

この記事は Cloudflare Advent Calendar 2023の17日目の記事です

作ったもの

https://short.sugawani.monster/

実際のコード

import { Hono } from 'hono'
import qr from 'qr-image'

type Bindings = {
    URL_BINDING: KVNamespace
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/', (c) => {
    return c.html(
        <html>
            <h1>URL短縮サービス</h1>
            <div>
                <span>短縮したいURLを入力してください</span>
            </div>
            <form action='/shorten' method='post'>
                <div>
                    <label>URL</label>
                    <input type='url' name='url' required />
                    <button>短縮</button>
                </div>
            </form>
        </html>
    )
})

app.post('/shorten', async (c) => {
    const body = await c.req.parseBody()
    const url = body['url']
    if (!url) {
        c.status(500)
        return c.text("URLは必須です")
    }
    if (typeof url != "string") {
        c.status(400)
        return c.text("文字列以外はだめです")
    }
    const generator = (len: number) => {
        const c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
        return Array.from({ length: len }, () => c.charAt(Math.floor(Math.random() * c.length))).join('')
    }
    let exists = false
    let key = ""
    do {
        key = generator(6)
        const v = await c.env.URL_BINDING.get(key)
        if (v != null) {
            exists = true
        } else {
            exists = false
        }
    } while (exists)

    await c.env.URL_BINDING.put(key, url)
    const shortenURL = `${new URL(c.req.url).origin}/${key}`
    const QRBase64 = `data:image/png;base64,${qr.imageSync(shortenURL).toString('base64')}`
    return c.html(
        <html>
            <h1>短縮URLが生成されました!</h1>
            <h2>{shortenURL}</h2>
            <img src={QRBase64} />
        </html>
    )
})

app.get('/:key', async (c) => {
    const key = c.req.param('key')
    const url = await c.env.URL_BINDING.get(key)
    if (url == null) {
        return c.notFound()
    }
    return c.redirect(url)
})

export default app

全体のコードはこちら
https://github.com/sugawani/cf-url-shortener
他にも各種設定ファイルなどありますが、それを含めても100行以内で作れます

解説

ホスティング

https://www.cloudflare.com/ja-jp/developer-platform/workers/
Cloudflare Workers 上で動いています

フレームワーク

https://hono.dev/
今最もホット(Honoだけに)なフレームワーク

データストア

https://www.cloudflare.com/ja-jp/developer-platform/workers-kv/
シンプルな KV ストア

QR コードの生成

https://www.npmjs.com/package/qr-image
Cloudflare Workers 上でも動くシンプルなライブラリ

感想

https://www.itmedia.co.jp/news/articles/2311/15/news194.html
短縮 URL サービスを使用した事故が起きたことをきっかけに、どんな仕組みで動いていてどんなリスクがあるか知りたくて作ってみました
基本的な実装はシンプルで、短縮前と短縮後の URL を紐づけして持っておくだけで短縮 URL を作ることができました
さまざまな記事で解説されていますが、サービス実装者は短縮後の URL の転送先を好きな URL に変更できるため、野良サービスを使うリスクが実装することでより実感できました

EGSTOCK,Inc.

Discussion