URL短縮サービスをWorkers+KVで作った


なが~~いURL、どうやって渡す?

世の中には超ながいURLが転がっている時があります。基本、どんなに長くてもクリックさせれば開けますが…長すぎるせいで見た目が悪い!なんてこともありますし、相手に見せながら入力させるときに困ってしまいます。

そんなときに役立つのがURL短縮サービスですが、どうせなら自作してみたいものです。 Cloudflare Workers+KVを使えばあっという間に作れます。やってみましょう!

完成したもの

これです。URLを入力して短縮!ボタンを押すだけで生成されます。

中身(仕組み)

フレームワークにはHonoを使用しています。

まずは最初の部分から:

import { Hono } from 'hono'
import { customAlphabet } from 'nanoid'
const rooturl = 'https://r.a1z.uk' //jsで取得すればいいなんて言わないで
const nanoid = customAlphabet(
  '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
  7,
)
type Env = {
	SHORT_URLS: KVNamespace
}
const app = new Hono<{ Bindings: Env }>()

短縮URLのランダムな文字列にはnanoidcustomAlphabetを使用しています。

HonoはKVの名前空間をバインドできるので、それも使っています。Honoさいこー!

/はこんな感じ。

app.get('/', (c) => {
    return c.html(`
        <html>
		<head>
			<meta charset="UTF-8" />
			<meta name="description" content="ながーーーいURLを短縮っ!" />
			<meta property="og:site_name" content="r.a1z.uk">
			<meta name="theme-color" content="#FACEFA">
			<title>Simple URL Shortener</title>
		</head>
            <h1>Simple URL Shortener</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>
		<style>
			html {
				display: flex;
				justify-content: center;
				text-align: center;
			}
		</style>
    `)
})

htmlのコードを返しています。シンプルですね✨

ここでボタンを押すと/shortenにPOSTされます。

async function shorten(kv: KVNamespace, url: string) {
	const key = nanoid()
	const createdAt = Date.now()
	await kv.put(key, JSON.stringify({ url, createdAt }))
	return { key }
}
app.post('/shorten', async (c) => {
	const body = await c.req.parseBody<{url: string}>()
	console.log(body)
	const { url } = body
	if (!url || typeof url !="string") {
		return c.html('<p>無効なURLが入力されました。</p>', 400)
	}
	const { key } = await shorten(c.env.SHORT_URLS, url)
	return c.html(`
	<h1>生成しました!</h1>
	<p id="url">${rooturl}/${key}</p>
	<p>リダイレクト先: ${url}</p>
	<style>
		html {
			display: flex;
			justify-content: center;
			text-align: center;
		}
	</style>
	`)
})

こちらも短縮URLを生成するfunctionを実行させた後、htmlを返すシンプルな仕組みです。

そして、生成したkeyに対してアクセスすると

interface URL {
	url: string
	createdAt: number
}
  
async function getUrl(kv: KVNamespace, key: string) {
	return kv.get<URL>(key, 'json')
}

app.get('/:key', async (c) => {
	const key = c.req.param('key')
	const res = await getUrl(c.env.SHORT_URLS, key)
	if (!res) {
		return c.text('存在しない短縮リンクです。', 404)
	}
	const url = res.url
	return c.redirect(url, 301)
})

このように、kvからkeyを探して、resが空っぽの場合は404、そうでなければリダイレクト先を結果から抽出して301を返すようにしています。

Workers/Pagesが本当に素晴らしい

さて、今回のURL短縮サービスにはCloudflare Workersを使っています。せっかくなのでWorkers/Pagesの魅力を語ろうと思います。

今の時代では、簡単なサービスを、自分でサーバーを用意せずに(しかも無料で)作ることができるようになりました。 私はまだそこら辺の高校生(2024年時点)ですが、すごい時代だと思います。

Workers/PagesはサーバーレスでコードやWebページを動かせるサービスの一つで、とにかく簡単で速いのが魅力です。 似たようなサービスはたくさんありますが、とにかく制約が少ないです。使わないわけにはいきません。

実は私がWorkersを本格的に触り始めたのは二ヶ月前ぐらいで、まだまだ知らない事も多いのですが、毎日Workersの凄さを感じています。本当にいい所だらけ… だからこそ、簡単なWebページやサービスを作ろうとしている人には

「Cloudflare Workers/Pagesを使わないかい?」

と勧めています。怪しい人にしか見えないと思いますが、Cloudflare大好き人間としてはみんなに推したいのです。

もちろん、大規模なプロジェクトだったり、複雑な構造になってくると自分でサーバーを用意する方が圧倒的に幸せですが… そうでなければWorkersなんかで十分です。私のプロジェクトは単純な物が多いので、徐々にWorkers/Pagesに移行しつつあります。 その方が管理だったりいろんな面で楽になります。電気代も減りますし…

というわけで、Cloudflare万歳な記事になっちゃいました。おわり!