この投稿では、Express.js(Node.js)とMongoDBを使用してbit.lyやgoo.glなどのURL短縮サービスを構築する方法を紹介します。これは、MongoDBホスティングプラットフォームを介して構築する最終製品のデモです。
URL短縮サービスはどのように機能しますか?
非常に高いレベルでは、URL短縮サービスは、入力されたURLを取得し、共有しやすい形式に簡略化された比較的短縮されたバージョンを作成することで機能します。短縮されたハッシュは、自動インクリメントされたカウンターをベースエンコードすることによって生成され、保存されたURLの数が増えるにつれて増加する最小の3文字のハッシュを作成します。
URLの短縮バージョンにアクセスすると、サービスはハッシュをデコードしてMongoDBに保存されている元のURLを取得し、ユーザーをそのURLにリダイレクトします。
はじめに
このチュートリアルでURL短縮サービスを構築するために使用するテクノロジーのリストは次のとおりです。
-
Express.js(Node.jsバックエンド)
Node.js用のウェブアプリケーションフレームワーク。これを使用して、URLを短縮し、ユーザーを元のURLにリダイレクトするためのAPIを構築します。
-
MongoDB(URLの保存)
このアプリケーションに最適なNoSQLデータベース。柔軟なスキーマ設計を提供し、簡単に開始できます。このチュートリアルでは、ScaleGridで共有MongoDBクラスターを使用します。セットアップには5分もかかりません。ここで、30日間の無料トライアルを作成して開始できます。
-
HTML、CSS、JavaScript(フロントエンド)
HTML、CSS、JavaScriptを使用して、ユーザーがURLを短縮するために使用するアプリケーションのフロントエンドを構築します。
URL短縮サービスチュートリアル
-
MongoDBデータベース構造のセットアップ
まず、ScaleGridで共有MongoDBクラスターを作成しましょう。これはクイッククラスターを作成する最も簡単な方法ですが、MongoDBをマシンにインストールして、そこで開始することもできます。
クラスターが作成されると、[クラスターの詳細]ページからシングルクリックでコピーできる接続文字列が提供されます。アプリケーションからクラスターに接続するには、この文字列が必要です。接続文字列を他の人と共有しないでください。
URL短縮サービスには2つのコレクションが必要です:
-
コレクション1
URLと動的に生成されたIDを保存するコレクション:
-
コレクション2
新しいURLが前のコレクションに保存されたときに自動インクリメントされるカウンターを維持するためのコレクション。この新しくインクリメントされたカウンターを使用して、前のコレクションに新しいドキュメントが作成されます:
データベースのどこにもハッシュを保存していないことに注意してください。ハッシュはベースエンコードされ、一般的なアルゴリズムを使用して動的にデコードされ、最初のコレクションに一意のIDが格納されます。このIDにより、ユーザーがリダイレクトする元のURLが取得されます。
このチュートリアルでは、共通のbase64エンコードおよびデコードメカニズムを使用して、独自の短縮ハッシュを生成します。 base64を使用した文字列のエンコード/デコードの詳細については、次のMDNWebDocを確認してください。
-
-
Express.jsバックエンドをセットアップする
Node.jsバックエンドを設定するために必要な依存関係のリストは次のとおりです。
- express(ベースアプリ)
- body-parser(HTTPリクエストを介して送信されたデータを解析するためのアドオン)
- btoa(base64エンコーディング)
- atob(base64デコード)
- dotenv(開発目的で.envファイルに接続文字列を保存する)
- mongoose(Node.js上のMongoDBのアダプター)
アプリのセットアップに使用できるpackage.jsonのサンプルバージョンは次のとおりです。
{ "name": "sg-url-shortener", "version": "1.0.0", "description": "A simple URL shortener built with Node.js and MongoDB", "dependencies": { "atob": "^2.0.3", "body-parser": "^1.15.2", "btoa": "^1.1.2", "dotenv": "^4.0.0", "express": "^4.10.2", "mongoose": "^4.13.7" }, "main": "index.js", "scripts": { "start": "node index.js" }, "engines": { "node": "4.8.4" } }
「npminstall」を実行して、必要なすべての依存関係をインストールします。
すべての依存関係を設定したら、共有MongoDBクラスターに接続する必要があります。プロジェクトのルートに.envファイルを作成し、それに接続文字列を追加します。接続文字列は、ScaleGridコンソールの[概要]タブにある[クラスターの詳細]ページから取得できます。
connectionString=mongodb://user:password@devservers
コードの記述を開始する前に、アプリフローを視覚化して、短縮プロセスがどのように機能するかを十分に理解することをお勧めします。 URL短縮のプロセスを示す図は次のとおりです。
短縮URLにアクセスしたときのリダイレクトのプロセスを示す図は次のとおりです:
プロセス全体を視覚化したので、次は上記のフローチャートをコードに変換します。
-
アプリケーションの初期化
ビジネスロジックの作成を開始する前に、ノードモジュールを使用してアプリケーションを初期化し、サーバーをセットアップする必要があります。
開発モードでのみ.envファイルをロードします。デモアプリケーションはHerokuでホストされているため、Herokuダッシュボードから環境変数が作成され、そこに接続文字列が既に含まれています。
if(process.env.NODE_ENV !== 'production') { require('dotenv').load(); }
アプリケーションの初期化、サーバー、ミドルウェアのセットアップ。環境変数から接続文字列も取得していることに注意してください:
var express = require('express'), bodyParser = require('body-parser'), app = express(), http = require('http').Server(app), mongoose = require('mongoose'), btoa = require('btoa'), atob = require('atob'), promise, connectionString = process.env.connectionString, port = process.env.PORT || 8080; http.listen(port, function() { console.log('Server Started. Listening on *:' + port); }); app.use(express.static('public')); app.use(bodyParser.urlencoded({ extended: true }));
アプリのフロントエンドを読み込むためのベースルート:
app.get('/', function(req, res) { res.sendFile('views/index.html', { root: __dirname }); });
-
MongoDBへのURLの保存
データを保存するためのコレクションスキーマを作成することから始めましょう。上で説明したように、2つのコレクションが必要です。1つは自動インクリメントされたカウンターを格納するためのもので、もう1つはURLを格納するためのものです。
var countersSchema = new mongoose.Schema({ _id: { type: String, required: true }, count: { type: Number, default: 0 } }); var Counter = mongoose.model('Counter', countersSchema); var urlSchema = new mongoose.Schema({ _id: {type: Number}, url: '', created_at: '' }); urlSchema.pre('save', function(next) { console.log('running pre-save'); var doc = this; Counter.findByIdAndUpdate({ _id: 'url_count' }, { $inc: { count: 1 } }, function(err, counter) { if(err) return next(err); console.log(counter); console.log(counter.count); doc._id = counter.count; doc.created_at = new Date(); console.log(doc); next(); }); }); var URL = mongoose.model('URL', urlSchema);
上記のコードは、2つのコレクションを作成し、これらのコレクションを保存するためのデータベースを設定します。また、カウンタを自動インクリメントし、URLが作成された日時をログに記録する必要があるため、URLスキーマに事前保存フックを使用しています。
次に、アプリケーションを新しく起動し、以前のエントリがすべて削除されていることを確認する必要があります。リセットしたら、カウンターを開始値10,000で初期化して、URL短縮プロセスを設定します。任意の値から始めることができます。これはランダムに選択され、1の値で自動インクリメントされます。
promise = mongoose.connect(connectionString, { useMongoClient: true }); promise.then(function(db) { console.log('connected!'); URL.remove({}, function() { console.log('URL collection removed'); }) Counter.remove({}, function() { console.log('Counter collection removed'); var counter = new Counter({_id: 'url_count', count: 10000}); counter.save(function(err) { if(err) return console.error(err); console.log('counter inserted'); }); }); });
これで、アプリケーションでURLの受け入れと短縮を開始する準備が整いました。フロントエンドがURLの送信に使用するPOSTAPIを作成しましょう:
app.post('/shorten', function(req, res, next) { console.log(req.body.url); var urlData = req.body.url; URL.findOne({url: urlData}, function(err, doc) { if(doc) { console.log('entry found in db'); res.send({ url: urlData, hash: btoa(doc._id), status: 200, statusTxt: 'OK' }); } else { console.log('entry NOT found in db, saving new'); var url = new URL({ url: urlData }); url.save(function(err) { if(err) return console.error(err); res.send({ url: urlData, hash: btoa(url._id), status: 200, statusTxt: 'OK' }); }); } }); });
フロー図で概説されているように、有効なURLが受信されると、データベースにその存在が存在するかどうかが確認されます。
見つかった場合、対応する_idフィールドをデコードし、ハッシュを返します。フロントエンドは短縮URLを作成し、リダイレクトのためにユーザーに提示します。
URLが見つからない場合は、コレクションに新しいドキュメントを保存します。 URLが保存されるたびに、事前保存ステップが実行されることを忘れないでください。これにより、カウンターが自動的にインクリメントされ、現在の日付と時刻がログに記録されます。ドキュメントが追加された後、フロントエンドにハッシュを送信します。フロントエンドは短縮URLを作成し、リダイレクトのためにユーザーに提示します。
-
ユーザーのリダイレクト
ほぼ完了です。短縮URLが作成されたら、短縮URLにアクセスしたときにユーザーをリダイレクトする方法が必要です。
app.get('/:hash', function(req, res) { var baseid = req.params.hash; var id = atob(baseid); URL.findOne({ _id: id }, function(err, doc) { if(doc) { res.redirect(doc.url); } else { res.redirect('/'); } }); });
上記のコードは、短縮URLでハッシュを検索し、base64でデコードし、そのIDがコレクションに存在するかどうかを確認し、それに応じてユーザーをリダイレクトします。 IDが見つからない場合、ユーザーはURL短縮サービスのホームページにリダイレクトされます。
フロントエンドコードについては、この投稿の最後に記載されているGitHubリポジトリを確認してください。これは基本的に、URLをバックエンドに送信するためのボタンが付いたテキストボックスフィールドであり、この記事の範囲外です。
その他のURL短縮サービスの機能強化
これで完了です。リンクを簡素化するために内部で使用できる最低限のURL短縮サービスがあります。ベルやホイッスルをさらに追加したい場合は、追加で実装できるもののリストを次に示します。
- より良いコード分割
- 小さい文字ハッシュ(例:base52)のより良い/カスタム短縮アルゴリズム
- ソーシャルメディアで短縮URLを共有する
- ワンクリックのURLコピー
- カスタムハッシュ
- ユーザー登録と短縮URLの関連付け
コード全体はこちらから入手できます:ScaleGrid URL Shortenerコードサンプル– GithubデモアプリケーションはHerokuでホストされています:ScaleGrid URL Shortener Demo
いつものように、すばらしいものを作成する場合は、@scalegridioについてツイートしてください。 MongoDB®のホスティングまたはRedis™*のホスティングについてサポートが必要な場合は、[email protected]までご連絡ください。