1006 lines
4.4 MiB
HTML
1006 lines
4.4 MiB
HTML
|
<html itemscope="" itemtype="https://schema.org/" lang="en-US"><head><meta http-equiv="Content-Security-Policy" content="default-src 'unsafe-inline' data:;"></meta><script src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgIG1hZ25ldDo/eHQ9dXJuOmJ0aWg6MGIzMTUwOGFlYjA2MzRiMzQ3YjgyNzBjN2JlZTRkNDExYjVkNDEwOSZkbj1hZ3BsLTMuMC50eHQgQUdQTC12My4wCi8qIGVzbGludC1kaXNhYmxlIG5vLXZhciwgc2VtaSwgcHJlZmVyLWFycm93LWNhbGxiYWNrLCBwcmVmZXItdGVtcGxhdGUgKi8KCi8qKgogKiBDb2xsZWN0aW9uIG9mIG1ldGhvZHMgZm9yIHNlbmRpbmcgYW5hbHl0aWNzIGV2ZW50cyB0byBBcmNoaXZlLm9yZydzIGFuYWx5dGljcyBzZXJ2ZXIuCiAqCiAqIFRoZXNlIGV2ZW50cyBhcmUgdXNlZCBmb3IgaW50ZXJuYWwgc3RhdHMgYW5kIHNlbnQgKGluIGFub255bWl6ZWQgZm9ybSkgdG8gR29vZ2xlIEFuYWx5dGljcy4KICoKICogQHNlZSBhbmFseXRpY3MubWQKICoKICogQHR5cGUge09iamVjdH0KICovCndpbmRvdy5hcmNoaXZlX2FuYWx5dGljcyA9IChmdW5jdGlvbiBkZWZpbmVBcmNoaXZlQW5hbHl0aWNzKCkgewogIHZhciBBUkNISVZFX0FOQUxZVElDU19WRVJTSU9OID0gMjsKICB2YXIgREVGQVVMVF9TRVJWSUNFID0gJ2FvXzInOwoKICB2YXIgc3RhcnRUaW1lID0gbmV3IERhdGUoKTsKCiAgLyoqCiAgICogQHJldHVybiB7Qm9vbGVhbn0KICAgKi8KICBmdW5jdGlvbiBpc1BlcmZvcm1hbmNlVGltaW5nQXBpU3VwcG9ydGVkKCkgewogICAgcmV0dXJuICdwZXJmb3JtYW5jZScgaW4gd2luZG93ICYmICd0aW1pbmcnIGluIHdpbmRvdy5wZXJmb3JtYW5jZTsKICB9CgogIC8qKgogICAqIERldGVybWluZXMgaG93IG1hbnkgbWlsbGlzZWNvbmRzIGVsYXBzZWQgYmV0d2VlbiB0aGUgYnJvd3NlciBzdGFydGluZyB0byBwYXJzZSB0aGUgRE9NIGFuZAogICAqIHRoZSBjdXJyZW50IHRpbWUuCiAgICoKICAgKiBVc2VzIHRoZSBQZXJmb3JtYW5jZSBBUEkgb3IgYSBmYWxsYmFjayB2YWx1ZSBpZiBpdCdzIG5vdCBhdmFpbGFibGUuCiAgICoKICAgKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9QZXJmb3JtYW5jZV9BUEkKICAgKgogICAqIEByZXR1cm4ge051bWJlcn0KICAgKi8KICBmdW5jdGlvbiBnZXRMb2FkVGltZSgpIHsKICAgIHZhciBzdGFydDsKCiAgICBpZiAoaXNQZXJmb3JtYW5jZVRpbWluZ0FwaVN1cHBvcnRlZCgpKQogICAgICBzdGFydCA9IHdpbmRvdy5wZXJmb3JtYW5jZS50aW1pbmcuZG9tTG9hZGluZzsKICAgIGVsc2UKICAgICAgc3RhcnQgPSBzdGFydFRpbWUuZ2V0VGltZSgpOwoKICAgIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHN0YXJ0OwogIH0KCiAgLyoqCiAgICogRGV0ZXJtaW5lcyBob3cgbWFueSBtaWxsaXNlY29uZHMgZWxhcHNlZCBiZXR3ZWVuIHRoZSB1c2VyIG5hdmlnYXRpbmcgdG8gdGhlIHBhZ2UgYW5kCiAgICogdGhlIGN1cnJlbnQgdGltZS4KICAgKgogICAqIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1BlcmZvcm1hbmNlX0FQSQogICAqCiAgICogQHJldHVybiB7TnVtYmVyfG51bGx9IG51bGwgaWYgdGhlIGJyb3dzZXIgZG9lc24ndCBzdXBwb3J0IHRoZSBQZXJmb3JtYW5jZSBBUEkKICAgKi8KICBmdW5jdGlvbiBnZXROYXZUb0RvbmVUaW1lKCkgewogICAgaWYgKCFpc1BlcmZvcm1hbmNlVGltaW5nQXBpU3VwcG9ydGVkKCkpCiAgICAgIHJldHVybiBudWxsOwoKICAgIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHdpbmRvdy5wZXJmb3JtYW5jZS50aW1pbmcubmF2aWdhdGlvblN0YXJ0OwogIH0KCiAgLyoqCiAgICogUGVyZm9ybXMgYW4gYXJpdGhtZXRpYyBjYWxjdWxhdGlvbiBvbiBhIHN0cmluZyB3aXRoIGEgbnVtYmVyIGFuZCB1bml0LCB3aGlsZSBtYWludGFpbmluZwogICAqIHRoZSB1bml0LgogICAqCiAgICogQHBhcmFtIHtTdHJpbmd9IG9yaWdpbmFsIHZhbHVlIHRvIG1vZGlmeSwgd2l0aCBhIHVuaXQKICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBkb09wZXJhdGlvbiBhY2NlcHRzIG9uZSBOdW1iZXIgcGFyYW1ldGVyLCByZXR1cm5zIGEgTnVtYmVyCiAgICogQHJldHVybnMge1N0cmluZ30KICAgKi8KICBmdW5jdGlvbiBjb21wdXRlV2l0aFVuaXQob3JpZ2luYWwsIGRvT3BlcmF0aW9uKSB7CiAgICB2YXIgbnVtYmVyID0gcGFyc2VGbG9hdChvcmlnaW5hbCwgMTApOwogICAgdmFyIHVuaXQgPSBvcmlnaW5hbC5yZXBsYWNlKC8oXGQqXC5cZCspfFxkKy8sICcnKTsKCiAgICByZXR1cm4gZG9PcGVyYXRpb24obnVtYmVyKSArIHVuaXQ7CiAgfQoKICAvKioKICAgKiBDb21wdXRlcyB0aGUgZGVmYXVsdCBmb250IHNpemUgb2YgdGhlIGJyb3dzZXIuCiAgICoKICAgKiBAcmV0dXJucyB7U3RyaW5nfG51bGx9IGNvbXB1dGVkIGZvbnQtc2l6ZSB3aXRoIHVuaXRzICh0eXBpY2FsbHkgcGl4ZWxzKSwgbnVsbCBpZiBpdCBjYW5ub3QgYmUgY29tcHV0ZWQKICAgKi8KICBmdW5jdGlvbiBnZXREZWZhdWx0Rm9udFNpemUoKSB7CiAgICB2YXIgZm9udFNpemVTdHI7CgogICAgaWYgKCEoJ2dldENvbXB1dGVkU3R5bGUnIGluIHdpbmRvdykpCiAgICAgIHJldHVybiBudWxsOwoKICAgIHZhciBzdHlsZSA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCk7CiAgICBpZiAoIXN0eWxlKQogICAgICByZXR1cm4gbnVsbDsKCiAgICBmb250U2l6ZVN0ciA9IHN0eWxlLmZvbnRTaXplOwoKICAgIC8vIERvbid0IG1vZGlmeSB0aGUgdmFsdWUgaWYgdHJhY2tpbmcgYm9vayByZWFkZXIuCiAgICBpZiAoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5jb250YWlucygnQm9va1JlYWRlclJvb3QnKSkKICAgICAgcmV0dXJuIGZvbnRTaXplU3RyOwoKICAgIHJldHVybiBjb21wdXRlV2l0aFVuaXQoZm9udFNpemVTdHIsIGZ1bmN0aW9uIHJldmVyc2VCb290c3RyYXBGb250U2l6ZShudW1iZXIpIHsKICAgICA
|
||
|
<script type="text/javascript">window.addEventListener('DOMContentLoaded',function(){var v=archive_analytics.values;v.service='wb';v.server_name='wwwb-app57.us.archive.org';v.server_ms=231;archive_analytics.send_pageview({});});</script><script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMAovLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ci8vIFdheWJhY2sgQ29tbW9uIEpTIExpYnJhcnkKLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKdmFyIFdCX3dvbWJhdF9yZXBsYXlTZXJ2ZXI7CnZhciBXQl93b21iYXRfcmVwbGF5UHJlZml4Owp2YXIgV0Jfd29tYmF0X3JlcGxheURhdGVQcmVmaXg7CnZhciBXQl93b21iYXRfY2FwdHVyZURhdGVQYXJ0Owp2YXIgV0Jfd29tYmF0X29yaWdIb3N0OwoKLy9Mb2NhdGlvbiBvYmplY3RzCnZhciBXQl93b21iYXRfc2VsZl9sb2NhdGlvbjsKdmFyIFdCX3dvbWJhdF90b3BfbG9jYXRpb247CnZhciBXQl93b21iYXRfb3BlbmVyX2xvY2F0aW9uOwoKLy8gRG9tYWluCnZhciBXQl93b21iYXRfZG9jdW1lbnRfZG9tYWluOwoKLy9mdW5jdGlvbiB0byBhbGxvdyBqcXVlcnkgZXhwYW5kbyByZXF1ZXN0cyAoaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83MjAwNzIyL2pxdWVyeS1leHBhbmRvLXByb3BlcnRpZXMpLAovL3doaWNoIHJldHVybiBhIGZ1bmN0aW9uIHRoYXQgaGFzIGl0cyBuYW1lIGRlZmluZWQgaW4gYSBwYXJhbWV0ZXIgb2YgdGhlIHJlcXVlc3QsIHRvIGJlIGV4ZWN1dGVkLiB3ZSByZXdyaXRlIHRoZSBmdW5jdGlvbiBjYWxsIGVsc2V3aGVyZSAoc2VlCi8vQXJjaGl2ZVVybFJlcGxheS54bWwpIGFuZCB0aGVuIGRlZmluZSBpdCBoZXJlIHRvIGVuc3VyZSBpdCBleGlzdHMuIGV4cGFuZG8gZnVuY3Rpb24gaW5jbHVkZSBjdXJyZW50IHRpbWVzdGFtcCBzbyB3ZSBjYW4gbmV2ZXIgcmVwbGF5IHRoZW0gd2l0aG91dAovL292ZXJyaWRpbmcgZGVmYXVsdCBleHBhbmRvIGJlaGF2aW9yCmZ1bmN0aW9uIGpRdWVyeVJFV1JJVFRFTl9CWV9XQVlCQUNLKCkgewogIG89YXJndW1lbnRzOwp9CgpmdW5jdGlvbiBXQl9HZXRfRG9tYWluKGhyZWYpIHsKICB2YXIgYSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTsKCiAgYS5ocmVmID0gaHJlZjsKICByZXR1cm4gYS5wcm90b2NvbCArICIvLyIgKyBhLmhvc3RuYW1lOwp9CgpmdW5jdGlvbiBXQl9TdHJpcFBvcnQoc3RyKSB7CiAgdmFyIGhvc3RXaXRoUG9ydCA9IHN0ci5tYXRjaCgvXmh0dHA6XC9cL1tcd1xkQC4tXSs6XGQrLyk7CiAgaWYgKGhvc3RXaXRoUG9ydCkgewogICAgdmFyIGhvc3ROYW1lID0gaG9zdFdpdGhQb3J0WzBdLnN1YnN0cigwLCBob3N0V2l0aFBvcnRbMF0ubGFzdEluZGV4T2YoJzonKSk7CiAgICByZXR1cm4gaG9zdE5hbWUgKyBzdHIuc3Vic3RyKGhvc3RXaXRoUG9ydFswXS5sZW5ndGgpOwogIH0KCiAgcmV0dXJuIHN0cjsKfQoKZnVuY3Rpb24gV0JfSXNIb3N0VXJsKHN0cikgewogIC8vIEdvb2QgZ3Vlc3MgdGhhdCdzIGl0cyBhIGhvc3RuYW1lCiAgaWYgKHN0ci5pbmRleE9mKCJ3d3cuIikgPT0gMCkgewogICAgcmV0dXJuIHRydWU7CiAgfQoKICAvLyBob3N0bmFtZTpwb3J0IChwb3J0IHJlcXVpcmVkKQogIHZhciBtYXRjaGVzID0gc3RyLm1hdGNoKC9eW1x3LV0rKFwuW1x3LV9dKykrKDpcZCspKFwvfCQpLyk7CiAgaWYgKG1hdGNoZXMgJiYgKG1hdGNoZXNbMF0ubGVuZ3RoIDwgNjQpKSB7CiAgICByZXR1cm4gdHJ1ZTsKICB9CgogIC8vIGlwOnBvcnQKICBtYXRjaGVzID0gc3RyLm1hdGNoKC9eXGQrXC5cZCtcLlxkK1wuXGQrKDpcZCspPyhcL3wkKS8pOwogIGlmIChtYXRjaGVzICYmIChtYXRjaGVzWzBdLmxlbmd0aCA8IDY0KSkgewogICAgcmV0dXJuIHRydWU7CiAgfQoKICByZXR1cm4gZmFsc2U7Cn0KCmZ1bmN0aW9uIFdCX1Jld3JpdGVVcmwodXJsKSB7CiAgdmFyIGh0dHBQcmVmaXggPSAiaHR0cDovLyI7CiAgdmFyIGh0dHBzUHJlZml4ID0gImh0dHBzOi8vIjsKCiAgaWYgKCF1cmwpIHsKICAgIHJldHVybiB1cmw7CiAgfQoKICAvLyBJZiBub3QgZGVhbGluZyB3aXRoIGEgc3RyaW5nLCBnZXQgc3RyaW5nIHZlcnNpb24gYW5kIHRyeSB0byBjb252ZXJ0IGl0CiAgaWYgKCh0eXBlb2YgdXJsKSAhPSAic3RyaW5nIikgewogICAgdXJsID0gdXJsLnRvU3RyaW5nKCk7CiAgfQoKICAvLyBJZiBzdGFydHMgd2l0aCBwcmVmaXgsIG5vIHJld3JpdGluZyBuZWVkZWQKICAvLyBPbmx5IGNoZWNrIHJlcGxheSBwcmVmaXggKG5vIGRhdGUpIGFzIGRhdGUgbWF5IGJlIGRpZmZlcmVudCBmb3IgZWFjaCBjYXB0dXJlCiAgaWYgKHVybC5pbmRleE9mKFdCX3dvbWJhdF9yZXBsYXlTZXJ2ZXIpID09IDApIHsKICAgIHJldHVybiB1cmw7CiAgfQoKICAvLyBJZiBzZXJ2ZXIgcmVsYXRpdmUgdXJsLCBhZGQgcHJlZml4IGFuZCBvcmlnaW5hbCBob3N0CiAgaWYgKFdCX0lzUmVsYXRpdmVVcmwodXJsKSkgewoKICAgIC8vIEFscmVhZHkgYSByZWxhdGl2ZSB1cmwsIGRvbid0IG1ha2UgYW55IGNoYW5nZXMhCiAgICBpZiAodXJsLmluZGV4T2YoV0Jfd29tYmF0X2NhcHR1cmVEYXRlUGFydCkgPj0gMCkgewogICAgICByZXR1cm4gdXJsOwogICAgfQoKICAgIHJldHVybiBXQl93b21iYXRfcmVwbGF5RGF0ZVByZWZpeCArIFdCX3dvbWJhdF9vcmlnSG9zdCArIHVybDsKICB9CgogIC8vIElmIGZ1bGwgdXJsIHN0YXJ0aW5nIHdpdGggaHR0cDovLyBhZGQgaHR0cCBwcmVmaXgKICBpZiAodXJsLmluZGV4T2YoaHR0cFByZWZpeCkgPT0gMCkgewogICAgcmV0dXJuIFdCX3dvbWJhdF9yZXBsYXlEYXRlUHJlZml4LnJlcGxhY2UoImh0dHBzOi8vIiwgImh0dHA6Ly8iKSArIHVybDsKICB9CgogIC8vIElmIGZ1bGwgdXJsIHN0YXJ0aW5nIHdpdGggaHR
|
||
|
<script type="text/javascript">
|
||
|
WB_wombat_Init("https://web.archive.org/web/", "20160411185424", "coligo.io:80");
|
||
|
</script>
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMAovLyBXYXliYWNrIE1hY2hpbmUgaGFja3MKdmFyIF9fd2JoYWNrID0gbmV3IGZ1bmN0aW9uKCl7CiAgdmFyIHByZWZpeDsKICB2YXIgb3JpZ19kb2N1bWVudCA9IHsKICAgIGNyZWF0ZUVsZW1lbnROUzogZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TCiAgfTsKICB0aGlzLmluaXQgPSBmdW5jdGlvbihfcHJlZml4KSB7CiAgICB0aGlzLmNoZWNrQ29va2llc051bWJlcigpOwogICAgcHJlZml4ID0gX3ByZWZpeDsKICAgIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyA9IGZ1bmN0aW9uKG5zLG5hbWUpIHsKICAgICAgaWYgKG5zLmluZGV4T2YocHJlZml4KT09MCkgewoJbnMgPSBucy5zdWJzdHJpbmcocHJlZml4Lmxlbmd0aCkucmVwbGFjZSgvXC8/WzAtOV0rXC8vLCAnJyk7CiAgICAgIH0KICAgICAgcmV0dXJuIG9yaWdfZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TLmNhbGwodGhpcywgbnMsIG5hbWUpOwogICAgfTsKICB9OwogIHRoaXMuY3JlYXRlQ29va2llID0gZnVuY3Rpb24obmFtZSx2YWx1ZSxkYXlzKSB7CiAgICBpZiAoZGF5cykgewogICAgICAgIHZhciBkYXRlID0gbmV3IERhdGUoKTsKICAgICAgICBkYXRlLnNldFRpbWUoZGF0ZS5nZXRUaW1lKCkrKGRheXMqMjQqNjAqNjAqMTAwMCkpOwogICAgICAgIHZhciBleHBpcmVzID0gIjsgZXhwaXJlcz0iK2RhdGUudG9HTVRTdHJpbmcoKTsKICAgIH0KICAgIGVsc2UgdmFyIGV4cGlyZXMgPSAiIjsKICAgIGRvY3VtZW50LmNvb2tpZSA9IG5hbWUrIj0iK3ZhbHVlK2V4cGlyZXMrIjsgcGF0aD0vIjsKICB9OwogIHRoaXMuZXJhc2VDb29raWUgPSBmdW5jdGlvbihuYW1lKSB7CiAgICB0aGlzLmNyZWF0ZUNvb2tpZShuYW1lLCAiIiwgLTEpOwogIH07CiAgdGhpcy5jaGVja0Nvb2tpZXNOdW1iZXIgPSBmdW5jdGlvbigpIHsKICAgIHZhciBjb29raWVzID0gZG9jdW1lbnQuY29va2llLnNwbGl0KCI7Iik7CiAgICBpZihjb29raWVzLmxlbmd0aCA+IDQwKSB7CiAgICAgICAgZm9yKHZhciBpPTA7IGk8Y29va2llcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICB2YXIgbmFtZSA9IGNvb2tpZXNbaV0uc3BsaXQoIj0iKVswXS50cmltKCk7CiAgICAgICAgICAgIHRoaXMuZXJhc2VDb29raWUobmFtZSk7CiAgICAgICAgfQogICAgfQogIH07CiAgLy8gc2F2ZSBKU09OIG9iamVjdCBmb3Igb3VyIHVzZSAtIHNvbWUgdGFyZ2V0IHBhZ2VzIHJlZGVmaW5lIEpTT04gd2l0aCB0aGVpcgogIC8vIG93biB2ZXJzaW9uIHRoYXQgbWF5IG5vdCBiZSBjb21wYXRpYmxlIHdpdGggKG5vdykgc3RhbmRhcmQgSlNPTi4KICB0aGlzLkpTT04gPSBKU09OOwp9OwovLyBAbGljZW5zZS1lbmQK" charset="utf-8"></script>
|
||
|
<script type="text/javascript">
|
||
|
__wbhack.init('https://web.archive.org/web');
|
||
|
</script>
|
||
|
<link rel="stylesheet" type="text/css" href="data:text/css;base64,QGltcG9ydCAncmVjb3JkLmNzcyc7IC8qIGZvciBTUE4xICovCgojd20taXBwLWJhc2UgewogIG1pbi1oZWlnaHQ6NjVweDsKICBwYWRkaW5nOjA7CiAgbWFyZ2luOjA7CiAgYm9yZGVyOm5vbmU7CiAgYmFja2dyb3VuZDpub25lIHRyYW5zcGFyZW50Owp9CiN3bS1pcHAgewogIHotaW5kZXg6IDIxNDc0ODM2NDA7Cn0KI3dtLWlwcCwgI3dtLWlwcCAqIHsKICBmb250LWZhbWlseTpMdWNpZGEgR3JhbmRlLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmOwogIGZvbnQtc2l6ZToxMnB4OwogIGxpbmUtaGVpZ2h0OjEuMjsKICBsZXR0ZXItc3BhY2luZzowOwogIHdpZHRoOmF1dG87CiAgaGVpZ2h0OmF1dG87CiAgbWF4LXdpZHRoOm5vbmU7CiAgbWF4LWhlaWdodDpub25lOwogIG1pbi13aWR0aDowICFpbXBvcnRhbnQ7CiAgbWluLWhlaWdodDowOwogIG91dGxpbmU6bm9uZTsKICBmbG9hdDpub25lOwogIHRleHQtYWxpZ246bGVmdDsKICBib3JkZXI6bm9uZTsKICBjb2xvcjogIzAwMDsKICB0ZXh0LWluZGVudDogMDsKICBwb3NpdGlvbjogaW5pdGlhbDsKICBiYWNrZ3JvdW5kOiBub25lOwp9CiN3bS1pcHAgZGl2LCAjd20taXBwIGNhbnZhcyB7CiAgZGlzcGxheTogYmxvY2s7Cn0KI3dtLWlwcCBkaXYsICN3bS1pcHAgdHIsICN3bS1pcHAgdGQsICN3bS1pcHAgYSwgI3dtLWlwcCBmb3JtIHsKICBwYWRkaW5nOjA7CiAgbWFyZ2luOjA7CiAgYm9yZGVyOm5vbmU7CiAgYm9yZGVyLXJhZGl1czowOwogIGJhY2tncm91bmQtY29sb3I6dHJhbnNwYXJlbnQ7CiAgYmFja2dyb3VuZC1pbWFnZTpub25lOwogIC8qei1pbmRleDoyMTQ3NDgzNjQwOyovCiAgaGVpZ2h0OmF1dG87Cn0KI3dtLWlwcCB0YWJsZSB7CiAgYm9yZGVyOm5vbmU7CiAgYm9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlOwogIG1hcmdpbjowOwogIHBhZGRpbmc6MDsKICB3aWR0aDphdXRvOwogIGZvbnQtc2l6ZTppbmhlcml0Owp9CiN3bS1pcHAgZm9ybSBpbnB1dCB7CiAgcGFkZGluZzoxcHggIWltcG9ydGFudDsKICBoZWlnaHQ6YXV0bzsKICBkaXNwbGF5OmlubGluZTsKICBtYXJnaW46MDsKICBjb2xvcjogIzAwMDsKICBiYWNrZ3JvdW5kOiBub25lICNmZmY7CiAgYm9yZGVyOiAxcHggc29saWQgIzY2NjsKfQojd20taXBwIGZvcm0gaW5wdXRbdHlwZT1zdWJtaXRdIHsKICBwYWRkaW5nOjAgOHB4ICFpbXBvcnRhbnQ7CiAgbWFyZ2luOjFweCAwIDFweCA1cHggIWltcG9ydGFudDsKICB3aWR0aDphdXRvICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAxcHggc29saWQgIzAwMCAhaW1wb3J0YW50OwogIGJhY2tncm91bmQ6ICNmZmYgIWltcG9ydGFudDsKICBjb2xvcjogIzAwMCAhaW1wb3J0YW50Owp9CiN3bS1pcHAgYSB7CiAgZGlzcGxheTogaW5saW5lOwp9ICAgIAojd20taXBwIGE6aG92ZXJ7CiAgdGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTsKfQojd20taXBwIGEud20tYnRuOmhvdmVyIHsKICB0ZXh0LWRlY29yYXRpb246bm9uZTsKICBjb2xvcjojZmYwICFpbXBvcnRhbnQ7Cn0KI3dtLWlwcCBhLndtLWJ0bjpob3ZlciBzcGFuIHsKICBjb2xvcjojZmYwICFpbXBvcnRhbnQ7Cn0KI3dtLWlwcCAjd20taXBwLWluc2lkZSB7CiAgbWFyZ2luOiAwIDZweDsKICBib3JkZXI6NXB4IHNvbGlkICMwMDA7CiAgYm9yZGVyLXRvcDpub25lOwogIGJhY2tncm91bmQtY29sb3I6cmdiYSgyNTUsMjU1LDI1NSwwLjkpOwogIC1tb3otYm94LXNoYWRvdzoxcHggMXB4IDRweCAjMzMzOwogIC13ZWJraXQtYm94LXNoYWRvdzoxcHggMXB4IDRweCAjMzMzOwogIGJveC1zaGFkb3c6MXB4IDFweCA0cHggIzMzMzsKICBib3JkZXItcmFkaXVzOjAgMCA4cHggOHB4Owp9Ci8qIHNlbGVjdG9ycyBhcmUgaW50ZW50aW9uYWxseSB2ZXJib3NlIHRvIGVuc3VyZSBwcmlvcml0eSAqLwojd20taXBwICN3bS1sb2dvIHsKICBwYWRkaW5nOjAgMTBweDsKICB2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7CiAgbWluLXdpZHRoOjExMHB4OwogIHdpZHRoOjE1JTsKfQojd20taXBwIHRhYmxlIHRyOjpiZWZvcmUsICN3bS1pcHAgdGFibGUgdHI6OmFmdGVyIHsKICBtYXJnaW46IDA7CiAgaGVpZ2h0OiBhdXRvOwp9CiN3bS1pcHAgdGFibGUuYyB7CiAgdmVydGljYWwtYWxpZ246dG9wOwogIG1hcmdpbi1sZWZ0OiA0cHg7Cn0KI3dtLWlwcCB0YWJsZS5jIHRkIHsKICBib3JkZXI6bm9uZSAhaW1wb3J0YW50Owp9CiN3bS1pcHAgLmMgdGQudSB7CiAgcGFkZGluZzozcHggMCAhaW1wb3J0YW50OwogIHRleHQtYWxpZ246Y2VudGVyOwp9CiN3bS1pcHAgLmMgdGQubiB7CiAgcGFkZGluZzowIDAgMCA1cHggIWltcG9ydGFudDsKICB2ZXJ0aWNhbC1hbGlnbjogYm90dG9tOwp9CiN3bS1pcHAgLmMgdGQubiBhIHsKICB0ZXh0LWRlY29yYXRpb246bm9uZTsKICBjb2xvcjojMzNmOwogIGZvbnQtd2VpZ2h0OmJvbGQ7Cn0KI3dtLWlwcCAuYyB0ZC5uIHRkLmIgewogIHBhZGRpbmc6MCA2cHggMCAwICFpbXBvcnRhbnQ7CiAgdGV4dC1hbGlnbjpyaWdodCAhaW1wb3J0YW50OwogIG92ZXJmbG93OnZpc2libGU7CiAgd2hpdGUtc3BhY2U6bm93cmFwOwogIGNvbG9yOiM5OWE7CiAgdmVydGljYWwtYWxpZ246bWlkZGxlOwp9CiN3bS1pcHAgLmMgdGQubiB0ci55IHRkLmIgewogIHBhZGRpbmc6MCA2cHggMnB4IDAgIWltcG9ydGFudDsKfQojd20taXBwIC5jIHRkLm4gdGQuYyB7CiAgYmFja2dyb3VuZDojMDAwOwogIGNvbG9yOiNmZjA7CiAgZm9udC13ZWlnaHQ6Ym9sZDsKICBwYWRkaW5nOjAgIWltcG9ydGFudDsKICB0ZXh0LWFsaWduOmNlbnRlcjsKfQojd20taXBwLmhpIC5jIHRkLm4gdGQuYyB7CiAgY29sb3I6I2VjMDA4YzsKfQojd20taXBwIC5jIHRkLm4gdGQuZiB7CiAgcGFkZGluZzowIDAgMCA2cHggIWltcG9ydGFudDsKICB0ZXh0LWFsaWduOmxlZnQgIWltcG9ydGFudDsKICBvdmVyZmxvdzp2aXNpYmxlOwogIHdoaXRlLXNwYWNlOm5vd3JhcDsKICBjb2xvcjojOTlhOwogIHZlcnRpY2FsLWFsaWduOm1pZGRsZTsKfQojd20taXBwIC5jIHRkLm4gdHIubSB0ZCB7CiAgdGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlOwogI
|
||
|
<link rel="stylesheet" type="text/css" href="data:text/css;base64,QGZvbnQtZmFjZXtmb250LWZhbWlseTonSWNvbm9jaGl2ZS1SZWd1bGFyJztzcmM6dXJsKCJkYXRhOmFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTtiYXNlNjQsdkcwQUFPeHNBQUFCQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFCQUpBQkFBQUFBRXhRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUVBQUFBQUFBQUFnNzJkQndBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUNRQVNRQmpBRzhBYmdCdkFHTUFhQUJwQUhZQVpRQXRBRklBWlFCbkFIVUFiQUJoQUhJQUFBQU9BRklBWlFCbkFIVUFiQUJoQUhJQUFBQVdBRllBWlFCeUFITUFhUUJ2QUc0QUlBQXhBQzRBTUFBQUFDUUFTUUJqQUc4QWJnQnZBR01BYUFCcEFIWUFaUUF0QUZJQVpRQm5BSFVBYkFCaEFISUFBQUFBQUFBQkFBQUFDd0NBQUFNQU1FOVRMekwwMGc2cEFBQUF2QUFBQUdCamJXRndmek4reWdBQUFSd0FBQVkwWjJGemNBQUFBQkFBQUFkUUFBQUFDR2RzZVdaNjVmWkxBQUFIV0FBQVlJaG9aV0ZrQ0Q4SHpnQUFaK0FBQUFBMmFHaGxZUW5wQnNzQUFHZ1lBQUFBSkdodGRIakJsQjR3QUFCb1BBQUFBZGhzYjJOaFJpY3NDQUFBYWhRQUFBRHViV0Y0Y0FDUUFWRUFBR3NFQUFBQUlHNWhiV1VSNks4d0FBQnJKQUFBQWFod2IzTjBBQU1BQUFBQWJNd0FBQUFnQUFNRUFBR1FBQVVBQUFLWkFzd0FBQUNQQXBrQ3pBQUFBZXNBTXdFSkFBQUFBQUFBQUFBQUFBQUFBQUFBb1FBQTVPQUNBQUFBQUFBQUFBQUFBQUFBUUFBQS8vOERnUCtBQUlBRGdBQ0FBQUFBQVFBQUFBQUFBQUFBQUFBQUlBQUFBQUFBQkFBQUFBTUFBQUFrQUFBQUJBQUFBYndBQXdBQkFBQUFKQUFEQUFvQUFBRzhBQVFCbUFBQUFHSUFRQUFGQUNJQUFRQWdBQ3NBTFFBL0FGUUFad0JwQXNNRHNpRzFJZFVoOWlJSUloRWlIaUtWSXA0aTFDTVlJM0FqNUNQcUkvZ2syQ1dtSmE4bHRDVzJKYmdsdmlYQ0pnWW1ZU1psSm1zbWtTYVpKcUFtOWljT0p4QW5GQ2M5SjA0blhTZnovLzMvL3dBQUFBQUFJQUFyQUMwQVB3QlVBR1lBYVFMQ0E3SWh0U0hWSWZZaUNDSVJJaDRpbFNLZUl0UWpGeU53SStRajZTUDRKTmdscGlXdkpiUWx0aVc0SmI0bHdpWUZKbUVtWlNackpwQW1tU2FnSnZZbkRpY1FKeE1uUFNkT0oxMG44Ly85Ly84QUFmL2ovOW4vMlAvSC83UC9vditoL1VuOFc5NVozanJlR3Q0SjNnSGQ5ZDEvM1hmZFF0MEEzS25jTnR3eTNDWGJSdHA1Mm5IYWJkcHMybXZhWnRwajJpSFp4OW5FMmIvWm05bVUyWTdaT2RraTJTSFpIOWozMk9mWTJkaEVBQU1BQVFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF3QUFBQUFCSGdBQUFBQUFBQUFYZ0FBQUFBQUFBQUJBQUFBQVFBQUFDQUFBQUFnQUFBQUF3QUFBQ3NBQUFBckFBQUFCQUFBQUMwQUFBQXRBQUFBQlFBQUFEOEFBQUEvQUFBQUJnQUFBRlFBQUFCVUFBQUFCd0FBQUdZQUFBQm5BQUFBQ0FBQUFHa0FBQUJwQUFBQUNnQUFBc0lBQUFMREFBQUFDd0FBQTdJQUFBT3lBQUFBRFFBQUliVUFBQ0cxQUFBQURnQUFJZFVBQUNIVkFBQUFEd0FBSWZZQUFDSDJBQUFBRUFBQUlnZ0FBQ0lJQUFBQUVRQUFJaEVBQUNJUkFBQUFFZ0FBSWg0QUFDSWVBQUFBRXdBQUlwVUFBQ0tWQUFBQUZBQUFJcDRBQUNLZUFBQUFGUUFBSXRRQUFDTFVBQUFBRmdBQUl4Y0FBQ01ZQUFBQUZ3QUFJM0FBQUNOd0FBQUFHUUFBSStRQUFDUGtBQUFBR2dBQUkra0FBQ1BxQUFBQUd3QUFJL2dBQUNQNEFBQUFIUUFBSk5nQUFDVFlBQUFBSGdBQUphWUFBQ1dtQUFBQUh3QUFKYThBQUNXdkFBQUFJQUFBSmJRQUFDVzBBQUFBSVFBQUpiWUFBQ1cyQUFBQUlnQUFKYmdBQUNXNEFBQUFJd0FBSmI0QUFDVytBQUFBSkFBQUpjSUFBQ1hDQUFBQUpRQUFKZ1VBQUNZR0FBQUFKZ0FBSm1FQUFDWmhBQUFBS0FBQUptVUFBQ1psQUFBQUtRQUFKbXNBQUNackFBQUFLZ0FBSnBBQUFDYVJBQUFBS3dBQUpwa0FBQ2FaQUFBQUxRQUFKcUFBQUNhZ0FBQUFMZ0FBSnZZQUFDYjJBQUFBTHdBQUp3NEFBQ2NPQUFBQU1BQUFKeEFBQUNjUUFBQUFNUUFBSnhNQUFDY1VBQUFBTWdBQUp6MEFBQ2M5QUFBQU5BQUFKMDRBQUNkT0FBQUFOUUFBSjEwQUFDZGRBQUFBTmdBQUovTUFBQ2Z6QUFBQU53QUIxTjhBQWRUZkFBQUFPQUFCODRFQUFmT0JBQUFBT1FBQjg1WUFBZk9XQUFBQU9nQUI4NTRBQWZPZUFBQUFPd0FCODZRQUFmT2tBQUFBUEFBQjg2Y0FBZk9uQUFBQVBRQUI4NndBQWZPc0FBQUFQZ0FCODlzQUFmUGJBQUFBUHdBQjlDWUFBZlFtQUFBQVFBQUI5RUVBQWZSQkFBQUFRUUFCOUdRQUFmUmxBQUFBUWdBQjlIMEFBZlI5QUFBQVJBQUI5S0VBQWZTaEFBQUFSUUFCOUtVQUFmU2xBQUFBUmdBQjlMQUFBZlN3QUFBQVJ3QUI5TDRBQWZTL0FBQUFTQUFCOU1JQUFmVENBQUFBU2dBQjlNZ0FBZlRJQUFBQVN3QUI5TkVBQWZUUkFBQUFUQUFCOU5vQUFmVGFBQUFBVFFBQjlPUUFBZlRsQUFBQVRnQUI5T2NBQWZUb0FBQUFVQUFCOVBBQUFmVHdBQUFBVWdBQjlQWUFBZlQyQUFBQVV3QUI5UG9BQWZUOEFBQUFWQUFCOVFBQUFmVUJBQUFBVndBQjlRY0FBZlVLQUFBQVdRQUI5UTBBQWZVTkFBQUFYUUFCOVJJQUFmVVRBQUFBWGdBQjlSY0FBZlVYQUFBQVlBQUI5U1FBQWZVbEFBQUFZUUFCOVZFQUFmVlJBQUFBWXdBQjlXZ0FBZlZvQUFBQVpBQUI5VzRBQWZWdUFBQUFaUUFCOVhZQUFmVjJBQUFBWmdBQjlYa0FBZlY1QUFBQVp3QUI5Yk1BQWZXekFBQUFhQUFCOWJ3QUFmVzhBQUFBYVFBQjljTUFBZlhEQUFBQWFnQUI5YzhBQWZYUEFBQUFhd0FCOWRFQUFmWFJBQUFBYkFBQjlkUUFBZlhXQUFBQWJRQUI5ZGtBQWZYWkFBQUFjQUFCOWVrQUFmWHFBQUFBY1FBQjlmSUFBZlh5QUFBQWN3QUI5Zm9BQWZYNkFBQUFkQUFCOXFNQUFmYWpBQUFBZFFBQkFBSC8vd0FQQUFFQUFBQUFBQUFBQUFBQ0FBQTNPUUVBQUFBQUFRQUFBQUFBQUFBQUFBSUFBRGM1QVFBQUFBQUJBQUFBQUFBQUFBQUFBZ0FBTnprQkFBQUFBQU1BSi8rNUE4b0RRZ0FjQUMwQU9nQUFQd0VtTlRRM05qYzJNeklYRmhVVUJ3WWpJaWNIQmlNaUp5WTFORGNsTWpjM
|
||
|
<!-- End Wayback Rewrite JS Include -->
|
||
|
|
||
|
<meta name="google-site-verification" content="sSps0lkjLyTuJdOQsgXDCtpXc0uw9HHaX3aMMSajTxw">
|
||
|
<title>Creating a URL Shortener with NodeJs, Express, and MongoDB | coligo</title>
|
||
|
<link rel="icon" type="image/png" href="data:text/html; charset=UTF-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGRhdGEtYWRibG9ja2tleT0iTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBTHF1REZFVFhSbjBIcjA1ZlVQN0VKVDc3eFluUG1SYnBNeTR2azhLWWlIbmtOcGVkbmpPQU5KY2FYRFhjS1FKTjBuWEtaSkw3VGNpSkQ4QW9IWEsxNThDQXdFQUFRPT1fVjdHamFMcTU4ZU9jem9xclBDanFUV3NJRnc3ZnFMZjI4VXNoWnNLcW1ESlpoeDRJK2xyUGlaYkNhcFZIUk9DU01UM1NudDZ1RDdGMmU1MUZzd0FVb3c9PSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHhtbDpsYW5nPSJlbiIgbGFuZz0iZW4iPgo8aGVhZD4KCTxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04Ii8+CiAgICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLCBzaHJpbmstdG8tZml0PW5vIiAvPgogICAgPHRpdGxlPmNvbGlnby5pbzwvdGl0bGU+Cgk8c2NyaXB0IHNyYz0iLy93d3cuZ29vZ2xlLmNvbS9hZHNlbnNlL2RvbWFpbnMvY2FmLmpzIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiID48L3NjcmlwdD4KCTxsaW5rIGhyZWY9Ii8vZDFseGhjNGp2c3R6cnAuY2xvdWRmcm9udC5uZXQvdGhlbWVzL2Fzc2V0cy9zdHlsZS5jc3MiIHJlbD0ic3R5bGVzaGVldCIgdHlwZT0idGV4dC9jc3MiIG1lZGlhPSJzY3JlZW4iIC8+Cgk8bGluayBocmVmPSIvL2QxbHhoYzRqdnN0enJwLmNsb3VkZnJvbnQubmV0L3RoZW1lcy9jbGVhblBlcHBlcm1pbnRfZjE1ZTM5YzYvc3R5bGUuY3NzIiByZWw9InN0eWxlc2hlZXQiIHR5cGU9InRleHQvY3NzIiBtZWRpYT0ic2NyZWVuIiAvPgoJPGxpbmsgaHJlZj0iaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3M/ZmFtaWx5PVBvcHBpbnM6MzAwIiByZWw9InN0eWxlc2hlZXQiPgoJPG1ldGEgbmFtZT0iZGVzY3JpcHRpb24gIiBjb250ZW50PSJUaGlzIGRvbWFpbiBtYXkgYmUgZm9yIHNhbGUhIiAvPgoJPC9oZWFkPgoKPGJvZHkgaWQ9ImFmZCIgc3R5bGU9InZpc2liaWxpdHk6aGlkZGVuIj4KCgoKPGRpdiBjbGFzcz0id3JhcHBlcjEiPgoJCjxzdHlsZT4KICAgIC5zYWxlX2Jhbm5lcl9ncmF5IHsKICAgICAgICBiYWNrZ3JvdW5kOiM3MDZiNjc7CiAgICAgICAgYmFja2dyb3VuZDogLW1vei1saW5lYXItZ3JhZGllbnQodG9wLCAjODE3Yzc4IDAlLCAjNWQ1ODU0IDEwMCUpOwogICAgICAgIGJhY2tncm91bmQ6IC13ZWJraXQtbGluZWFyLWdyYWRpZW50KHRvcCwgIzgxN2M3OCAwJSwjNWQ1ODU0IDEwMCUpOwogICAgICAgIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCh0byBib3R0b20sICM4MTdjNzggMCUsIzVkNTg1NCAxMDAlKTsKICAgICAgICBmaWx0ZXI6IHByb2dpZDpEWEltYWdlVHJhbnNmb3JtLk1pY3Jvc29mdC5ncmFkaWVudCggc3RhcnRDb2xvcnN0cj0nIzgxN2M3OCcsIGVuZENvbG9yc3RyPScjNWQ1ODU0JyxHcmFkaWVudFR5cGU9MCApOwogICAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZWVlOwogICAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZWVlOwogICAgICAgIGNvbG9yOiAjYzhjOGM4OwogICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICBmb250OiBib2xkIDE2cHgvMzZweCBzYW5zLXNlcmlmOwogICAgICAgIGhlaWdodDogMzZweDsKICAgIH0KCiAgICAuc2FsZV9iYW5uZXJfZ3JheSBhIHsKICAgICAgICBkaXNwbGF5OiBibG9jazsKICAgICAgICBjb2xvcjojZmZmOwogICAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgIH0KPC9zdHlsZT4KCgoKPGRpdiBjbGFzcz0ic2FsZV9iYW5uZXJfZ3JheSIgc3R5bGU9ImJvcmRlci10b3A6bm9uZSI+CgkgICAgICAgIDxhIGhyZWY9Imh0dHBzOi8vd3d3Lm15ZG9tYWluY29udGFjdC5jb20vaW5kZXgucGhwP2RvbWFpbl9uYW1lPWNvbGlnby5pbyIgdGFyZ2V0PSJfYmxhbmsiPgoJICAgICAgICBCdXkgdGhpcyBkb21haW4uCgk8L2E+CjwvZGl2PiAgICA8ZGl2IGNsYXNzPSJ3cmFwcGVyMiI+CiAgICAgICAgPGRpdiBjbGFzcz0id3JhcHBlcjMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJoZWFkZXIiPgogICAgICAgIAkJPGgxPmNvbGlnby5pbzwvaDE+CiAgICAgICAgCTwvZGl2PgogICAgICAgICAgICAKICAgICAgICAgICAgPGRpdiBjbGFzcz0idGNIb2xkZXIiPgogICAgICAgICAgICAJPGRpdiBpZD0idGMiPjwvZGl2PgoJCQkJCQkJCTxkaXYgY2xhc3M9InNlYXJjaEhvbGRlciI+CgkJCQkJPGRpdiBpZD0ic2VhcmNoIj48L2Rpdj4KCQkJCTwvZGl2PgoJCQkJICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj4KCTxkaXYgY2xhc3M9ImZvb3RlciI+CgkJPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgoKICAgIGZ1bmN0aW9uIHNob3dJbXByaW50KCl7CgogICAgICAgIHZhciBpbXByaW50d25kID0gd2luZG93Lm9wZW4oJycsJ3BjcmV3X2ltcHJpbnQnLCd3aWR0aD02NDAsaGVpZ2h0PTQ4MCxsZWZ0PTIwMCx0b3A9MjAwLG1lbnViYXI9bm8sc3RhdHVzPXllcyx0b29sYmFyPW5vJyk7CiAgICAgICAgaW1wcmludHduZC5kb2N1bWVudC53cml0ZWxuKCIiKTsKICAgICAgICBpbXByaW50d25kLmRvY3VtZW50LmNsb3NlKCk7CiAgICB9CgogICAgZnVuY3Rpb24gc2hvd1BvbGljeSgpewogICAgICAgIHZhciBsaW5rID0gJ3d3dy5wYXJraW5nY3Jldy5uZXQnOwogICAgICAgIHBvbGljeXduZCA9IHdpbmRvdy5vcGVuKAogICAgICAgICAgICAgICAgJ2h0dHA6Ly8nICsgbGluayArICcvcHJpdmFjeS5odG1sJywncGNyZXdfcG9saWN5Jywnd2lkdGg9ODkwLGhlaWdodD0zMzAsbGVmdD0yMDAsdG9wPTIwMCxtZW51YmFyPW5vLHN0YXR1cz15ZXMsdG9vbGJhcj1ubycpOwogICAgICAgIHBvbGljeXduZC5mb2N1cygpOwogICAgfQoKICAgIGZ1bmN0aW9uIHNob3dBYm91dFVzKCl7CiAgICAgICAgdmFyIGxpbmsgPSAnaHR0cDovLycrZG9jdW1lbn
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
||
|
<meta name="author" content="Fady Makram">
|
||
|
<meta name="description" content="Learn how to build a URL shortening service similar to sites like bitly.com">
|
||
|
<link rel="canonical" href="https://web.archive.org/web/20160411185424/http://coligo.io/create-url-shortener-with-node-express-mongo/">
|
||
|
|
||
|
<meta itemprop="name" content="Creating a URL Shortener with NodeJs, Express, and MongoDB">
|
||
|
<meta itemprop="description" content="Learn how to build a URL shortening service similar to sites like bitly.com">
|
||
|
<meta itemprop="image" content="https://web.archive.org/web/20160411185424im_/http://coligo.io/create-url-shortener-with-node-express-mongo/cover.jpg">
|
||
|
|
||
|
<meta property="og:url" content="https://web.archive.org/web/20160411185424/http://coligo.io/create-url-shortener-with-node-express-mongo/">
|
||
|
<meta property="og:type" content="article">
|
||
|
<meta property="og:title" content="Creating a URL Shortener with NodeJs, Express, and MongoDB">
|
||
|
<meta property="og:image" content="https://web.archive.org/web/20160411185424im_/http://coligo.io/create-url-shortener-with-node-express-mongo/cover.jpg">
|
||
|
<meta property="og:description" content="Learn how to build a URL shortening service similar to sites like bitly.com">
|
||
|
<meta property="og:site_name" content="Coligo">
|
||
|
<meta property="article:publisher" content="https://www.facebook.com/coligo.io">
|
||
|
|
||
|
<meta name="twitter:card" content="summary_large_image">
|
||
|
<meta name="twitter:site" content="@coligo_io">
|
||
|
<meta name="twitter:creator" content="@coligo_io">
|
||
|
<meta name="twitter:title" content="Creating a URL Shortener with NodeJs, Express, and MongoDB">
|
||
|
<meta name="twitter:description" content="Learn how to build a URL shortening service similar to sites like bitly.com">
|
||
|
<meta name="twitter:image" content="https://web.archive.org/web/20160411185424im_/http://coligo.io/create-url-shortener-with-node-express-mongo/cover.jpg">
|
||
|
<link rel="stylesheet" type="text/css" href="data:text/css;base64,Kiw6YWZ0ZXIsOmJlZm9yZXtib3gtc2l6aW5nOmluaGVyaXR9aHRtbHtib3gtc2l6aW5nOmJvcmRlci1ib3g7Zm9udC1mYW1pbHk6c2Fucy1zZXJpZjstbXMtdGV4dC1zaXplLWFkanVzdDoxMDAlOy13ZWJraXQtdGV4dC1zaXplLWFkanVzdDoxMDAlfWlucHV0W3R5cGU9ZW1haWxdLGlucHV0W3R5cGU9cGFzc3dvcmRdLGlucHV0W3R5cGU9c2VhcmNoXSxpbnB1dFt0eXBlPXRleHRdey13ZWJraXQtYXBwZWFyYW5jZTpub25lOy1tb3otYXBwZWFyYW5jZTpub25lfWJvZHl7bWFyZ2luOjB9YXJ0aWNsZSxhc2lkZSxkZXRhaWxzLGZpZ2NhcHRpb24sZmlndXJlLGZvb3RlcixoZWFkZXIsaGdyb3VwLG1haW4sbmF2LHNlY3Rpb24sc3VtbWFyeXtkaXNwbGF5OmJsb2NrfWF1ZGlvLGNhbnZhcyxwcm9ncmVzcyx2aWRlb3tkaXNwbGF5OmlubGluZS1ibG9jazt2ZXJ0aWNhbC1hbGlnbjpiYXNlbGluZX1hdWRpbzpub3QoW2NvbnRyb2xzXSl7ZGlzcGxheTpub25lO2hlaWdodDowfVtoaWRkZW5dLHRlbXBsYXRle2Rpc3BsYXk6bm9uZX1he2JhY2tncm91bmQ6MCAwfWE6YWN0aXZlLGE6aG92ZXJ7b3V0bGluZTowfWFiYnJbdGl0bGVde2JvcmRlci1ib3R0b206MXB4IGRvdHRlZH1iLG9wdGdyb3VwLHN0cm9uZ3tmb250LXdlaWdodDo3MDB9ZGZue2ZvbnQtc3R5bGU6aXRhbGljfWgxe2ZvbnQtc2l6ZToyZW07bWFyZ2luOi42N2VtIDB9bWFya3tiYWNrZ3JvdW5kOiNmZjA7Y29sb3I6IzAwMH1zbWFsbHtmb250LXNpemU6ODAlfXN1YixzdXB7Zm9udC1zaXplOjc1JTtsaW5lLWhlaWdodDowO3Bvc2l0aW9uOnJlbGF0aXZlO3ZlcnRpY2FsLWFsaWduOmJhc2VsaW5lfXN1cHt0b3A6LS41ZW19c3Vie2JvdHRvbTotLjI1ZW19aW1ne2JvcmRlcjowfXN2Zzpub3QoOnJvb3Qpe292ZXJmbG93OmhpZGRlbn1maWd1cmV7bWFyZ2luOjFlbSA0MHB4fWhye2JveC1zaXppbmc6Y29udGVudC1ib3g7aGVpZ2h0OjB9cHJlLHRleHRhcmVhe292ZXJmbG93OmF1dG99Y29kZSxrYmQscHJlLHNhbXB7Zm9udC1mYW1pbHk6bW9ub3NwYWNlLG1vbm9zcGFjZTtmb250LXNpemU6MWVtfWJ1dHRvbixpbnB1dCxvcHRncm91cCxzZWxlY3QsdGV4dGFyZWF7Y29sb3I6aW5oZXJpdDtmb250OmluaGVyaXQ7bWFyZ2luOjB9YnV0dG9ue292ZXJmbG93OnZpc2libGV9YnV0dG9uLHNlbGVjdHt0ZXh0LXRyYW5zZm9ybTpub25lfWJ1dHRvbixodG1sIGlucHV0W3R5cGU9YnV0dG9uXSxpbnB1dFt0eXBlPXJlc2V0XSxpbnB1dFt0eXBlPXN1Ym1pdF17LXdlYmtpdC1hcHBlYXJhbmNlOmJ1dHRvbjtjdXJzb3I6cG9pbnRlcn1idXR0b25bZGlzYWJsZWRdLGh0bWwgaW5wdXRbZGlzYWJsZWRde2N1cnNvcjpkZWZhdWx0fWJ1dHRvbjo6LW1vei1mb2N1cy1pbm5lcixpbnB1dDo6LW1vei1mb2N1cy1pbm5lcntib3JkZXI6MDtwYWRkaW5nOjB9aW5wdXR7bGluZS1oZWlnaHQ6bm9ybWFsfWlucHV0W3R5cGU9Y2hlY2tib3hdLGlucHV0W3R5cGU9cmFkaW9de2JveC1zaXppbmc6Ym9yZGVyLWJveDtwYWRkaW5nOjB9aW5wdXRbdHlwZT1udW1iZXJdOjotd2Via2l0LWlubmVyLXNwaW4tYnV0dG9uLGlucHV0W3R5cGU9bnVtYmVyXTo6LXdlYmtpdC1vdXRlci1zcGluLWJ1dHRvbntoZWlnaHQ6YXV0b31pbnB1dFt0eXBlPXNlYXJjaF17LXdlYmtpdC1hcHBlYXJhbmNlOnRleHRmaWVsZDtib3gtc2l6aW5nOmNvbnRlbnQtYm94fWlucHV0W3R5cGU9c2VhcmNoXTo6LXdlYmtpdC1zZWFyY2gtY2FuY2VsLWJ1dHRvbixpbnB1dFt0eXBlPXNlYXJjaF06Oi13ZWJraXQtc2VhcmNoLWRlY29yYXRpb257LXdlYmtpdC1hcHBlYXJhbmNlOm5vbmV9ZmllbGRzZXR7Ym9yZGVyOjFweCBzb2xpZCBzaWx2ZXI7bWFyZ2luOjAgMnB4O3BhZGRpbmc6LjM1ZW0gLjYyNWVtIC43NWVtfWxlZ2VuZHtib3JkZXI6MDtwYWRkaW5nOjB9dGFibGV7Ym9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2JvcmRlci1zcGFjaW5nOjB9dGQsdGh7cGFkZGluZzowfQoudWkuYnV0dG9ue2N1cnNvcjpwb2ludGVyO2Rpc3BsYXk6aW5saW5lLWJsb2NrO21pbi1oZWlnaHQ6MWVtO291dGxpbmU6MDtib3JkZXI6bm9uZTt2ZXJ0aWNhbC1hbGlnbjpiYXNlbGluZTtiYWNrZ3JvdW5kOiNlMGUxZTI7Y29sb3I6cmdiYSgwLDAsMCwuNik7Zm9udC1mYW1pbHk6TGF0byxIZWx2ZXRpY2EgTmV1ZSxBcmlhbCxIZWx2ZXRpY2Esc2Fucy1zZXJpZjttYXJnaW46MCAuMjVlbSAwIDA7cGFkZGluZzouNzg1NzE0MjllbSAxLjVlbTt0ZXh0LXRyYW5zZm9ybTpub25lO3RleHQtc2hhZG93Om5vbmU7Zm9udC13ZWlnaHQ6NzAwO2xpbmUtaGVpZ2h0OjFlbTtmb250LXN0eWxlOm5vcm1hbDt0ZXh0LWFsaWduOmNlbnRlcjt0ZXh0LWRlY29yYXRpb246bm9uZTtib3JkZXItcmFkaXVzOi4yODU3MTQyOXJlbTstd2Via2l0LXVzZXItc2VsZWN0Om5vbmU7LW1vei11c2VyLXNlbGVjdDpub25lOy1tcy11c2VyLXNlbGVjdDpub25lO3VzZXItc2VsZWN0Om5vbmU7LXdlYmtpdC10cmFuc2l0aW9uOm9wYWNpdHkgLjFzIGVhc2UsYmFja2dyb3VuZC1jb2xvciAuMXMgZWFzZSxjb2xvciAuMXMgZWFzZSxib3gtc2hhZG93IC4xcyBlYXNlLGJhY2tncm91bmQgLjFzIGVhc2U7dHJhbnNpdGlvbjpvcGFjaXR5IC4xcyBlYXNlLGJhY2tncm91bmQtY29sb3IgLjFzIGVhc2UsY29sb3IgLjFzIGVhc2UsYm94LXNoYWRvdyAuMXMgZWFzZSxiYWNrZ3JvdW5kIC4xcyBlYXNlO3dpbGwtY2hhbmdlOicnOy13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjp0cmFuc3BhcmVudH0udWkuYnV0dG9uLC51aS5idXR0b246aG92ZXJ7Ym94LXNoYWRvdzppbnNldCAwIDAgMCAxcHggdHJhbnNwYXJlbnQsaW5zZXQgMCAwIDAgMCByZ2JhKDM0LDM2LDM4LC4xNSl9LnVpLmJ1dHRvbjpob3ZlcntiYWNrZ3JvdW5kLWNvbG9yOiNjYWNiY2Q7YmFja2dyb3VuZC1pbWFnZTpub25lO2NvbG9yOnJnYmEoMCwwLDAsLjgpfS51aS5idXR0b246aG92ZXIgLmljb257b3BhY2l0eTouODV9LnVpLmJ1dHRvbjpmb2N1c3tiYWNrZ3JvdW5kLWNvbG9yOiNjYWNiY2Q7Y29sb3I6cmdiYSgwLDAsMCwuOCk7YmFja
|
||
|
</head>
|
||
|
<body><!-- BEGIN WAYBACK TOOLBAR INSERT -->
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMAovKiogdGltZXN0YW1wIG5hbWVzcGFjZWQgbWV0aG9kcyAqKi8KdmFyIF9fd2JUcyA9IChmdW5jdGlvbigpIHsKICAgIGZ1bmN0aW9uIF9zcGxpdF90aW1lc3RhbXAodGltZXN0YW1wKSB7CiAgICAgICAgaWYodHlwZW9mIHRpbWVzdGFtcCA9PSAibnVtYmVyIikgewogICAgICAgICAgICB0aW1lc3RhbXAgPSB0aW1lc3RhbXAudG9TdHJpbmcoKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIFsKICAgICAgICAgICAgdGltZXN0YW1wLnNsaWNlKC0xNCwgLTEwKSwKICAgICAgICAgICAgdGltZXN0YW1wLnNsaWNlKC0xMCwgLTgpLAogICAgICAgICAgICB0aW1lc3RhbXAuc2xpY2UoLTgsIC02KSwKICAgICAgICAgICAgdGltZXN0YW1wLnNsaWNlKC02LCAtNCksCiAgICAgICAgICAgIHRpbWVzdGFtcC5zbGljZSgtNCwgLTIpLAogICAgICAgICAgICB0aW1lc3RhbXAuc2xpY2UoLTIpCiAgICAgICAgXTsKICAgIH0KICAgIHZhciBNT05USFNfTE9ORyA9IFsKCSJKYW51YXJ5IiwgIkZlYnJ1YXJ5IiwgIk1hcmNoIiwgIkFwcmlsIiwgIk1heSIsICJKdW5lIiwKCSJKdWx5IiwgIkF1Z3VzdCIsICJTZXB0ZW1iZXIiLCAiT2N0b2JlciIsICJOb3ZlbWJlciIsICJEZWNlbWJlciIKICAgIF07CiAgICB2YXIgTU9OVEhTX1NIT1JUID0gWwoJIkphbiIsICJGZWIiLCAiTWFyIiwgIkFwciIsICJNYXkiLCAiSnVuIiwgIkp1bCIsICJBdWciLCAiU2VwIiwKCSJPY3QiLCAiTm92IiwgIkRlYyIKICAgIF07CiAgICB2YXIgRklFTERTID0gewoJJ1knOiBmdW5jdGlvbihkKSB7IHJldHVybiBkLmdldFVUQ0Z1bGxZZWFyKCkgfSwKCSdtJzogZnVuY3Rpb24oZCkgeyByZXR1cm4gZC5nZXRVVENNb250aCgpICsgMSB9LAoJJ2InOiBmdW5jdGlvbihkKSB7IHJldHVybiBNT05USFNfU0hPUlRbZC5nZXRVVENNb250aCgpXSB9LAoJJ0InOiBmdW5jdGlvbihkKSB7IHJldHVybiBNT05USFNfTE9OR1tkLmdldFVUQ01vbnRoKCldIH0sCgknZCc6IGZ1bmN0aW9uKGQpIHsgcmV0dXJuIGQuZ2V0VVRDRGF0ZSgpIH0sCgknSCc6IGZ1bmN0aW9uKGQpIHsgcmV0dXJuICgnMCcrZC5nZXRVVENIb3VycygpKS5zbGljZSgtMikgfSwKCSdNJzogZnVuY3Rpb24oZCkgeyByZXR1cm4gKCcwJytkLmdldFVUQ01pbnV0ZXMoKSkuc2xpY2UoLTIpIH0sCgknUyc6IGZ1bmN0aW9uKGQpIHsgcmV0dXJuICgnMCcrZC5nZXRVVENTZWNvbmRzKCkpLnNsaWNlKC0yKSB9LAoJJyUnOiBmdW5jdGlvbigpIHsgcmV0dXJuICclJyB9CiAgICB9OwogICAgZnVuY3Rpb24gdGltZXN0YW1wMmRhdGV0aW1lKHRpbWVzdGFtcCkgewogICAgICAgICAgICB2YXIgdHNfYXJyYXkgPSBfc3BsaXRfdGltZXN0YW1wKHRpbWVzdGFtcCk7CiAgICAgICAgICAgIHJldHVybiBuZXcgRGF0ZShEYXRlLlVUQygKCQl0c19hcnJheVswXSwgdHNfYXJyYXlbMV0tMSwgdHNfYXJyYXlbMl0sCgkJdHNfYXJyYXlbM10sIHRzX2FycmF5WzRdLCB0c19hcnJheVs1XQoJICAgICkpOwogICAgfQogICAgcmV0dXJuIHsKCXRpbWVzdGFtcDJkYXRldGltZTogdGltZXN0YW1wMmRhdGV0aW1lLAoJZ2V0TW9udGhOYW1lOiBmdW5jdGlvbihtb24pIHsKCSAgICByZXR1cm4gTU9OVEhTX0xPTkdbbW9uXTsKCX0sCglmb3JtYXQ6IGZ1bmN0aW9uKHRpbWVzdGFtcCwgZm10KSB7CgkgICAgcmV0dXJuIGZtdC5yZXBsYWNlKC8lLi9nLCBmdW5jdGlvbihwaCkgewoJCXZhciBmaWVsZCA9IEZJRUxEU1twaFsxXV07CgkJcmV0dXJuIGZpZWxkID8gZmllbGQodGltZXN0YW1wMmRhdGV0aW1lKHRpbWVzdGFtcCkpIDogcGg7CgkgICAgfSk7Cgl9CiAgICB9Cn0pKCk7Ci8vIEBsaWNlbnNlLWVuZAo=" charset="utf-8"></script>
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMApmdW5jdGlvbiBnZXRFdmVudFgoZXZlbnQpIHsKCXZhciBwb3N4ID0gMDsKCWlmIChldmVudC5wYWdlWCB8fCBldmVudC5wYWdlWSkgewoJCXBvc3ggPSAgZXZlbnQucGFnZVg7Cgl9CgllbHNlIGlmIChldmVudC5jbGllbnRYIHx8IGV2ZW50LmNsaWVudFkpIAl7CgkJcG9zeCA9IGV2ZW50LmNsaWVudFggKyBkb2N1bWVudC5ib2R5LnNjcm9sbExlZnQKCQkJKyBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsTGVmdDsKCX0KCXJldHVybiBwb3N4Owp9CmZ1bmN0aW9uIGdldEVsZW1lbnRYKG9iaikgewoJdmFyIHggPSAwOwoJaWYgKG9iai5vZmZzZXRQYXJlbnQpIHsKCQlkbyB7CgkJCXggKz0gb2JqLm9mZnNldExlZnQ7CgkJfSB3aGlsZSAob2JqID0gb2JqLm9mZnNldFBhcmVudCk7Cgl9CglyZXR1cm4geDsKfQpmdW5jdGlvbiB6ZXJvUGFkKHYsIGxlbikgewogIHYgPSB2LnRvU3RyaW5nKCk7CiAgcmV0dXJuIHYubGVuZ3RoID49IGxlbiA/IHYgOiAiMDAwMDAwMDAiLnN1YnN0cmluZygwLCBsZW4gLSB2Lmxlbmd0aCkgKyB2Owp9CgovKioKICogQ2hlY2sgaWYgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWF4IGFuZCB0aGUgbWluIG5vbiB6ZXJvIGNhcHR1cmUgbnVtYmVycwogKiBpcyBsYXJnZXIgdGhhbiAzIG9yZGVycyBvZiBtYWduaXR1ZGUuIElmIHllcywgd2UgbmVlZCB0byBzY2FsZS4KKiovCmZ1bmN0aW9uIGNhcHR1cmVncmFwaF9zY2FsZV9pc19yZXF1aXJlZChjYXB0dXJlcykgewogICAgdmFyIG1heCA9IDA7CiAgICB2YXIgbWluID0gMTAwMDsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FwdHVyZXMubGVuZ3RoOyBpKyspIHsKCXZhciB5ZWFyID0gY2FwdHVyZXNbaV07CiAgICAgICAgbWF4ID0gTWF0aC5tYXgobWF4LCBNYXRoLm1heC5hcHBseShudWxsLCB5ZWFyWzFdKSk7CiAgICAgICAgbWluID0gTWF0aC5taW4obWluLCBNYXRoLm1pbi5hcHBseShudWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhclsxXS5maWx0ZXIoQm9vbGVhbikpKTsKICAgIH0KICAgIHJldHVybiAoTWF0aC5sb2cxcChtYXgpIC0gTWF0aC5sb2cxcChtaW4pID4gMyk7Cn0KCi8qKgogKiBTY2FsZSBjYXB0dWdyYXBoIGNvdW50cyBhbmQgbWF4IG1heGNvdW50IHVzaW5nIGxvZzFwIGlmIG5lY2Vzc2FyeS4KICovCmZ1bmN0aW9uIGNhcHR1cmVncmFwaF9zY2FsZShjYXB0dXJlcykgewogICAgdmFyIG1heGNvdW50ID0gMDsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FwdHVyZXMubGVuZ3RoOyBpKyspIHsKCW1heGNvdW50ID0gTWF0aC5tYXgobWF4Y291bnQsIE1hdGgubWF4LmFwcGx5KG51bGwsIGNhcHR1cmVzW2ldWzFdKSk7CiAgICB9CiAgICBpZiAoY2FwdHVyZWdyYXBoX3NjYWxlX2lzX3JlcXVpcmVkKGNhcHR1cmVzKSkgewoJdmFyIHNjYWxlZCA9IFtdOwoJZm9yICh2YXIgaSA9IDA7IGkgPCBjYXB0dXJlcy5sZW5ndGg7IGkrKykgewoJICAgIHZhciB5ZWFyID0gY2FwdHVyZXNbaV07CgkgICAgLy8gWFhYIG1hcCBtYXkgbm90IGJlIGF2YWlsYWJsZSBvbiBhbGwgcGxhdGZvcm1zCgkgICAgc2NhbGVkLnB1c2goW3llYXJbMF0sIHllYXJbMV0ubWFwKE1hdGgubG9nMXApXSk7Cgl9CgljYXB0dXJlcyA9IHNjYWxlZDsKCW1heGNvdW50ID0gTWF0aC5sb2cxcChtYXhjb3VudCk7CiAgICB9CiAgICByZXR1cm4gW2NhcHR1cmVzLCBtYXhjb3VudF07Cn0KCi8qKgogKiBEcmF3IHllYXJzLCBoaWdobGlnaHQgY3VycmVudCB5ZWFyLCBkcmF3IGNhcHR1cmUgZnJlcXVlbmN5IHBlciBtb250aAogKi8KZnVuY3Rpb24gc3BhcmtsaW5lKGNhcHR1cmVzLCB3aWR0aCwgaGVpZ2h0LCBjYW52YXMsIHN0YXJ0X3llYXIsCiAgICAgICAgICAgICAgICAgICBjdXJfeWVhciwgY3VyX21vbnRoKSB7CiAgICB2YXIgY3R4ID0gY2FudmFzLmdldENvbnRleHQoIjJkIik7CiAgICBpZiAoIWN0eCkgcmV0dXJuOwogICAgY3R4LmZpbGxTdHlsZSA9ICIjRkZGIjsKICAgIHZhciBlbmRfeWVhciA9IG5ldyBEYXRlKCkuZ2V0VVRDRnVsbFllYXIoKTsKICAgIHZhciB5ZWFyX3dpZHRoID0gd2lkdGggLyAoZW5kX3llYXIgLSBzdGFydF95ZWFyICsgMSk7CgogICAgdmFyIHNjYWxlZCA9IGNhcHR1cmVncmFwaF9zY2FsZShjYXB0dXJlcy55ZWFycyk7CiAgICB2YXIgeWVhcnMgPSBzY2FsZWRbMF07CiAgICB2YXIgbWF4Y291bnQgPSBzY2FsZWRbMV07CgogICAgdmFyIHlzY2FsZSA9IGhlaWdodCAvIG1heGNvdW50OwoKICAgIGZ1bmN0aW9uIHllYXJfbGVmdCh5ZWFyKSB7CglyZXR1cm4gTWF0aC5jZWlsKCh5ZWFyIC0gc3RhcnRfeWVhcikgKiB5ZWFyX3dpZHRoKSArIDAuNTsKICAgIH0KCiAgICBpZiAoY3VyX3llYXIgPj0gc3RhcnRfeWVhcikgewoJdmFyIHggPSB5ZWFyX2xlZnQoY3VyX3llYXIpOwoJY3R4LmZpbGxTdHlsZSA9ICIjRkZGRkE1IjsKCWN0eC5maWxsUmVjdCh4LCAwLCB5ZWFyX3dpZHRoLCBoZWlnaHQpOwogICAgfQogICAgZm9yICh2YXIgeWVhciA9IHN0YXJ0X3llYXI7IHllYXIgPD0gZW5kX3llYXI7IHllYXIrKykgewoJdmFyIHggPSB5ZWFyX2xlZnQoeWVhcik7CiAgICAgICAgY3R4LmJlZ2luUGF0aCgpOwogICAgICAgIGN0eC5tb3ZlVG8oeCwgMCk7CiAgICAgICAgY3R4LmxpbmVUbyh4LCBoZWlnaHQpOwogICAgICAgIGN0eC5saW5lV2lkdGggPSAxOwogICAgICAgIGN0eC5zdHJva2VTdHlsZSA9ICIjQ0NDIjsKICAgICAgICBjdHguc3Ryb2tlKCk7CiAgICB9CiAgICBjdXJfbW9udGggPSBwYXJzZUludChjdXJfbW9udGgpIC0gMTsKICAgIHZhciBtb250aF93aWR0aCA9ICh5ZWFyX3dpZHRoIC0gMSkgLyAxMjsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgeWVhcnMubGVuZ3RoOyBpKyspIHsKCXZhciB5ZWFyID0geWVhcnNbaV1bMF07Cgl2YXIgbW9udGhzID0geWVhcnNbaV1bMV07Cgl2YXIgbGVm
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMAp2YXIgd2JBdXRvQ29tcGxldGUgPSAoZnVuY3Rpb24oKXsKICAgIC8vICJ1c2Ugc3RyaWN0IjsKICAgIGZ1bmN0aW9uIHdiQXV0b0NvbXBsZXRlKG9wdGlvbnMpewogICAgICAgIGlmICghZG9jdW1lbnQucXVlcnlTZWxlY3RvcikgcmV0dXJuOwoKICAgICAgICAvLyBoZWxwZXJzCiAgICAgICAgZnVuY3Rpb24gaGFzQ2xhc3MoZWwsIGNsYXNzTmFtZSl7IHJldHVybiBlbC5jbGFzc0xpc3QgPyBlbC5jbGFzc0xpc3QuY29udGFpbnMoY2xhc3NOYW1lKSA6IG5ldyBSZWdFeHAoJ1xcYicrIGNsYXNzTmFtZSsnXFxiJykudGVzdChlbC5jbGFzc05hbWUpOyB9CgogICAgICAgIGZ1bmN0aW9uIGFkZEV2ZW50KGVsLCB0eXBlLCBoYW5kbGVyKXsKICAgICAgICAgICAgaWYgKGVsLmF0dGFjaEV2ZW50KSBlbC5hdHRhY2hFdmVudCgnb24nK3R5cGUsIGhhbmRsZXIpOyBlbHNlIGVsLmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlcik7CiAgICAgICAgfQogICAgICAgIGZ1bmN0aW9uIHJlbW92ZUV2ZW50KGVsLCB0eXBlLCBoYW5kbGVyKXsKICAgICAgICAgICAgLy8gaWYgKGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIpIG5vdCB3b3JraW5nIGluIElFMTEKICAgICAgICAgICAgaWYgKGVsLmRldGFjaEV2ZW50KSBlbC5kZXRhY2hFdmVudCgnb24nK3R5cGUsIGhhbmRsZXIpOyBlbHNlIGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlcik7CiAgICAgICAgfQogICAgICAgIGZ1bmN0aW9uIGxpdmUoZWxDbGFzcywgZXZlbnQsIGNiLCBjb250ZXh0KXsKICAgICAgICAgICAgYWRkRXZlbnQoY29udGV4dCB8fCBkb2N1bWVudCwgZXZlbnQsIGZ1bmN0aW9uKGUpewogICAgICAgICAgICAgICAgdmFyIGZvdW5kLCBlbCA9IGUudGFyZ2V0IHx8IGUuc3JjRWxlbWVudDsKICAgICAgICAgICAgICAgIHdoaWxlIChlbCAmJiAhKGZvdW5kID0gaGFzQ2xhc3MoZWwsIGVsQ2xhc3MpKSkgZWwgPSBlbC5wYXJlbnRFbGVtZW50OwogICAgICAgICAgICAgICAgaWYgKGZvdW5kKSBjYi5jYWxsKGVsLCBlKTsKICAgICAgICAgICAgfSk7CiAgICAgICAgfQoKICAgICAgICB2YXIgbyA9IHsKICAgICAgICAgICAgc2VsZWN0b3I6IDAsCiAgICAgICAgICAgIHNvdXJjZTogMCwKICAgICAgICAgICAgbWluQ2hhcnM6IDMsCiAgICAgICAgICAgIGRlbGF5OiAxNTAsCiAgICAgICAgICAgIG9mZnNldExlZnQ6IDAsCiAgICAgICAgICAgIG9mZnNldFRvcDogMSwKICAgICAgICAgICAgY2FjaGU6IDEsCiAgICAgICAgICAgIG1lbnVDbGFzczogJycsCiAgICAgICAgICAgIHJlbmRlckl0ZW06IGZ1bmN0aW9uIChpdGVtLCBzZWFyY2gpewogICAgICAgICAgICAgICAgLy8gZXNjYXBlIHNwZWNpYWwgY2hhcmFjdGVycwogICAgICAgICAgICAgICAgc2VhcmNoID0gc2VhcmNoLnJlcGxhY2UoL1stXC9cXF4kKis/LigpfFtcXXt9XS9nLCAnXFwkJicpOwogICAgICAgICAgICAgICAgdmFyIHJlID0gbmV3IFJlZ0V4cCgiKCIgKyBzZWFyY2guc3BsaXQoJyAnKS5qb2luKCd8JykgKyAiKSIsICJnaSIpOwogICAgICAgICAgICAgICAgcmV0dXJuICc8ZGl2IGNsYXNzPSJ3Yi1hdXRvY29tcGxldGUtc3VnZ2VzdGlvbiIgZGF0YS12YWw9IicgKyBpdGVtICsgJyI+JyArIGl0ZW0ucmVwbGFjZShyZSwgIjxiPiQxPC9iPiIpICsgJzwvZGl2Pic7CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIG9uU2VsZWN0OiBmdW5jdGlvbihlLCB0ZXJtLCBpdGVtKXt9CiAgICAgICAgfTsKICAgICAgICBmb3IgKHZhciBrIGluIG9wdGlvbnMpIHsgaWYgKG9wdGlvbnMuaGFzT3duUHJvcGVydHkoaykpIG9ba10gPSBvcHRpb25zW2tdOyB9CgogICAgICAgIC8vIGluaXQKICAgICAgICB2YXIgZWxlbXMgPSB0eXBlb2Ygby5zZWxlY3RvciA9PSAnb2JqZWN0JyA/IFtvLnNlbGVjdG9yXSA6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoby5zZWxlY3Rvcik7CiAgICAgICAgZm9yICh2YXIgaT0wOyBpPGVsZW1zLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIHZhciB0aGF0ID0gZWxlbXNbaV07CgogICAgICAgICAgICAvLyBjcmVhdGUgc3VnZ2VzdGlvbnMgY29udGFpbmVyICJzYyIKICAgICAgICAgICAgdGhhdC5zYyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICAgICAgICB0aGF0LnNjLmNsYXNzTmFtZSA9ICd3Yi1hdXRvY29tcGxldGUtc3VnZ2VzdGlvbnMgJytvLm1lbnVDbGFzczsKCiAgICAgICAgICAgIHRoYXQuYXV0b2NvbXBsZXRlQXR0ciA9IHRoYXQuZ2V0QXR0cmlidXRlKCdhdXRvY29tcGxldGUnKTsKICAgICAgICAgICAgdGhhdC5zZXRBdHRyaWJ1dGUoJ2F1dG9jb21wbGV0ZScsICdvZmYnKTsKICAgICAgICAgICAgdGhhdC5jYWNoZSA9IHt9OwogICAgICAgICAgICB0aGF0Lmxhc3RfdmFsID0gJyc7CgogICAgICAgICAgICB0aGF0LnVwZGF0ZVNDID0gZnVuY3Rpb24ocmVzaXplLCBuZXh0KXsKICAgICAgICAgICAgICAgIHZhciByZWN0ID0gdGhhdC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICAgICAgICAgICAgICAgIHRoYXQuc2Muc3R5bGUubGVmdCA9IE1hdGgucm91bmQocmVjdC5sZWZ0ICsgKHdpbmRvdy5wYWdlWE9mZnNldCB8fCBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsTGVmdCkgKyBvLm9mZnNldExlZnQpICsgJ3B4JzsKICAgICAgICAgICAgICAgIHRoYXQuc2Muc3R5bGUudG9wID0gTWF0aC5yb3VuZChyZWN0LmJvdHRvbSArICh3aW5kb3cucGFnZVlPZmZzZXQgfHwgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCkgKyBvLm9mZnNldFRvcCkgKyAncHgnOwogICAgICAgICAgICAgICAgdGhhdC5zYy5zdHlsZS53aWR0aCA9IE1hdGgucm91bmQocmVjdC5yaWdodCAtIHJlY3QubGVmdCkgKyAncHgnOyAvLyBvdXRlcldpZHRoCiAgICAgICAgICAgICAgICBpZiAoIXJlc2l6ZSkgewogICAgICAgICAgICAgICAgICAgIHRoYXQu
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,Ly8gQGxpY2Vuc2UgbWFnbmV0Oj94dD11cm46YnRpaDowYjMxNTA4YWViMDYzNGIzNDdiODI3MGM3YmVlNGQ0MTFiNWQ0MTA5JmRuPWFncGwtMy4wLnR4dCBBR1BMLTMuMAppZih0eXBlb2YgX193bT09PSJ1bmRlZmluZWQiKSBfX3dtPXt9OwooZnVuY3Rpb24oKXsKICB2YXIgX0pTT04gPSB0eXBlb2YgX193YmhhY2sgIT0gJ3VuZGVmaW5lZCcgPyAgX193YmhhY2suSlNPTiA6IEpTT047CiAgdmFyIHByZXR0eU1vbnRocyA9IFsKICAgICJKYW4iLCJGZWIiLCJNYXIiLCJBcHIiLCJNYXkiLCJKdW4iLCJKdWwiLCJBdWciLCJTZXAiLCJPY3QiLCJOb3YiLCJEZWMiXTsKICB2YXIgJEQ9ZG9jdW1lbnQsJFI9ZG9jdW1lbnQsJD1mdW5jdGlvbihuKXtyZXR1cm4gJFIuZ2V0RWxlbWVudEJ5SWQobil9OwoKICBmdW5jdGlvbiBmb3JtYXROdW1iZXIobikgewogICAgcmV0dXJuICgnJytuKS5yZXBsYWNlKC9cQig/PShcZHszfSkrJCkvZywgJywnKTsKICB9CiAgdmFyIGFqYXg9X193bS5hamF4PWZ1bmN0aW9uIGFqYXgobWV0aG9kLCB1cmwsIGNhbGxiYWNrLCBoZWFkZXJzLCBkYXRhKSB7CiAgICB2YXIgeG1saHR0cDsKICAgIGlmICh3aW5kb3cuWE1MSHR0cFJlcXVlc3QpIHsKICAgICAgeG1saHR0cCA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpOwogICAgfSBlbHNlIHsKICAgICAgeG1saHR0cCA9IG5ldyBBY3RpdmVYT2JqZWN0KCJNaWNyb3NvZnQuWE1MSFRUUCIpOwogICAgfQogICAgeG1saHR0cC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHsKICAgICAgaWYgKHRoaXMucmVhZHlTdGF0ZSA9PSA0KSB7CgljYWxsYmFjayh4bWxodHRwKTsKICAgICAgfQogICAgfTsKICAgIHhtbGh0dHAub3BlbihtZXRob2QsIHVybCwgdHJ1ZSk7CiAgICBpZiAoaGVhZGVycykgewogICAgICBmb3IgKHZhciBoZWFkZXIgaW4gaGVhZGVycykgewogICAgICAgIGlmIChoZWFkZXJzLmhhc093blByb3BlcnR5KGhlYWRlcikpIHsKICAgICAgICAgIHhtbGh0dHAuc2V0UmVxdWVzdEhlYWRlcihoZWFkZXIsIGhlYWRlcnNbaGVhZGVyXSk7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICAvLyBwYXNzIGNvb2tpZXMgZm9yIHVzZXIgYXV0aG9yaXphdGlvbgogICAgeG1saHR0cC53aXRoQ3JlZGVudGlhbHMgPSB0cnVlOwogICAgeG1saHR0cC5zZW5kKGRhdGEpOwogIH0KCiAgX193bS5oPWZ1bmN0aW9uIGhpZGVUb29sYmFyKGV2KSB7CiAgICAkKCJ3bS1pcHAiKS5zdHlsZS5kaXNwbGF5PSJub25lIjsKICAgIGV2LnN0b3BQcm9wYWdhdGlvbigpOwogIH0KCiAgdmFyICRleHBhbmQsICRjYXBpbmZvOwoKICBfX3dtLmJ0PWZ1bmN0aW9uIGJvb3RzdHJhcChpbWdXaWR0aCxpbWdIZWlnaHQseWVhckltZ1dpZHRoLG1vbnRoSW1nV2lkdGgsCgkJCSAgICAgY29sbCx3YkN1cnJlbnRVcmwsY2FwdHVyZURhdGUsZmlyc3RZZWFyLAoJCQkgICAgIHN0YXRpY19wcmVmaXgsYmFubmVyQ3NzKSB7CiAgICBfX3dtLnN0YXRpY19wcmVmaXggPSBzdGF0aWNfcHJlZml4IHx8ICcvc3RhdGljLyc7CiAgICB2YXIgd2JQcmVmaXg9Jy8nKyhjb2xsfHwnd2ViJykrJy8nOwogICAgY2FwdHVyZURhdGUgPSBjYXB0dXJlRGF0ZS5zcGxpdCgnLScpOwogICAgdmFyIGRpc3BsYXlEYXkgPSBjYXB0dXJlRGF0ZVsyXTsKICAgIHZhciBkaXNwbGF5TW9udGggPSBjYXB0dXJlRGF0ZVsxXTsKICAgIHZhciBkaXNwbGF5WWVhciA9IGNhcHR1cmVEYXRlWzBdOwogICAgdmFyIHRyYWNrZXJWYWwsY3VyWWVhciA9IC0xLGN1ck1vbnRoID0gLTE7CiAgICB2YXIgeWVhclRyYWNrZXIsbW9udGhUcmFja2VyOwoKICAgIC8vIG1vdmUgI3dtLWlwcCBjb250ZW50IHRvIGl0cyBzaGFkb3dSb290IGlmIHN1cHBvcnRlZAogICAgdmFyIHdtaXBwID0gJCgnd20taXBwLWJhc2UnKTsKICAgIGlmICh3bWlwcC5hdHRhY2hTaGFkb3cpewogICAgICB2YXIgc2hhZG93ID0gd21pcHAuYXR0YWNoU2hhZG93KHttb2RlOidvcGVuJ30pOwogICAgICAkUiA9IHNoYWRvdzsgLy8gdGhpcyBjaGFuZ2VzIHRoZSBiYXNlIG9mICQoKQogICAgICB2YXIgbmF2ID0gd21pcHAuY2hpbGRyZW5bMF07bmF2LmlkPSd3bS1pcHAnOwogICAgICBzaGFkb3cuYXBwZW5kQ2hpbGQobmF2KTsKICAgICAgaWYgKGJhbm5lckNzcykgewoJZm9yKHZhciBpID0gMDsgaSA8IGJhbm5lckNzcy5sZW5ndGg7IGkrKykgewoJICB2YXIgbGluayA9ICRELmNyZWF0ZUVsZW1lbnQoJ2xpbmsnKTsKCSAgbGluay5zZXRBdHRyaWJ1dGUoJ3JlbCcsICdzdHlsZXNoZWV0Jyk7CgkgIGxpbmsuc2V0QXR0cmlidXRlKCd0eXBlJywgJ3RleHQvY3NzJyk7CgkgIGxpbmsuc2V0QXR0cmlidXRlKCdocmVmJywgc3RhdGljX3ByZWZpeCArIGJhbm5lckNzc1tpXSk7CgkgIHNoYWRvdy5hcHBlbmRDaGlsZChsaW5rKTsKCX0KICAgICAgfQogICAgfQogICAgaWYgKHdpbmRvdy50b3AgPT0gd2luZG93LnNlbGYpIHsKICAgICAgd21pcHAuc3R5bGUuZGlzcGxheSA9ICJibG9jayI7CiAgICB9CgogICAgdmFyIG1vZGFsSXNPcGVuID0gZmFsc2UsIGJhbm5lcklzVmlzaWJsZSA9IHRydWU7CgogICAgZnVuY3Rpb24gcHJvY2Vzc01lc3NhZ2UoZXYpIHsKICAgICAgaWYgKGV2Lm9yaWdpbi5pbmRleE9mKCdhcmNoaXZlLm9yZycpID09PSAtMSkKCXJldHVybjsKICAgICAgLy8gZG9uYXRpb24gYmFubmVyIHNlbmRzIG1zZyBhcyBKU09OIHN0cmluZy4KICAgICAgdmFyIG1zZyA9IHR5cGVvZiBldi5kYXRhID09ICJzdHJpbmciID8gX0pTT04ucGFyc2UoZXYuZGF0YSkgOiBldi5kYXRhOwogICAgICBjb25zb2xlLmxvZygnZ290IG1lc3NhZ2UgJW8nLCBtc2cpOwogICAgICBpZiAobXNnLmV2ZW50ID09ICdzZXQgaGVpZ2h0JykgewoJdmFyIGlIZWlnaHQgPSBtc2cudmFsdWU7IC8vIGlmcmFtZSBoZWlnaHQKCXZhciBiSGVpZ2h0ID0gbXNnLmJhbm5lckhlaWdodDsgLy8gYmFubmVyIGhlaWdodAoJaWYgKHR5cGVvZiBpSGVpZ2h0ICE9PSAibnVtYmVyIiB8fCBpSGVpZ2h0IDw9IDApIHJldHVybjsKCWlmICghYmFubmVySXNWaXNpYmxlKSByZXR1cm47Cglw
|
||
|
<style type="text/css">
|
||
|
body {
|
||
|
margin-top:0 !important;
|
||
|
padding-top:0 !important;
|
||
|
/*min-width:800px !important;*/
|
||
|
}
|
||
|
.wb-autocomplete-suggestions {
|
||
|
text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1);
|
||
|
position: absolute; display: none; z-index: 2147483647; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box;
|
||
|
}
|
||
|
.wb-autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; }
|
||
|
.wb-autocomplete-suggestion b { font-weight: bold; }
|
||
|
.wb-autocomplete-suggestion.selected { background: #f0f0f0; }
|
||
|
</style>
|
||
|
<div id="wm-ipp-base" lang="en" style="display:none;direction:ltr;">
|
||
|
<div id="wm-ipp" style="position:fixed;left:0;top:0;right:0;">
|
||
|
<div id="wm-ipp-inside">
|
||
|
<div style="position:relative;">
|
||
|
<div id="wm-logo" style="float:left;width:130px;padding-top:10px;">
|
||
|
<a href="https://web.archive.org/web/" title="Wayback Machine home page"><img alt="Wayback Machine" width="110" height="39" border="0" src="data:image/png;base64,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
|
||
|
</div>
|
||
|
<div class="r" style="float:right;">
|
||
|
<div id="wm-btns" style="text-align:right;height:25px;">
|
||
|
<div id="wm-save-snapshot-success">success</div>
|
||
|
<div id="wm-save-snapshot-fail">fail</div>
|
||
|
<a href="#" onclick="__wm.saveSnapshot('http://coligo.io/create-url-shortener-with-node-express-mongo/', '20160411185424')" title="Share via My Web Archive" id="wm-save-snapshot-open">
|
||
|
<span class="iconochive-web"></span>
|
||
|
</a>
|
||
|
<a href="https://archive.org/account/login.php" title="Sign In" id="wm-sign-in">
|
||
|
<span class="iconochive-person"></span>
|
||
|
</a>
|
||
|
<span id="wm-save-snapshot-in-progress" class="iconochive-web"></span>
|
||
|
<a href="http://faq.web.archive.org/" title="Get some help using the Wayback Machine" style="top:-6px;"><span class="iconochive-question" style="color:rgb(87,186,244);font-size:160%;"></span></a>
|
||
|
<a id="wm-tb-close" href="#close" onclick="__wm.h(event);return false;" style="top:-2px;" title="Close the toolbar"><span class="iconochive-remove-circle" style="color:#888888;font-size:240%;"></span></a>
|
||
|
</div>
|
||
|
<div id="wm-share">
|
||
|
<a href="https://web.archive.org/web/20160411185424/http://web.archive.org/screenshot/http://coligo.io/create-url-shortener-with-node-express-mongo/" id="wm-screenshot" title="screenshot">
|
||
|
<span class="wm-icon-screen-shot"></span>
|
||
|
</a>
|
||
|
<a href="#" onclick="window.open('https://www.facebook.com/sharer/sharer.php?u=https://web.archive.org/web/20160411185424/http://coligo.io:80/create-url-shortener-with-node-express-mongo/', '', 'height=400,width=600'); return false;" title="Share on Facebook" style="margin-right:5px;" target="_blank"><span class="iconochive-facebook" style="color:#3b5998;font-size:160%;"></span></a>
|
||
|
<a href="#" onclick="window.open('https://twitter.com/intent/tweet?text=https://web.archive.org/web/20160411185424/http://coligo.io:80/create-url-shortener-with-node-express-mongo/&via=internetarchive', '', 'height=400,width=600'); return false;" title="Share on Twitter" style="margin-right:5px;" target="_blank"><span class="iconochive-twitter" style="color:#1dcaff;font-size:160%;"></span></a>
|
||
|
</div>
|
||
|
</div>
|
||
|
<table class="c" style="">
|
||
|
<tbody>
|
||
|
<tr>
|
||
|
<td class="u" colspan="2">
|
||
|
<form target="_top" method="get" action="https://web.archive.org/web/submit" name="wmtb" id="wmtb"><input type="text" name="url" id="wmtbURL" value="http://coligo.io/create-url-shortener-with-node-express-mongo/" onfocus="this.focus();this.select();"><input type="hidden" name="type" value="replay"><input type="hidden" name="date" value="20160411185424"><input type="submit" value="Go"></form>
|
||
|
</td>
|
||
|
<td class="n" rowspan="2" style="width:110px;">
|
||
|
<table>
|
||
|
<tbody>
|
||
|
<!-- NEXT/PREV MONTH NAV AND MONTH INDICATOR -->
|
||
|
<tr class="m">
|
||
|
<td class="b" nowrap="nowrap"><a href="https://web.archive.org/web/20160222095908/http://coligo.io:80/create-url-shortener-with-node-express-mongo/" title="22 Feb 2016"><strong>Feb</strong></a></td>
|
||
|
<td class="c" id="displayMonthEl" title="You are here: 18:54:24 Apr 11, 2016">APR</td>
|
||
|
<td class="f" nowrap="nowrap"><a href="https://web.archive.org/web/20160513003033/http://coligo.io:80/create-url-shortener-with-node-express-mongo/" title="13 May 2016"><strong>May</strong></a></td>
|
||
|
</tr>
|
||
|
<!-- NEXT/PREV CAPTURE NAV AND DAY OF MONTH INDICATOR -->
|
||
|
<tr class="d">
|
||
|
<td class="b" nowrap="nowrap"><a href="https://web.archive.org/web/20160320212153/http://coligo.io:80/create-url-shortener-with-node-express-mongo/" title="21:21:53 Mar 20, 2016"><img alt="Previous capture" width="14" height="16" border="0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAYAAAAmlE46AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMZJREFUeNqU07EKQWEUwHEuSSaLkicgg3TZzCySvIKk2GVgJZmsBg9g8QK8gcHoASwGNoMS/l+db6Au3zn1W279b+fe796w779Cyimi7ymCFBbYox51CGLoYIS0XHv+C2uYynofExRmJWgG3fE7TGKMHuK/VrFhRJ7DRBmXN2XCBibIa87Ek5US2sM04Ro5DHDRhGbumMu6SzxcQztndFHGVhPaOaCKFo6a0Iz5+jcoYYira2jnhhkKWNlO83ec0EYFu7cAAwCVABzGI3/GxAAAAABJRU5ErkJggg=="></a></td>
|
||
|
<td class="c" id="displayDayEl" style="width:34px;font-size:24px;white-space:nowrap;" title="You are here: 18:54:24 Apr 11, 2016">11</td>
|
||
|
<td class="f" nowrap="nowrap"><a href="https://web.archive.org/web/20160416080821/http://coligo.io:80/create-url-shortener-with-node-express-mongo" title="08:08:21 Apr 16, 2016"><img alt="Next capture" width="14" height="16" border="0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAYAAAAmlE46AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMhJREFUeNqU0s8KAUEcwPHBJjm5KHkCV+07cJHkKZRyUK6cKYeVPxd5BVcHjspB+VPKA7g4cHNQwndqp/awMb9ffdra+jazMxtx3c9UKTXCTgkmihK28JCWhG//2cARdcRtwuBkMMQGRUloJo8F5shJQjMV/9D6SElCPQk0cUINMdvQTBYTHFCWhGaSeheOILihiwGeNuELM7RxNS//hUu0sLe9jjOqKIRFYSve0fP/nsevrTiBVfV3dHCxOSkdrjDGWnInXwEGAM40IjLd/vWIAAAAAElFTkSuQmCC"></a></td>
|
||
|
</tr>
|
||
|
<!-- NEXT/PREV YEAR NAV AND YEAR INDICATOR -->
|
||
|
<tr class="y">
|
||
|
<td class="b" nowrap="nowrap">2015</td>
|
||
|
<td class="c" id="displayYearEl" title="You are here: 18:54:24 Apr 11, 2016">2016</td>
|
||
|
<td class="f" nowrap="nowrap"><a href="https://web.archive.org/web/20170704195331/http://coligo.io/create-url-shortener-with-node-express-mongo/" title="04 Jul 2017"><strong>2017</strong></a></td>
|
||
|
</tr>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td class="s">
|
||
|
<div id="wm-nav-captures">
|
||
|
<a class="t" href="https://web.archive.org/web/20160411185424*/http://coligo.io/create-url-shortener-with-node-express-mongo/" title="See a list of every capture for this URL">20 captures</a>
|
||
|
<div class="r" title="Timespan for captures of this URL">21 Feb 2016 - 16 Dec 2017</div>
|
||
|
</div>
|
||
|
</td>
|
||
|
<td class="k">
|
||
|
<a href="https://web.archive.org/web/20160411185424/http://coligo.io/create-url-shortener-with-node-express-mongo/" id="wm-graph-anchor">
|
||
|
<div id="wm-ipp-sparkline" title="Explore captures for this URL" style="position: relative">
|
||
|
<canvas id="wm-sparkline-canvas" width="625" height="27" border="0"></canvas>
|
||
|
</div>
|
||
|
</a>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
<div style="position:absolute;bottom:0;right:2px;text-align:right;">
|
||
|
<a id="wm-expand" class="wm-btn wm-closed" href="#expand" onclick="__wm.ex(event);return false;"><span id="wm-expand-icon" class="iconochive-down-solid"></span> <span style="font-size:80%">About this capture</span></a>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div id="wm-capinfo" style="border-top:1px solid #777;display:none; overflow: hidden">
|
||
|
<div style="background-color:#666;color:#fff;font-weight:bold;text-align:center">COLLECTED BY</div>
|
||
|
<div style="padding:3px;position:relative" id="wm-collected-by-content">
|
||
|
<div style="display:inline-block;vertical-align:top;width:50%;">
|
||
|
<span class="c-logo" style="background-image:url("data:image/jpeg; charset=UTF-8;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCABBALQDAREAAhEBAxEB/8QAHQAAAgMBAQEBAQAAAAAAAAAAAAcBBggJBQIDCv/EAEAQAAEDAwMCBAMEBQoHAAAAAAECAwQFBhEABwgSIQkTMVEUIkEjMmFxFUJigbQkMzhScnaCkZKhFiVUZLHB8f/EABsBAQADAQEBAQAAAAAAAAAAAAABAgYFAwcE/8QAMhEAAgECBQMCBQMDBQAAAAAAAAECAxEEBRIhMQZBURMiFGFxgaEyQrGR0eEVkqLB8P/aAAwDAQACEQMRAD8A6p6AjI99AGR7jQXDI99AGR7jQBoCdARke+gJ0BGgDIzjPfQE6ANAGgDQBoA0AaANAGgDQBoA0B8LcQhClLUAACTn21H6lsNluzLm5HNOELkm7f7F0OPdtYp58uo1WQ6WqVTlemFOAEur/ZT/AJ61GB6bnKCrYxuMeyXLM7j8+jQeihu/JQHt5t55BU5XN9fgJGf5miUeE2yj8Pt23lH8yRr97wOV0tlSTt5k0/wc747M6vui7L6IGN8t96atEm3N3aZcGB1GBcFLjoD34B6Mloo/0nVll+WVtpU9PzUm/wANlXmWZ0HqlJNfNW/gbWyPMW1dx7kO2t9Uddl320AP0XKdC480d/njPdgsHGQDg9/rrj5n07WwaeIovVDz4+qO3gc5hivZPaX8miUkY7ems8t9ztE5HvqeAZ7375z8feN94MWPuZX6lHq8iGmcGoVMclBDSlFKepSfunsTj20cbWb7kXHvRazTa/R4VdpMpMiDUI7cqM6n0caWkKSr94I0a0uzJW/Aut/+R22fGm1oV4bpzp8Smz5yaeyuJCXJUXikqAKUdwMJPfQHq7J70WPv7YMLcvbuTLkUOe68yw5LiqjuFTSyheUK74yD31LTXIL9qAR1AfXQBnQBkD10AZHvoAzoAyNCLoOoe+fy0JAKB7f+tAToA0AaAw54kfJmpbdW0ztDZVUMKs1+MXqnLbc6VxoXp0JI7pUvByf6oOtp0lksMTOWOr/pj5+W5m88x7g1g6XLtf79jLuwS6hc9JtTbfbthC5tcQ3IWfM6S+86nzHHXFevSkeufoMa9s2zj15vRtGPFj82Fy70pXnvJ/8ArDh3Fgbb7I19Nn7mLvifVFMof+IpzUeHDdQokEsKdKlupBBGSEjI1wadadROaZ150Y07I9Gv7aUmr7PP78bR3fNq1Ah+YqfT6iylqZDCFdLnzIPSopyCRgZScg6ininGpons7bEvDxlT9SG5lTea9o1RoFOrLk5TFYokxkU6c05hxsLWAUlQ7gZwQc9iPx1rMnzKHqRw+I3jLb7nBxOX3i6tJb9/sdK+DPI1/fvapLVxvpXdNtlMOpKz3kJx9m/j8QMK/aB1nOpcm/0rFNw/RLf6PwdrJsf8ZT9Ob90fyvJo195mOy5IecS202krWsnASkDJOfp21muWdhn86PJ6/ri5J8gNxNy6RCkT4EZ5+Q2Wh1pjUqMoMtrJ+iMFJz+3r1l7ntwgtuTrV4V29Cd0+LtMt2fLDtXsWQqhSAV5UqOB1x1n8PLPR/g1FS8rSIWzKH40P9HW2P72Nfw7uqrgsMXwpTjhfaZP/XVP+JVq1XlfQrHka3JDl5stxdpUaVuZXHv0jUApUGkwWvOmSAPVYRkBKAe3UogZ7d9QoX5Gq+yM9WB4wfGy7LgZody0W57TZkOJabqE5ht2Okk4y6W1FSB7nBAH100L9rJu1yav3Q3dp23G1s3dqDQqhddJgxUznG6KUPOuRCMl5sEgLSEnq7H01Khd2lsVcns4im4vc+dm+VdzVOz7KiVilVemxBNEaqttoVKYzhRa6FHJSSnI9iDo4xXDJTf7iqbseJ3sbtfuxO2dZty6bnrsCU3T1/oVhp1tyYvADDZUsFSgohJ7euR31MacW92Q5NcIbu9vK7arjpY1LvDd6bJo8mrtBUSitN+fOed6QVtpSnsegkBSiekZ9dNG732XcmLursy7T/Gi2AkVJMeobeXrDiFRCpPQw4Up/rFAXn9w1GmHn8E3l4HneNeovLvaqgXdsPcUWtUtU1SnlpkBp6MroAIW04QnzE4KShzGAvqGe2vSlpi7z4KSu9kOTaK3K7au3lEoFyIabqERlfnttylyUtlTi1JQHV4K+lKgM4+nbsNeVSzl7eC0bpblz1UsGgDQHC/xD7qqdX3p3Any3HCWqoaUwCeyWWz5YA/0qP8Ai19WqtZf0kpw2c1Ff7ufwYfBy+LzuSluk3/x/wAlE4w8nKzsvddu3lTYLM6Xb6THchPrKEy4hR0KQFYPQcY7gHBA7EdtY7LsG8xwrot2NJXlLDVfUXDHzvHu1vvzyvem1XbPZapsUuiRFw46WFF5psOKCnHH5i0ttpBKU+yUgep9ddKlleEyym/i6u/jz9uTwqVZYqV4I0nsvaVw7Z8BN4qPc1wW/VKsyqrSZCKLUkTmoSvgYw8h1xv5PNHSVEJJGFp75JA42Np062Y04Uk0mlyrdz9VH24aW97eDk9uHuBJrUNqixZBUwlaXHVD+sn7oH5Hv+embweFnFU+zv8AdcF8FSW+s3v4VlzVJrd9iO28oxq/RH/iU/RS209aT+4pV/q1teqFDGZBRx/dqLf8GVymToZxPDfX+5s3xFN6FbK8VLtqkCYY9YuJtNv0spICw7JBS4of2Wg6rt9QNfLoLds2ydzFvhWce7avHaXda7b0dissXrAds+CH3UJIYKOp5xIJz/OFsD8UatF2REtyj+F5uFVdh+W9c2Kut8R2blVIoT7ZIITUoq1Fkgg4+bDiQR69Q0ts4k+Gag8aDvx1tj+9jX8O7qnCLDF8Kb+hdaY/7+p/xKtWqcr6FF3OZ3ITciydwOflxVvkDLqMmyaLcjtMmR4SCtz4GIooSwgAjAUtJyQc/MTq87OWl9iEmo/Mt3MPdfw9dzdr2GOPu38u1b1pT7IhuRqKITEmNnDjb5CiFkDuFEdWfqdV2ta5PuZubwmr3qm4HEsW7cbqZ7Ft1aVRY4ewv+SlKXEtqH1SA4Uge2ol7Yxku4T9zXgwxzO2cvHgPybpu7Oy1UNKpNeckVCgOIUFmI6oFMmItB+8geZ2zkFKgPUaPb3IlO/I3fCc4q/8cV6Zyw3KSiooizH2qA28sLU7UM5fmOD3SVEJz36iVY7A6m+halyxyza3OBfFNzadUPlTLjMUh59KqeWQf0l56Tk/CdAK847Kx8uD8301WN3fwHZcGAN7t6PC1ufZ6vWhtrtTNo9zt09aaHUWaEWHkzEj7Mre6yopJ+91Z1ZS+ZFn4Lz4ItXqbkndKiqmuGChFNlJjk/Il0lxJWB9CQAD74Gqt+0tbe51Y1UkNAGgDQHE3xDtv6jTdyL6dcbUfOqxqrfbsppSiokfuX/tr7VLCRzfo+EaXMNDf25PmuFr/AdRyp1NlLVb78Fe8NjjVaG/m9Lg3CYMuh23TP0u7TlKKUznPNS202opOfLyepQ+oAHoTnFY+rLLMDCeH2b2NwmsRW9Kfbk01y7vW+buvirbNW0Ta9i2q78AxSaan4REpQSnqcdCOnKST8ieyenvgk510+n8vw6w6xlX31Jd32+Rl85x9b13hoPTFdl3GdtRtO/YHh/7k206z5U2twKpVHooAC2EuxmkNBSf1SpppCwD36VAnGuPmGIWKzylJ2UU0tuOe39TqYCE8PllR3bk99zk3u3tFUtuo9rVCZJZeF3UVuux2mgepllx91pCVZ/WV5PV27YWMZ10sdh44r1IQV7S0/fn/s/ZgMQ5Qi57bXOgnhhWDNgbhU+qriq
|
||
|
Organization: <a style="color:#33f;" href="https://archive.org/details/alexacrawls" target="_new"><span class="wm-title">Alexa Crawls</span></a>
|
||
|
<div style="max-height:75px;overflow:hidden;position:relative;">
|
||
|
<div style="position:absolute;top:0;left:0;width:100%;height:75px;background:linear-gradient(to bottom,rgba(255,255,255,0) 0%,rgba(255,255,255,0) 90%,rgba(255,255,255,255) 100%);"></div>
|
||
|
Starting in 1996, <a href="http://www.alexa.com/">Alexa Internet</a> has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to the <a href="http://web.archive.org/">Wayback Machine</a> after an embargo period.
|
||
|
</div>
|
||
|
</div>
|
||
|
<div style="display:inline-block;vertical-align:top;width:49%;">
|
||
|
<span class="c-logo" style="background-image:url("data:image/jpeg; charset=UTF-8;base64,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
|
||
|
<div>Collection: <a style="color:#33f;" href="https://archive.org/details/alexacrawls" target="_new"><span class="wm-title">Alexa Crawls</span></a></div>
|
||
|
<div style="max-height:75px;overflow:hidden;position:relative;">
|
||
|
<div style="position:absolute;top:0;left:0;width:100%;height:75px;background:linear-gradient(to bottom,rgba(255,255,255,0) 0%,rgba(255,255,255,0) 90%,rgba(255,255,255,255) 100%);"></div>
|
||
|
Starting in 1996, <a href="http://www.alexa.com/">Alexa Internet</a> has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to the <a href="http://web.archive.org/">Wayback Machine</a> after an embargo period.
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div style="background-color:#666;color:#fff;font-weight:bold;text-align:center" title="Timestamps for the elements of this page">TIMESTAMPS</div>
|
||
|
<div>
|
||
|
<div id="wm-capresources" style="margin:0 5px 5px 5px;max-height:250px;overflow-y:scroll !important"></div>
|
||
|
<div id="wm-capresources-loading" style="text-align:left;margin:0 20px 5px 5px;display:none"><img alt="loading" src="data:image/gif;base64,R0lGODlhFAAUAOMIAAAAABoaGjMzM0xMTGZmZoCAgJmZmbKysv///////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgAIACwAAAAAFAAUAAAEUxDJSau9CADMteZTEEjehhzHJYqkiaLWOlZvGs8WDO6UIPAGw8TnAwWDEuKPcxQml0YnjzcYYAqFS7VqwWItWyuCQJB4s2AxmWxGg9bl6YQtl0cAACH5BAEKAA8ALAAAAAAUABQAAART8MlJq70vBMy15pMgSN72AMAliqSJotY6Vm8azxYM7tQw8IfDxOcDBYMS4o9zFCaXRiePRyBgDIZLtWrBYi1b66NQkHizYDGZbEaD1uXphC2XRwAAIfkEAQoADwAsAAAAABQAFAAABFPwyUmrvU8IzLXm0zBI3vYEwSWKpImi1jpWbxrPFgzuFEHwAMDE5wMFgxLij3MUJpdGJ49XKGAOh0u1asFiLVvrw2CQeLNgMZlsRoPW5emELZdHAAAh+QQBCgAPACwAAAAAFAAUAAAEU/DJSau9bwzMteYTQUje9gjCJYqkiaLWOlZvGs8WDO5UUfCBwMTnAwWDEuKPcxQml0Ynj2cwYACAS7VqwWItW+vjcJB4s2AxmWxGg9bl6YQtl0cAACH5BAEKAA8ALAAAAAAUABQAAART8MlJq72PEMy15lNRSN72DMMliqSJotY6Vm8azxYM7pRh8ALBxOcDBYMS4o9zFCaXRiePdzhgAoFLtWrBYi1b6wMAkHizYDGZbEaD1uXphC2XRwAAIfkEAQoADwAsAAAAABQAFAAABFPwyUmrva8UzLXmk2FI3vYQxCWKpImi1jpWbxrPFgzu1HHwg8HE5wMFgxLij3MUJpdGJ48HAGAEgku1asFiLVvrIxCQeLNgMZlsRoPW5emELZdHAAAh+QQBCgAPACwAAAAAFAAUAAAEU/DJSau9zxjMtebTcUje9hTFJYqkiaLWOlZvGs8WDO4UAPAEwsTnAwWDEuKPcxQml0YnjxcIYAaDS7VqwWItW+tDIJB4s2AxmWxGg9bl6YQtl0cAACH5BAEKAA8ALAAAAAAUABQAAART8MlJq73vHMy15hMASN72GMYliqSJotY6Vm8azxYM7lQQ8IXCxOcDBYMS4o9zFCaXRiePJxBgCIRLtWrBYi1b62MwkHizYDGZbEaD1uXphC2XRwAAOw=="></div>
|
||
|
</div>
|
||
|
</div></div></div></div><div id="donato" style="position:relative;width:100%;">
|
||
|
<div id="donato-base">
|
||
|
<iframe id="donato-if" src="data:text/html;base64,PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPjx0aXRsZT5Eb25hdGU8L3RpdGxlPjxzdHlsZT5ib2R5e21hcmdpbjowfTwvc3R5bGU+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPgogICAgICA8c2NyaXB0IHNyYz0iZGF0YTphcHBsaWNhdGlvbi9qYXZhc2NyaXB0O2Jhc2U2NCxMeW9oSUdwUmRXVnllU0IyTVM0eE1DNHlJSHdnS0dNcElESXdNRFVzSURJd01UTWdhbEYxWlhKNUlFWnZkVzVrWVhScGIyNHNJRWx1WXk0Z2ZDQnFjWFZsY25rdWIzSm5MMnhwWTJWdWMyVUtMeTlBSUhOdmRYSmpaVTFoY0hCcGJtZFZVa3c5YW5GMVpYSjVMVEV1TVRBdU1pNXRhVzR1YldGd0Npb3ZDaWhtZFc1amRHbHZiaWhsTEhRcGUzWmhjaUJ1TEhJc2FUMTBlWEJsYjJZZ2RDeHZQV1V1Ykc5allYUnBiMjRzWVQxbExtUnZZM1Z0Wlc1MExITTlZUzVrYjJOMWJXVnVkRVZzWlcxbGJuUXNiRDFsTG1wUmRXVnllU3gxUFdVdUpDeGpQWHQ5TEhBOVcxMHNaajBpTVM0eE1DNHlJaXhrUFhBdVkyOXVZMkYwTEdnOWNDNXdkWE5vTEdjOWNDNXpiR2xqWlN4dFBYQXVhVzVrWlhoUFppeDVQV011ZEc5VGRISnBibWNzZGoxakxtaGhjMDkzYmxCeWIzQmxjblI1TEdJOVppNTBjbWx0TEhnOVpuVnVZM1JwYjI0b1pTeDBLWHR5WlhSMWNtNGdibVYzSUhndVptNHVhVzVwZENobExIUXNjaWw5TEhjOUwxc3JMVjAvS0Q4NlhHUXFYQzU4S1Z4a0t5Zy9PbHRsUlYxYkt5MWRQMXhrSzN3cEx5NXpiM1Z5WTJVc1ZEMHZYRk1yTDJjc1F6MHZYbHRjYzF4MVJrVkdSbHg0UVRCZEszeGJYSE5jZFVaRlJrWmNlRUV3WFNza0wyY3NUajB2WGlnL09seHpLaWc4VzF4M1hGZGRLejRwVzE0K1hTcDhJeWhiWEhjdFhTb3BLU1F2TEdzOUwxNDhLRngzS3lsY2N5cGNMejgrS0Q4NlBGd3ZYREUrZkNra0x5eEZQUzllVzF4ZExEcDdmVnh6WFNva0x5eFRQUzhvUHpwZWZEcDhMQ2tvUHpwY2N5cGNXeWtyTDJjc1FUMHZYRndvUHpwYklseGNYQzlpWm01eWRGMThkVnRjWkdFdFprRXRSbDE3TkgwcEwyY3NhajB2SWx0ZUlseGNYSEpjYmwwcUlueDBjblZsZkdaaGJITmxmRzUxYkd4OExUOG9QenBjWkN0Y0xud3BYR1FyS0Q4NlcyVkZYVnNyTFYwL1hHUXJmQ2t2Wnl4RVBTOWVMVzF6TFM4c1REMHZMU2hiWEdSaExYcGRLUzluYVN4SVBXWjFibU4wYVc5dUtHVXNkQ2w3Y21WMGRYSnVJSFF1ZEc5VmNIQmxja05oYzJVb0tYMHNjVDFtZFc1amRHbHZiaWhsS1hzb1lTNWhaR1JGZG1WdWRFeHBjM1JsYm1WeWZId2liRzloWkNJOVBUMWxMblI1Y0dWOGZDSmpiMjF3YkdWMFpTSTlQVDFoTG5KbFlXUjVVM1JoZEdVcEppWW9YeWdwTEhndWNtVmhaSGtvS1NsOUxGODlablZ1WTNScGIyNG9LWHRoTG1Ga1pFVjJaVzUwVEdsemRHVnVaWEkvS0dFdWNtVnRiM1psUlhabGJuUk1hWE4wWlc1bGNpZ2lSRTlOUTI5dWRHVnVkRXh2WVdSbFpDSXNjU3doTVNrc1pTNXlaVzF2ZG1WRmRtVnVkRXhwYzNSbGJtVnlLQ0pzYjJGa0lpeHhMQ0V4S1NrNktHRXVaR1YwWVdOb1JYWmxiblFvSW05dWNtVmhaSGx6ZEdGMFpXTm9ZVzVuWlNJc2NTa3NaUzVrWlhSaFkyaEZkbVZ1ZENnaWIyNXNiMkZrSWl4eEtTbDlPM2d1Wm00OWVDNXdjbTkwYjNSNWNHVTllMnB4ZFdWeWVUcG1MR052Ym5OMGNuVmpkRzl5T25nc2FXNXBkRHBtZFc1amRHbHZiaWhsTEc0c2NpbDdkbUZ5SUdrc2J6dHBaaWdoWlNseVpYUjFjbTRnZEdocGN6dHBaaWdpYzNSeWFXNW5JajA5ZEhsd1pXOW1JR1VwZTJsbUtHazlJandpUFQwOVpTNWphR0Z5UVhRb01Da21KaUkrSWowOVBXVXVZMmhoY2tGMEtHVXViR1Z1WjNSb0xURXBKaVpsTG14bGJtZDBhRDQ5TXo5YmJuVnNiQ3hsTEc1MWJHeGRPazR1WlhobFl5aGxLU3doYVh4OElXbGJNVjBtSm00cGNtVjBkWEp1SVc1OGZHNHVhbkYxWlhKNVB5aHVmSHh5S1M1bWFXNWtLR1VwT25Sb2FYTXVZMjl1YzNSeWRXTjBiM0lvYmlrdVptbHVaQ2hsS1R0cFppaHBXekZkS1h0cFppaHVQVzRnYVc1emRHRnVZMlZ2WmlCNFAyNWJNRjA2Yml4NExtMWxjbWRsS0hSb2FYTXNlQzV3WVhKelpVaFVUVXdvYVZzeFhTeHVKaVp1TG01dlpHVlVlWEJsUDI0dWIzZHVaWEpFYjJOMWJXVnVkSHg4YmpwaExDRXdLU2tzYXk1MFpYTjBLR2xiTVYwcEppWjRMbWx6VUd4aGFXNVBZbXBsWTNRb2Jpa3BabTl5S0drZ2FXNGdiaWw0TG1selJuVnVZM1JwYjI0b2RHaHBjMXRwWFNrL2RHaHBjMXRwWFNodVcybGRLVHAwYUdsekxtRjBkSElvYVN4dVcybGRLVHR5WlhSMWNtNGdkR2hwYzMxcFppaHZQV0V1WjJWMFJXeGxiV1Z1ZEVKNVNXUW9hVnN5WFNrc2J5WW1ieTV3WVhKbGJuUk9iMlJsS1h0cFppaHZMbWxrSVQwOWFWc3lYU2x5WlhSMWNtNGdjaTVtYVc1a0tHVXBPM1JvYVhNdWJHVnVaM1JvUFRFc2RHaHBjMXN3WFQxdmZYSmxkSFZ5YmlCMGFHbHpMbU52Ym5SbGVIUTlZU3gwYUdsekxuTmxiR1ZqZEc5eVBXVXNkR2hwYzMxeVpYUjFjbTRnWlM1dWIyUmxWSGx3WlQ4b2RHaHBjeTVqYjI1MFpYaDBQWFJvYVhOYk1GMDlaU3gwYUdsekxteGxibWQwYUQweExIUm9hWE1wT25ndWFYTkdkVzVqZEdsdmJpaGxLVDl5TG5KbFlXUjVLR1VwT2lobExuTmxiR1ZqZEc5eUlUMDlkQ1ltS0hSb2FYTXVjMlZzWldOMGIzSTlaUzV6Wld4bFkzUnZjaXgwYUdsekxtTnZiblJsZUhROVpTNWpiMjUwWlhoMEtTeDRMbTFoYTJWQmNuSmhlU2hsTEhSb2FYTXBLWDBzYzJWc1pXTjBiM0k2SWlJc2JHVnVaM1JvT2pBc2RHOUJjbkpoZVRwbWRXNWpkR2x2YmlncGUzSmxkSFZ5YmlCbkxtTmhiR3dvZEdocGN5bDlMR2RsZERwbWRXNWpkR2x2YmlobEtYdHlaWFIxY200Z2JuVnNiRDA5WlQ5MGFHbHpMblJ2UVhKeVlYa29LVG93UG1VL2RHaHBjMXQwYUdsekxteGxibWQwYUN0bFhUcDBhR2x6VzJWZGZTeHdkWE5vVTNSaFkyczZablZ1WTNScGIyNG9aU2w3ZG1GeUlIUTllQzV0WlhKblpTaDBhR2x6TG1OdmJuTjBjblZqZEc5eUtDa3NaU2s3Y21WMGRYSnVJSFF1Y0hKbGRrOWlhbVZqZEQxMGFHbHpMSFF1WTI5dWRHVjRkRDEwYUdsekxtTnZiblJsZUhRc2RIMHNaV0ZqYURwbWRXNWpkR2x2Ymlob
|
||
|
</iframe>
|
||
|
</div>
|
||
|
</div><script type="text/javascript">
|
||
|
__wm.bt(625,27,25,2,"web","http://coligo.io/create-url-shortener-with-node-express-mongo/","2016-04-11",1996,"/_static/",['css/banner-styles.css','css/iconochive.css']);
|
||
|
__wm.checkScreenShot("http://coligo.io/create-url-shortener-with-node-express-mongo/", '20160411185424');
|
||
|
</script>
|
||
|
<!-- END WAYBACK TOOLBAR INSERT -->
|
||
|
<div class="ui inverted menu">
|
||
|
<div class="ui container">
|
||
|
<a href="https://web.archive.org/web/20160411185424/http://coligo.io/" class="header item">
|
||
|
coligo
|
||
|
</a>
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
<div id="container" class="ui fluid grid container">
|
||
|
<div class="sixteen wide column">
|
||
|
<div class="ui huge center aligned header">
|
||
|
Creating a URL Shortener with NodeJs, Express, and MongoDB
|
||
|
<div class="sub header">
|
||
|
<div id="article-info" class="ui small horizontal divided list">
|
||
|
<div class="item">
|
||
|
<div><i class="calendar icon"></i>February 13th, 2016</div>
|
||
|
</div>
|
||
|
<div class="item">
|
||
|
<div><i class="tags icon"></i>nodejs, express, mongodb</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="sixteen wide tablet sixteen wide mobile twelve wide computer column">
|
||
|
<div class="ui fluid card">
|
||
|
<div class="image">
|
||
|
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDAkKCgr/2wBDAQICAgICAgUDAwUKBwYHCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgr/wAARCAGgA1IDASIAAhEBAxEB/8QAHQABAAICAwEBAAAAAAAAAAAAAAcIBQYBAwQJAv/EAGUQAAEDAgMEBQYFCw0LCQkAAAABAgMEBQYHEQgSITETQVFhcQkUIjKBsUJScpGhFRYjMzVic3R2s8EYNDY4Q1NYY4KSstHTFxkkNzlGV3WVtPAlJkRFg5Oiw9QoVFVkhJTE4fH/xAAdAQEAAQUBAQEAAAAAAAAAAAAABgIDBAUHAQgJ/8QAVhEAAgEDAgMEBQULCAYHCQEAAAECAwQRBSEGEjETQVFhByJxkbEUMoGhwSM0NkJScnOys9HwCBUWMzVigpI3OEN0tOEkU1Vjk8LDRVRkg4SUotLT8f/aAAwDAQACEQMRAD8AlgAEDP0MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAByxkkr0ihjc97l0a1qaqvgh5KUYRcpPCRRVq0qNN1KklGKWW20kkurbbSSXi2kcAy0OCr7LCsj2RRrpqjJJeK+Omuh4K+219sl6KvpnRqvqqvFrvBeSmuttY0q8rdlQrRlLwT+HTP0ZIvpXHXB2uXzsrC/pVKq/FjLd468uVFS/wuXu3OgAGyJWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPYFVE608NQe4YBxvN7U+c579AMMAap2gHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8gAFVETVVPbacP3K8rvU0W7FrxmkTRvs7fYbTZ8K220qkqM6edP3WROS/epyT3kZ1jirTNJzBPnqfkx7vzn0Xs3fkjkvHHpk4U4N5reEvlFyv9nTaxF/95PeMfNLmn/dRr9owhcrkqS1SLTwr8J7fSd4N/Sps9ts9sssLvNYWtXd+yTPX0l8V7PoPNecW221q6CNfOJ05xsdwRfvl6veavdr5cry7/C5tI+qFnBiezr9pE423EvF0lKs+yofSl9C6y9r2OLU9K9K/prqqtfT+Sae3lLEowa7nGGVOs/Cc2oZ3RsVXji0U9SkELZJ266PkjRN1vhr63sMjDUWq+Ui9G+OohcmjmqmuncqLyU0A7KWqqqKZKmjqHRSJyexdF8O82d3wHa/J4/JKjjUj3vdN/Rhx8ms4+sl2s/ycdH/m2D0a7nTuYLPNUeYzkt8vlSdN56ODaW2U2smwXfAnBZ7NJ/8ATyu9zv6/nNdmgnppVp6mF8cjfWY9uiobJaMdRv0gvMaMXqnjbwXxTq9hmKyhtd8pW+cRsmjcmscjXcU72uQwbfiHXOHqqt9UpucO6Xf9EukvY8Mjmm+k30h+jG8jpnF9tKtR6RqdZ4XfCr8yqku6bU13tGggzV3wVXUW9NblWoiTjuonptTw+F7OPcYXiiq1U4ouioqaaKT3T9VsNUpdpbTUvFd69q6r4eZ9JcMcY8OcY2fyjSrhVEvnR6Tj5Sg/WXtw4vukwADYEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3UVvrrk7coKV0na9ODU8VXgZLC+H4Lkjq+vYroWu3Y4tdEeqc1XuPFm/n/AJObP9kbdcz8aUdrY5utLRJ6dRUadUcLPSd46ad5Hr7W5U67t7WHNPp47+SXX4HzX6RvT49B1arovD9uq1xBuMpyy4RkusYwjvNx6Ntximmt8NmRpcD1T0R1dXMj7WxN3l+deH0HvgwdZIVRZWyyrr+6S8PmTQpfmn5YpGTSUOS+U6PYiqjLjiSpVN7vSGL9LiDMY+Ui2v8AF73ozNBLRE7lDY7dFBonylRzvpLMdP4kvN6k+Re3H1R3+s4je8TemHiSTndajOjF/ixl2SX+Gkk/fNs+pceFbKmixWZjlaqqiqxXL852OsFoc7ffZ4N7rVYE19x8b75tF5/Yle6S+514qqVeurkffJkT5muREMG7HWPauVEdjW9yPe5ETW7Tqqqv8sr/AKL3st53H6z+MjRVOGuIrl81xqU5PzlVl8ap9q0w/Ztd5LLTqvV/g7f6jifC9nlc6SWysRXJorkjVOHsPixWYrzEs9fNb63Fl8p6mmldHPE+6TtdG9q6K1U3uCopkrNn1nhh56PsmcWKKZW6adFfZ+HzuUf0Xu47wuP1v/2PKfDGvUMSoajNPylVX1qqfYufB9im4RxyRLpp9imX3LqeKpwM9qK6iuKL2NmZ+lP6j5f4S8odtgYQka6HOKquLGr9pvVJFVNX2uajvpJqyy8sVi2jljpM4MqKKuh4JJXYfqFglTv6KRVavgjkKJaZxHab0qnOvDOfqkvtN3Za96XuHpKdnqU6iX4sp9ov8tVS+qSLg19oudsTeraRWs/fGrvN+fq9p5kVF5Lz5HmyM2ssiNoqn3MucZxSV7Y96ey17OgrI06/sbvXTvbvIbRifDlNBTvuluZ0as4zRtT0Vb1qidSldprdVV1QvIcsumcY96fxWx2HgP8AlB3F5qlLSOKLdUqk2oxqxTjHmk0l2kG3yptpc0JOKysrG6wAHJQSQ+pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfunhkqaiOmiT0pHo1vDkqrpqUznGnBzk8JJt+xLL+BZuLilaW869V4jBOTfgopyb+hJn7oLbX3SboKCmdI74S8mt8V6jZrRgmipNJrk5KiT4mn2NPZ8L28O4yccVvsVtVG6RwQM1c7w617VU12743q6jWC1sWBi85XJ6a+CfB9/gcxq6txBxTVlRsI9nS6N5xt/el13X4sT5GvOM/SX6Yr2rYcOU3b2aeJST5fVfTtavVNrfs6azjbL6mful8tdlj3aqZEcjfQgjTVy+zqTxNXvGLLlddYonLTwr8CN3pL4r+hDGOc57le9yucq6q5y6qvipwSPR+D9O03FSt90qeLXqr2R+15fkjqfA/oO4X4W5bm+Su7lb5mvucX/cpvKePyp8z71FBER
|
||
|
</div>
|
||
|
<div class="center aligned content"><a target="_blank" href="https://web.archive.org/web/20160411185424/https://shrinkr.herokuapp.com/"><button class="ui orange huge button"><i class="unhide icon"></i>View Demo</button></a><a target="_blank" href="https://web.archive.org/web/20160411185424/https://github.com/coligo-io/url-shortener-node-mongo-express"><button class="ui blue huge button"><i class="github icon"></i>Get Code</button></a></div>
|
||
|
<div class="content">
|
||
|
<div class="post-content">
|
||
|
<p>In this tutorial we will be creating a URL shortening service similar to sites like <a target="_blank" href="https://web.archive.org/web/20160411185424/https://bitly.com/">bitly.com</a> using NodeJS, Express, and MongoDB. Express will be used to handle our routing and redirection while MongoDB takes care of storing and looking up the shortened links. Have a look at the <a target="_blank" href="https://web.archive.org/web/20160411185424/http://shrinkr.herokuapp.com/">demo</a> to understand what we will building throughout this tutorial.</p>
|
||
|
<p>At a high level the URL shortener works by taking a long URL and applying a hashing algorithm to spit out a shorter version of the URL and stores them in a database for later lookup.</p>
|
||
|
<p><img class="ui image" alt="how url shortening works" src="data:text/html; charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICA8aGVhZD4KICAgIDx0aXRsZT5XYXliYWNrIE1hY2hpbmU8L3RpdGxlPgogICAgPHNjcmlwdD4KICAgIC8qCiAgICBAbGljc3RhcnQgIFRoZSBmb2xsb3dpbmcgaXMgdGhlIGVudGlyZSBsaWNlbnNlIG5vdGljZSBmb3IgdGhlIEphdmFTY3JpcHQgY29kZSBpbiB0aGlzIHBhZ2UuCgogICAgQ29weXJpZ2h0IChDKSAyMDIwIEludGVybmV0IEFyY2hpdmUKCiAgICBUaGUgSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuCiAgICByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBBZmZlcm8KICAgIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pCiAgICBhbnkgbGF0ZXIgdmVyc2lvbi4gIFRoZSBjb2RlIGlzIGRpc3RyaWJ1dGVkIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOwogICAgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTCiAgICBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHUEwgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBBcyBhZGRpdGlvbmFsIHBlcm1pc3Npb24gdW5kZXIgR05VIEFHUEwgdmVyc2lvbiAzIHNlY3Rpb24gNywgeW91CiAgICBtYXkgZGlzdHJpYnV0ZSBub24tc291cmNlIChlLmcuLCBtaW5pbWl6ZWQgb3IgY29tcGFjdGVkKSBmb3JtcyBvZgogICAgdGhhdCBjb2RlIHdpdGhvdXQgdGhlIGNvcHkgb2YgdGhlIEdOVSBBR1BMIG5vcm1hbGx5IHJlcXVpcmVkIGJ5CiAgICBzZWN0aW9uIDQsIHByb3ZpZGVkIHlvdSBpbmNsdWRlIHRoaXMgbGljZW5zZSBub3RpY2UgYW5kIGEgVVJMCiAgICB0aHJvdWdoIHdoaWNoIHJlY2lwaWVudHMgY2FuIGFjY2VzcyB0aGUgQ29ycmVzcG9uZGluZyBTb3VyY2UuCgogICAgQGxpY2VuZCAgVGhlIGFib3ZlIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZSBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlLgogICAgKi8KICAgIDwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICB3aW5kb3cud2ViQ29tcG9uZW50TG9hZGVyQ29uZmlnID0gewogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcmNoaXZlLm9yZycsCiAgICAgIHZlcnNpb246ICcyOWU1NmU4NycKICAgICAgfQogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9qcXVlcnktMS4xMC4yLm1pbi5qcz92MS4xMC4yIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYW5hbHl0aWNzLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9ucG0vanF1ZXJ5LXVpLm1pbi5qcz92MS4xMi4xIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYm9vdHN0cmFwLm1pbi5qcz92My4wLjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9jb21wb25lbnRzL25wbS9jbGlwYm9hcmQvZGlzdC9jbGlwYm9hcmQuanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9pZS1kb20tbm9kZS1yZW1vdmUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb2JpbGUtdG9wLW5hdi5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3BvbHlmaWxsLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvY29tcG9uZW50cy9ucG0vQHdlYmNvbXBvbmVudHMvd2ViY29tcG9uZW50c2pzL3dlYmNvbXBvbmVudHMtYnVuZGxlLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb3JlLWZhY2V0cy5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JhZGlvLXBsYXllci1jb250cm9sbGVyLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0L3VtZC9yZWFjdC5wcm9kdWN0aW9uLm1pbi5qcz92MTYuNy4wIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0LWRvbS91bWQvcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzP3YxNi43LjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoa
|
||
|
<p>Once someone visits the short version of the URL, our server will query the database for that hash key to retrieve the original, longer URL and redirect them to it.</p>
|
||
|
<p><img class="ui image" alt="how url shortening redirects works" src="data:text/html; charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICA8aGVhZD4KICAgIDx0aXRsZT5XYXliYWNrIE1hY2hpbmU8L3RpdGxlPgogICAgPHNjcmlwdD4KICAgIC8qCiAgICBAbGljc3RhcnQgIFRoZSBmb2xsb3dpbmcgaXMgdGhlIGVudGlyZSBsaWNlbnNlIG5vdGljZSBmb3IgdGhlIEphdmFTY3JpcHQgY29kZSBpbiB0aGlzIHBhZ2UuCgogICAgQ29weXJpZ2h0IChDKSAyMDIwIEludGVybmV0IEFyY2hpdmUKCiAgICBUaGUgSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuCiAgICByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBBZmZlcm8KICAgIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pCiAgICBhbnkgbGF0ZXIgdmVyc2lvbi4gIFRoZSBjb2RlIGlzIGRpc3RyaWJ1dGVkIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOwogICAgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTCiAgICBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHUEwgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBBcyBhZGRpdGlvbmFsIHBlcm1pc3Npb24gdW5kZXIgR05VIEFHUEwgdmVyc2lvbiAzIHNlY3Rpb24gNywgeW91CiAgICBtYXkgZGlzdHJpYnV0ZSBub24tc291cmNlIChlLmcuLCBtaW5pbWl6ZWQgb3IgY29tcGFjdGVkKSBmb3JtcyBvZgogICAgdGhhdCBjb2RlIHdpdGhvdXQgdGhlIGNvcHkgb2YgdGhlIEdOVSBBR1BMIG5vcm1hbGx5IHJlcXVpcmVkIGJ5CiAgICBzZWN0aW9uIDQsIHByb3ZpZGVkIHlvdSBpbmNsdWRlIHRoaXMgbGljZW5zZSBub3RpY2UgYW5kIGEgVVJMCiAgICB0aHJvdWdoIHdoaWNoIHJlY2lwaWVudHMgY2FuIGFjY2VzcyB0aGUgQ29ycmVzcG9uZGluZyBTb3VyY2UuCgogICAgQGxpY2VuZCAgVGhlIGFib3ZlIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZSBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlLgogICAgKi8KICAgIDwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICB3aW5kb3cud2ViQ29tcG9uZW50TG9hZGVyQ29uZmlnID0gewogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcmNoaXZlLm9yZycsCiAgICAgIHZlcnNpb246ICcyOWU1NmU4NycKICAgICAgfQogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9qcXVlcnktMS4xMC4yLm1pbi5qcz92MS4xMC4yIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYW5hbHl0aWNzLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9ucG0vanF1ZXJ5LXVpLm1pbi5qcz92MS4xMi4xIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYm9vdHN0cmFwLm1pbi5qcz92My4wLjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9jb21wb25lbnRzL25wbS9jbGlwYm9hcmQvZGlzdC9jbGlwYm9hcmQuanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9pZS1kb20tbm9kZS1yZW1vdmUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb2JpbGUtdG9wLW5hdi5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3BvbHlmaWxsLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvY29tcG9uZW50cy9ucG0vQHdlYmNvbXBvbmVudHMvd2ViY29tcG9uZW50c2pzL3dlYmNvbXBvbmVudHMtYnVuZGxlLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb3JlLWZhY2V0cy5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JhZGlvLXBsYXllci1jb250cm9sbGVyLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0L3VtZC9yZWFjdC5wcm9kdWN0aW9uLm1pbi5qcz92MTYuNy4wIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0LWRvbS91bWQvcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzP3YxNi43LjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0
|
||
|
<p>You can get the full, working code for the URL shortener on <a target="_blank" href="https://web.archive.org/web/20160411185424/https://github.com/coligo-io/url-shortener-node-mongo-express">GitHub</a> or fire up your favorite editor and follow along!</p>
|
||
|
<h1 id="installing-mongodb">Installing MongoDB</h1>
|
||
|
<p>Let's start off by installing MongoDB locally, which is a NoSQL database that will store our links and their hashes.</p>
|
||
|
<p>Although any type of database would work perfectly fine, whether it is a relational one like MySQL or a non-relational one like Mongo, we will go with the non-relational alternative as there plenty of tutorials already out there on building URL shorteners using relational databases.</p>
|
||
|
<p>The MongoDB website has excellent instructions on installing the database on all 3 platforms: <a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/manual/tutorial/install-mongodb-on-os-x/">OS X</a>,
|
||
|
<a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/manual/tutorial/install-mongodb-on-windows/">Windows</a>,
|
||
|
and <a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/manual/administration/install-on-linux/">Linux</a></p>
|
||
|
<p>The process is surprisingly simple and takes just a couple of minutes. For the purpose of this tutorial I will go over installing MongoDB on OS X using Homebrew.</p>
|
||
|
<p>Type the following commands in your terminal to update your packages and install mongodb:</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-keyword">brew </span>update
|
||
|
<span class="hljs-keyword">brew </span><span class="hljs-keyword">install </span>mongodb
|
||
|
</code></pre>
|
||
|
<p>If you wish to install it on your Mac without Homebrew you can check out the manual instructions <a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/manual/tutorial/install-mongodb-on-os-x/#install-mongodb-manually">here</a>.</p>
|
||
|
<p>Once the installation completes successfully, we will need to create the directory to which the <code>mongod</code> process will write data. The default directory that <code>mongod</code> uses is <strong>/data/db</strong>, so lets go ahead and create that:</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-title">mkdir</span> -p /<span class="hljs-class"><span class="hljs-keyword">data</span>/db</span>
|
||
|
</code></pre>
|
||
|
<p>We can now start up our MongoDB database by typing:</p>
|
||
|
<pre><code class="lang-bash hljs">mongod
|
||
|
</code></pre>
|
||
|
<p>Note: if you run into any 'permission denied' errors, make sure that the user account running <code>mongod</code> has <strong>read</strong> and <strong>write</strong> permissions to the <strong>/data/db</strong> directory.</p>
|
||
|
<p>Once you execute the <code>mongod</code> command you should see a similar output:</p>
|
||
|
<p><img class="ui image" alt="mongod output" src="data:text/html; charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICA8aGVhZD4KICAgIDx0aXRsZT5XYXliYWNrIE1hY2hpbmU8L3RpdGxlPgogICAgPHNjcmlwdD4KICAgIC8qCiAgICBAbGljc3RhcnQgIFRoZSBmb2xsb3dpbmcgaXMgdGhlIGVudGlyZSBsaWNlbnNlIG5vdGljZSBmb3IgdGhlIEphdmFTY3JpcHQgY29kZSBpbiB0aGlzIHBhZ2UuCgogICAgQ29weXJpZ2h0IChDKSAyMDIwIEludGVybmV0IEFyY2hpdmUKCiAgICBUaGUgSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuCiAgICByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBBZmZlcm8KICAgIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pCiAgICBhbnkgbGF0ZXIgdmVyc2lvbi4gIFRoZSBjb2RlIGlzIGRpc3RyaWJ1dGVkIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOwogICAgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTCiAgICBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHUEwgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBBcyBhZGRpdGlvbmFsIHBlcm1pc3Npb24gdW5kZXIgR05VIEFHUEwgdmVyc2lvbiAzIHNlY3Rpb24gNywgeW91CiAgICBtYXkgZGlzdHJpYnV0ZSBub24tc291cmNlIChlLmcuLCBtaW5pbWl6ZWQgb3IgY29tcGFjdGVkKSBmb3JtcyBvZgogICAgdGhhdCBjb2RlIHdpdGhvdXQgdGhlIGNvcHkgb2YgdGhlIEdOVSBBR1BMIG5vcm1hbGx5IHJlcXVpcmVkIGJ5CiAgICBzZWN0aW9uIDQsIHByb3ZpZGVkIHlvdSBpbmNsdWRlIHRoaXMgbGljZW5zZSBub3RpY2UgYW5kIGEgVVJMCiAgICB0aHJvdWdoIHdoaWNoIHJlY2lwaWVudHMgY2FuIGFjY2VzcyB0aGUgQ29ycmVzcG9uZGluZyBTb3VyY2UuCgogICAgQGxpY2VuZCAgVGhlIGFib3ZlIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZSBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlLgogICAgKi8KICAgIDwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICB3aW5kb3cud2ViQ29tcG9uZW50TG9hZGVyQ29uZmlnID0gewogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcmNoaXZlLm9yZycsCiAgICAgIHZlcnNpb246ICcyOWU1NmU4NycKICAgICAgfQogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9qcXVlcnktMS4xMC4yLm1pbi5qcz92MS4xMC4yIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYW5hbHl0aWNzLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9ucG0vanF1ZXJ5LXVpLm1pbi5qcz92MS4xMi4xIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYm9vdHN0cmFwLm1pbi5qcz92My4wLjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9jb21wb25lbnRzL25wbS9jbGlwYm9hcmQvZGlzdC9jbGlwYm9hcmQuanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9pZS1kb20tbm9kZS1yZW1vdmUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb2JpbGUtdG9wLW5hdi5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3BvbHlmaWxsLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvY29tcG9uZW50cy9ucG0vQHdlYmNvbXBvbmVudHMvd2ViY29tcG9uZW50c2pzL3dlYmNvbXBvbmVudHMtYnVuZGxlLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb3JlLWZhY2V0cy5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JhZGlvLXBsYXllci1jb250cm9sbGVyLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0L3VtZC9yZWFjdC5wcm9kdWN0aW9uLm1pbi5qcz92MTYuNy4wIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0LWRvbS91bWQvcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzP3YxNi43LjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9p
|
||
|
<h1 id="creating-the-nodejs-express-project">Creating the NodeJS + Express Project</h1>
|
||
|
<p>Now that we have our MongoDB instance up and running, let's create the project structure for our Node + Express application.</p>
|
||
|
<p>Start off by creating a new empty directory and <code>cd</code>-ing into it, we'll call this directory <em>url-shortener</em>:</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-built_in">mkdir</span> url-shortener
|
||
|
<span class="hljs-built_in">cd</span> url-shortener
|
||
|
</code></pre>
|
||
|
<p>Let's initialize a <em>package.json</em> file to keep track of our dependencies installed via NPM as well as some basic project metadata:</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-built_in">npm</span> init
|
||
|
</code></pre>
|
||
|
<p>You will be prompted with a couple of questions which you could answer however you wish, hit enter to accept the defaults shown in the parentheses:</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-string">name:</span> (url-shortener)
|
||
|
<span class="hljs-string">version:</span> (<span class="hljs-number">1.0</span><span class="hljs-number">.0</span>)
|
||
|
<span class="hljs-string">description:</span> A NodeJS, Express, and MongoDB URL Shortening Service
|
||
|
entry <span class="hljs-string">point:</span> (index.js) app.js
|
||
|
test <span class="hljs-string">command:</span>
|
||
|
git <span class="hljs-string">repository:</span>
|
||
|
<span class="hljs-string">keywords:</span>
|
||
|
<span class="hljs-string">author:</span> Fady Makram
|
||
|
<span class="hljs-string">license:</span> (ISC) MIT
|
||
|
</code></pre>
|
||
|
<p>Once you confirm your inputs, a package.json file will be created in the url-shortener directory with <strong>app.js</strong> being our entry point.</p>
|
||
|
<p>Install Express by typing <code>npm install express --save</code>. The <code>--save</code> flag will add express to your package.json dependencies.</p>
|
||
|
<p>Using your favorite text editor, create the main entry point for our Node application <strong>app.js</strong> as well as the folder structure shown below:</p>
|
||
|
<p><img class="ui image" alt="folder structure" src="data:image/png;base64,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
|
||
|
<p>Our <strong>app.js</strong> file will have 3 routes which Express will handle:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// require and instantiate express</span>
|
||
|
<span class="hljs-keyword">var</span> express = require(<span class="hljs-string">'express'</span>);
|
||
|
<span class="hljs-keyword">var</span> app = express();
|
||
|
|
||
|
app.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-comment">// route to serve up the homepage (index.html)</span>
|
||
|
});
|
||
|
|
||
|
app.post(<span class="hljs-string">'/api/shorten'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-comment">// route to create and return a shortened URL given a long URL</span>
|
||
|
});
|
||
|
|
||
|
app.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/:encoded_id'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-comment">// route to redirect the visitor to their original URL given the short URL</span>
|
||
|
});
|
||
|
|
||
|
<span class="hljs-keyword">var</span> server = app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
|
||
|
console.log(<span class="hljs-string">'Server listening on port 3000'</span>);
|
||
|
});
|
||
|
</code></pre>
|
||
|
<h1 id="creating-the-front-end">Creating the Front End</h1>
|
||
|
<p>If you haven't already checked out the <a target="_blank" href="https://web.archive.org/web/20160411185424/http://shrinkr.herokuapp.com/">demo</a>, the front end we'll be creating is going to be quite simple:</p>
|
||
|
<p><img class="ui image" alt="url shortener front end" src="data:text/html; charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICA8aGVhZD4KICAgIDx0aXRsZT5XYXliYWNrIE1hY2hpbmU8L3RpdGxlPgogICAgPHNjcmlwdD4KICAgIC8qCiAgICBAbGljc3RhcnQgIFRoZSBmb2xsb3dpbmcgaXMgdGhlIGVudGlyZSBsaWNlbnNlIG5vdGljZSBmb3IgdGhlIEphdmFTY3JpcHQgY29kZSBpbiB0aGlzIHBhZ2UuCgogICAgQ29weXJpZ2h0IChDKSAyMDIwIEludGVybmV0IEFyY2hpdmUKCiAgICBUaGUgSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuCiAgICByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBBZmZlcm8KICAgIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pCiAgICBhbnkgbGF0ZXIgdmVyc2lvbi4gIFRoZSBjb2RlIGlzIGRpc3RyaWJ1dGVkIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOwogICAgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTCiAgICBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHUEwgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBBcyBhZGRpdGlvbmFsIHBlcm1pc3Npb24gdW5kZXIgR05VIEFHUEwgdmVyc2lvbiAzIHNlY3Rpb24gNywgeW91CiAgICBtYXkgZGlzdHJpYnV0ZSBub24tc291cmNlIChlLmcuLCBtaW5pbWl6ZWQgb3IgY29tcGFjdGVkKSBmb3JtcyBvZgogICAgdGhhdCBjb2RlIHdpdGhvdXQgdGhlIGNvcHkgb2YgdGhlIEdOVSBBR1BMIG5vcm1hbGx5IHJlcXVpcmVkIGJ5CiAgICBzZWN0aW9uIDQsIHByb3ZpZGVkIHlvdSBpbmNsdWRlIHRoaXMgbGljZW5zZSBub3RpY2UgYW5kIGEgVVJMCiAgICB0aHJvdWdoIHdoaWNoIHJlY2lwaWVudHMgY2FuIGFjY2VzcyB0aGUgQ29ycmVzcG9uZGluZyBTb3VyY2UuCgogICAgQGxpY2VuZCAgVGhlIGFib3ZlIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZSBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlLgogICAgKi8KICAgIDwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICB3aW5kb3cud2ViQ29tcG9uZW50TG9hZGVyQ29uZmlnID0gewogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcmNoaXZlLm9yZycsCiAgICAgIHZlcnNpb246ICcyOWU1NmU4NycKICAgICAgfQogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9qcXVlcnktMS4xMC4yLm1pbi5qcz92MS4xMC4yIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYW5hbHl0aWNzLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9ucG0vanF1ZXJ5LXVpLm1pbi5qcz92MS4xMi4xIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYm9vdHN0cmFwLm1pbi5qcz92My4wLjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9jb21wb25lbnRzL25wbS9jbGlwYm9hcmQvZGlzdC9jbGlwYm9hcmQuanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9pZS1kb20tbm9kZS1yZW1vdmUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb2JpbGUtdG9wLW5hdi5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3BvbHlmaWxsLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvY29tcG9uZW50cy9ucG0vQHdlYmNvbXBvbmVudHMvd2ViY29tcG9uZW50c2pzL3dlYmNvbXBvbmVudHMtYnVuZGxlLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb3JlLWZhY2V0cy5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JhZGlvLXBsYXllci1jb250cm9sbGVyLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0L3VtZC9yZWFjdC5wcm9kdWN0aW9uLm1pbi5qcz92MTYuNy4wIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0LWRvbS91bWQvcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzP3YxNi43LjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaX
|
||
|
<p>It uses some simple Bootstrap and CSS for basic styling to give our page some life. For the purpose of this tutorial we won't go through the process of recreating the front end as it's mostly just a basic HTML form to submit the URL for shortening.</p>
|
||
|
<p>We can save this HTML file in our <strong>views</strong> folder as <strong>index.html</strong>:</p>
|
||
|
<pre><code class="lang-html hljs"><span class="hljs-meta"><!DOCTYPE html></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">html</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">head</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">title</span>></span>URL Shortener - coligo.io<span class="hljs-tag"></<span class="hljs-name">title</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'https://fonts.googleapis.com/css?family=Raleway'</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">'stylesheet'</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text/css'</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"css/styles.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">head</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">body</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"site-wrapper"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"site-wrapper-inner"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"main-container"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inner cover"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"glyphicon glyphicon-link"</span>></span><span class="hljs-tag"></<span class="hljs-name">span</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">h1</span>></span>URL Shortener<span class="hljs-tag"></<span class="hljs-name">h1</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">h4</span>></span>coligo.io<span class="hljs-tag"></<span class="hljs-name">h4</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-12"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group input-group-lg"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"url-field"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Paste a link..."</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group-btn"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-shorten"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>></span>SHORTEN<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">span</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-12"</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"link"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||
|
|
||
|
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://code.jquery.com/jquery-2.1.4.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
||
|
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"javascripts/shorten.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">body</span>></span>
|
||
|
<span class="hljs-tag"></<span class="hljs-name">html</span>></span>
|
||
|
</code></pre>
|
||
|
<p>Let's do the same with the CSS file. Create a file called <strong>styles.css</strong> in the <strong>public/css</strong> directory:</p>
|
||
|
<pre><code class="lang-css hljs"><span class="hljs-selector-class">.btn</span><span class="hljs-selector-pseudo">:focus</span>, <span class="hljs-selector-class">.btn-shorten</span><span class="hljs-selector-pseudo">:focus</span>{
|
||
|
<span class="hljs-attribute">outline</span>: <span class="hljs-number">0</span> <span class="hljs-meta">!important</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-tag">html</span>,
|
||
|
<span class="hljs-selector-tag">body</span> {
|
||
|
<span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
|
||
|
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4791D2</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-tag">body</span> {
|
||
|
<span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
|
||
|
<span class="hljs-attribute">text-align</span>: center;
|
||
|
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, sans-serif;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.btn-shorten</span> {
|
||
|
<span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>;
|
||
|
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#F89406</span>;
|
||
|
<span class="hljs-attribute">border</span>: none;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.btn-shorten</span><span class="hljs-selector-pseudo">:hover</span>,
|
||
|
<span class="hljs-selector-class">.btn-shorten</span><span class="hljs-selector-pseudo">:focus</span>,
|
||
|
<span class="hljs-selector-class">.btn-shorten</span><span class="hljs-selector-pseudo">:active</span>,
|
||
|
<span class="hljs-selector-class">.btn-shorten</span><span class="hljs-selector-class">.active</span> {
|
||
|
<span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>;
|
||
|
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FA8900</span>;
|
||
|
<span class="hljs-attribute">border</span>: none;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.site-wrapper</span> {
|
||
|
<span class="hljs-attribute">display</span>: table;
|
||
|
<span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
|
||
|
<span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
|
||
|
<span class="hljs-attribute">min-height</span>: <span class="hljs-number">100%</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.site-wrapper-inner</span> {
|
||
|
<span class="hljs-attribute">display</span>: table-cell;
|
||
|
<span class="hljs-attribute">vertical-align</span>: top;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.main-container</span> {
|
||
|
<span class="hljs-attribute">margin-right</span>: auto;
|
||
|
<span class="hljs-attribute">margin-left</span>: auto;
|
||
|
<span class="hljs-attribute">margin-top</span>: <span class="hljs-number">80px</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.inner</span> {
|
||
|
<span class="hljs-attribute">padding</span>: <span class="hljs-number">30px</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.inner</span> <span class="hljs-selector-tag">h4</span> {
|
||
|
<span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">30px</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.glyphicon-link</span> {
|
||
|
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">2em</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-class">.inner</span> <span class="hljs-selector-tag">h1</span> {
|
||
|
<span class="hljs-attribute">margin-top</span>: <span class="hljs-number">5px</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-id">#link</span> {
|
||
|
<span class="hljs-attribute">display</span>: none;
|
||
|
<span class="hljs-attribute">padding-top</span>: <span class="hljs-number">15px</span>;
|
||
|
}
|
||
|
|
||
|
<span class="hljs-selector-id">#link</span> <span class="hljs-selector-tag">a</span>{
|
||
|
<span class="hljs-attribute">color</span>: <span class="hljs-number">#F89406</span>;
|
||
|
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5em</span>;
|
||
|
<span class="hljs-attribute">margin-right</span>: <span class="hljs-number">20px</span>;
|
||
|
}
|
||
|
|
||
|
@<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">768px</span>) {
|
||
|
<span class="hljs-selector-class">.main-container</span> {
|
||
|
<span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">992px</span>) {
|
||
|
<span class="hljs-selector-class">.main-container</span> {
|
||
|
<span class="hljs-attribute">width</span>: <span class="hljs-number">700px</span>;
|
||
|
}
|
||
|
}
|
||
|
</code></pre>
|
||
|
<p>Now that we have the basic HTML structure and the styling for our front end in the right folders, let's serve the files using the Express routes we saw earlier in our <strong>app.js</strong> file:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// require and instantiate express</span>
|
||
|
<span class="hljs-keyword">var</span> express = <span class="hljs-keyword">require</span>(<span class="hljs-string">'express'</span>);
|
||
|
<span class="hljs-keyword">var</span> app = express();
|
||
|
<span class="hljs-comment">// we'll need the path module to correctly concatenate our paths</span>
|
||
|
<span class="hljs-keyword">var</span> path = <span class="hljs-keyword">require</span>(<span class="hljs-string">'path'</span>);
|
||
|
|
||
|
<span class="hljs-comment">// tell Express to serve files from our public folder</span>
|
||
|
app.<span class="hljs-keyword">use</span>(express.<span class="hljs-keyword">static</span>(path.join(__dirname, <span class="hljs-string">'public'</span>)));
|
||
|
|
||
|
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-comment">// route to serve up the homepage (index.html)</span>
|
||
|
res.sendFile(path.join(__dirname, <span class="hljs-string">'views/index.html'</span>));
|
||
|
});
|
||
|
|
||
|
<span class="hljs-keyword">var</span> server = app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
|
||
|
console.log(<span class="hljs-string">'Server listening on port 3000'</span>);
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>Saving the file, typing <code>node app.js</code> to start the server and going to <code>http://localhost:3000</code>, you should see the basic homepage for our URL Shortener.</p>
|
||
|
<h1 id="developing-a-shortening-algorithm">Developing a Shortening Algorithm</h1>
|
||
|
<p>This is the interesting part where the actual URL shortening happens.</p>
|
||
|
<p>Before we dive into the implementation, let's explore the basic theory behind URL shortening. Our requirement is:</p>
|
||
|
<blockquote>
|
||
|
<p>given a long URL such as "<a target="_blank" href="https://web.archive.org/web/20160411185424/http://stackoverflow.com/questions/tagged/node.js">http://stackoverflow.com/questions/tagged/node.js</a>", generate a <strong>unique</strong> URL that is <strong>shorter</strong> such as "coligo.io/3Ys".</p>
|
||
|
</blockquote>
|
||
|
<p>First let's look at actually generating a <strong>unique</strong> key from our original URL, in this case <strong>3Ys</strong>, which is at the end of our coligo.io URL.</p>
|
||
|
<p>To satisfy the <em>uniqueness</em> requirement, we can leverage a special class of functions called <a target="_blank" href="https://web.archive.org/web/20160411185424/https://en.wikipedia.org/wiki/Bijection">bijective functions</a>, which guarantee a 1-to-1 mapping. For our purposes a bijective function basically says:</p>
|
||
|
<blockquote>
|
||
|
<p>A long URL is mapped to exactly one key, and a key is mapped to exactly one long URL</p>
|
||
|
</blockquote>
|
||
|
<p>We will be using base encoding/decoding as our bijective function, specifically base58. We will be converting a unique integer ID (which is in base10) to it's equivalent in base58. The base58 alphabet we will be using is:</p>
|
||
|
<pre><code class="hljs"><span class="hljs-number">123456789</span>abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
|
||
|
</code></pre><p>It is just the numbers 1-9, a-z, and A-Z, giving us a total of 58 characters, hence the 58 in base<strong>58</strong>. We are excluding 0, l, O to avoid confusion when sharing the URL over the phone or copying it manually.</p>
|
||
|
<p>If you're not familiar with bases you can check out this <a target="_blank" href="https://web.archive.org/web/20160411185424/http://code.tutsplus.com/tutorials/base-what-a-practical-introduction-to-base-encoding--net-27590">tutorial</a>.</p>
|
||
|
<p>Now to put it all together to generate a <strong>shorter</strong> URL, our solution will be as follows:</p>
|
||
|
<ol>
|
||
|
<li>Create a global auto incremented integer</li>
|
||
|
<li>Every time a new URL is shortened and added to our database, we'll increment that global number (base 10) and use it as our unique ID for that entry in the DB</li>
|
||
|
<li>Base58 encode that unique ID to generate a unique, shorter URL</li>
|
||
|
</ol>
|
||
|
<p>For example: An entry with the unique ID <em>10002</em> (base 10) will result in a base58 encoding of <em>3Ys</em>. So if you store a lot of URLs, say <em>100,000,000</em>, that would generate a shortened hash: <em>9QwvW</em></p>
|
||
|
<p>Now that we have the theory out of the way and our solution mocked up, we can create a node module for our base encoding and decoding. Let's call this file <strong>base58.js</strong> and save it in our url-shortener directory.</p>
|
||
|
<p>Our alphabet as mentioned earlier (1-9, a-z, A-Z, excluding 0, l, O):</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-variable"><span class="hljs-keyword">var</span> alphabet</span> = <span class="hljs-string">"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"</span>;
|
||
|
<span class="hljs-variable"><span class="hljs-keyword">var</span> base</span> = alphabet.length; <span class="hljs-comment">// base is the length of the alphabet (58 in this case)</span>
|
||
|
</code></pre>
|
||
|
<p>In fact, feel free to define your own alphabet and base since the code we're going to use for encoding and decoding are generic enough to handle any base.</p>
|
||
|
<p>The generic encode function that we'll be using to turn the unique database entry ID into a base58 string for a shorter and unique URL:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// utility function to convert base 10 integer to base 58 string</span>
|
||
|
function encode(<span class="hljs-built_in">num</span>){
|
||
|
<span class="hljs-keyword">var</span> encoded = <span class="hljs-string">''</span>;
|
||
|
<span class="hljs-keyword">while</span> (<span class="hljs-built_in">num</span>){
|
||
|
<span class="hljs-keyword">var</span> remainder = <span class="hljs-built_in">num</span> % base;
|
||
|
<span class="hljs-built_in">num</span> = Math.floor(<span class="hljs-built_in">num</span> / base);
|
||
|
encoded = alphabet[remainder].toString() + encoded;
|
||
|
}
|
||
|
<span class="hljs-keyword">return</span> encoded;
|
||
|
}
|
||
|
</code></pre>
|
||
|
<p>The decode function to convert the base58 key at the end of a URL (coligo.io/<strong>3Ys</strong>) to it's equivalent in base10 to retrieve the long URL from the MongoDB:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// utility function to convert a base 58 string to base 10 integer</span>
|
||
|
function decode(<span class="hljs-keyword">str</span>){
|
||
|
var decoded = <span class="hljs-number">0</span><span class="hljs-comment">;</span>
|
||
|
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">str</span>){
|
||
|
var index = alphabet.indexOf(<span class="hljs-keyword">str</span>[<span class="hljs-number">0</span>])<span class="hljs-comment">;</span>
|
||
|
var power = <span class="hljs-keyword">str</span>.<span class="hljs-keyword">length</span> - <span class="hljs-number">1</span><span class="hljs-comment">;</span>
|
||
|
decoded += index * (Math.pow(base, power))<span class="hljs-comment">;</span>
|
||
|
<span class="hljs-keyword">str</span> = <span class="hljs-keyword">str</span>.substring(<span class="hljs-number">1</span>)<span class="hljs-comment">;</span>
|
||
|
}
|
||
|
<span class="hljs-keyword">return</span> decoded<span class="hljs-comment">;</span>
|
||
|
}
|
||
|
</code></pre>
|
||
|
<p>Expose the 2 functions for use in our app.js when handling the routes:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-function"><span class="hljs-keyword">module</span>.<span class="hljs-title">exports</span>.<span class="hljs-title">encode</span> =</span> encode;
|
||
|
<span class="hljs-function"><span class="hljs-keyword">module</span>.<span class="hljs-title">exports</span>.<span class="hljs-title">decode</span> =</span> decode;
|
||
|
</code></pre>
|
||
|
<h1 id="creating-the-database-and-models">Creating the database and models</h1>
|
||
|
<p>So far, we've installed MongoDB and created a simple front end which is served up using Express. We then went on to developed a shortening algorithm to shrink and expand our URLs.</p>
|
||
|
<p>Let's actually put MongoDB to work by creating a database and 2 collections: one to store our URLs that we're shortening and one to keep track of the global integer that acts as an ID for each entry in the URLs collection.</p>
|
||
|
<p>An example of our <strong>urls</strong> collection:</p>
|
||
|
<table class="ui collapsing table"><thead><tr>
|
||
|
<th>_id</th>
|
||
|
<th style="text-align:center">long_url</th>
|
||
|
<th style="text-align:center">created_at</th>
|
||
|
</tr>
|
||
|
</thead><tbody><tr>
|
||
|
<td>....</td>
|
||
|
<td style="text-align:center">....</td>
|
||
|
<td style="text-align:center">....</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>10002</td>
|
||
|
<td style="text-align:center"><a target="_blank" href="https://web.archive.org/web/20160411185424/http://stackoverflow.com/questions/tagged/node.js">http://stackoverflow.com/questions/tagged/node.js</a></td>
|
||
|
<td style="text-align:center">2015-12-26 11:27</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>10003</td>
|
||
|
<td style="text-align:center"><a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/getting-started/node/">https://docs.mongodb.org/getting-started/node/</a></td>
|
||
|
<td style="text-align:center">2015-12-27 12:14</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>10004</td>
|
||
|
<td style="text-align:center"><a target="_blank" href="https://web.archive.org/web/20160411185424/http://expressjs.com/en/starter/basic-routing.html">http://expressjs.com/en/starter/basic-routing.html</a></td>
|
||
|
<td style="text-align:center">2015-12-28 16:12</td>
|
||
|
</tr>
|
||
|
</tbody></table><p>Our simple <strong>counters</strong> collection that keeps track of the <em>last</em> <em>_id</em> we inserted into our <em>urls</em> collection:</p>
|
||
|
<table class="ui collapsing table"><thead><tr>
|
||
|
<th>_id</th>
|
||
|
<th style="text-align:center">seq</th>
|
||
|
</tr>
|
||
|
</thead><tbody><tr>
|
||
|
<td>url_count</td>
|
||
|
<td style="text-align:center">10004</td>
|
||
|
</tr>
|
||
|
</tbody></table><p>Every time a user creates a short URL:</p>
|
||
|
<ol>
|
||
|
<li>Our server will check the value of the <em>url_count</em> in our <em>counters</em> collection</li>
|
||
|
<li>Increment it by 1 and insert that new globally unique ID as the <em>_id</em> of the new entry in the <em>urls</em> collection</li>
|
||
|
</ol>
|
||
|
<p>If you're more familiar with relational databases like MySQL, Postgres, SQLServer, etc.. you might be wondering why we need a separate collection to keep track of that global auto incremented ID. Unlike MySQL, for instance, which allows you to define a column as an auto incremented integer, MongoDB doesn't support auto incremented fields. Instead, MongoDB has a unique field called <strong>_id</strong> that acts as a primary key, referred to as an <a target="_blank" href="https://web.archive.org/web/20160411185424/https://docs.mongodb.org/v3.0/reference/object-id/">ObjectID</a>: a 12-byte BSON type.</p>
|
||
|
<p>We create a separate counters collection as opposed to just reading the last value of the <em>urls</em> collection to leverage the atomicity of the findAndModify method in MongoDB. This allows our application to handle concurrent URL shortening requests by atomically incrementing the <strong>seq</strong> field and returning the new value for use in our urls collection.</p>
|
||
|
<p>Let's go ahead and start creating these collections and their respective models. We'll start off by creating the counters collection via the <code>mongo</code> command line tool since we only need to insert a single item in it, the <em>url_count</em>.</p>
|
||
|
<p>If you don't already have an instance of MongoDB running from our previous steps, go ahead and type <code>mongod</code> in your terminal to start it up.</p>
|
||
|
<p>In a separate terminal type <code>mongo</code> to connect to the running instance of MongoDB:</p>
|
||
|
<p><img class="ui image" alt="mongod and mongo commands" src="data:text/html; charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICA8aGVhZD4KICAgIDx0aXRsZT5XYXliYWNrIE1hY2hpbmU8L3RpdGxlPgogICAgPHNjcmlwdD4KICAgIC8qCiAgICBAbGljc3RhcnQgIFRoZSBmb2xsb3dpbmcgaXMgdGhlIGVudGlyZSBsaWNlbnNlIG5vdGljZSBmb3IgdGhlIEphdmFTY3JpcHQgY29kZSBpbiB0aGlzIHBhZ2UuCgogICAgQ29weXJpZ2h0IChDKSAyMDIwIEludGVybmV0IEFyY2hpdmUKCiAgICBUaGUgSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZSBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuCiAgICByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBBZmZlcm8KICAgIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCiAgICBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pCiAgICBhbnkgbGF0ZXIgdmVyc2lvbi4gIFRoZSBjb2RlIGlzIGRpc3RyaWJ1dGVkIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOwogICAgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTCiAgICBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHUEwgZm9yIG1vcmUgZGV0YWlscy4KCiAgICBBcyBhZGRpdGlvbmFsIHBlcm1pc3Npb24gdW5kZXIgR05VIEFHUEwgdmVyc2lvbiAzIHNlY3Rpb24gNywgeW91CiAgICBtYXkgZGlzdHJpYnV0ZSBub24tc291cmNlIChlLmcuLCBtaW5pbWl6ZWQgb3IgY29tcGFjdGVkKSBmb3JtcyBvZgogICAgdGhhdCBjb2RlIHdpdGhvdXQgdGhlIGNvcHkgb2YgdGhlIEdOVSBBR1BMIG5vcm1hbGx5IHJlcXVpcmVkIGJ5CiAgICBzZWN0aW9uIDQsIHByb3ZpZGVkIHlvdSBpbmNsdWRlIHRoaXMgbGljZW5zZSBub3RpY2UgYW5kIGEgVVJMCiAgICB0aHJvdWdoIHdoaWNoIHJlY2lwaWVudHMgY2FuIGFjY2VzcyB0aGUgQ29ycmVzcG9uZGluZyBTb3VyY2UuCgogICAgQGxpY2VuZCAgVGhlIGFib3ZlIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZSBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlLgogICAgKi8KICAgIDwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICB3aW5kb3cud2ViQ29tcG9uZW50TG9hZGVyQ29uZmlnID0gewogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcmNoaXZlLm9yZycsCiAgICAgIHZlcnNpb246ICcyOWU1NmU4NycKICAgICAgfQogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9qcXVlcnktMS4xMC4yLm1pbi5qcz92MS4xMC4yIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYW5hbHl0aWNzLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9ucG0vanF1ZXJ5LXVpLm1pbi5qcz92MS4xMi4xIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYm9vdHN0cmFwLm1pbi5qcz92My4wLjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9jb21wb25lbnRzL25wbS9jbGlwYm9hcmQvZGlzdC9jbGlwYm9hcmQuanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9pZS1kb20tbm9kZS1yZW1vdmUtcG9seWZpbGwubWluLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb2JpbGUtdG9wLW5hdi5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3BvbHlmaWxsLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvY29tcG9uZW50cy9ucG0vQHdlYmNvbXBvbmVudHMvd2ViY29tcG9uZW50c2pzL3dlYmNvbXBvbmVudHMtYnVuZGxlLmpzP3Y9NWIzZDFhNjIiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNoaXZlLm9yZy9pbmNsdWRlcy9idWlsZC9qcy9tb3JlLWZhY2V0cy5taW4uanM/dj01YjNkMWE2MiIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSIvL2FyY2hpdmUub3JnL2luY2x1ZGVzL2J1aWxkL2pzL3JhZGlvLXBsYXllci1jb250cm9sbGVyLm1pbi5qcz92PTViM2QxYTYyIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0L3VtZC9yZWFjdC5wcm9kdWN0aW9uLm1pbi5qcz92MTYuNy4wIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ii8vYXJjaGl2ZS5vcmcvaW5jbHVkZXMvYnVpbGQvbnBtL3JlYWN0LWRvbS91bWQvcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzP3YxNi43LjAiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iLy9hcmNo
|
||
|
<p>We can now use the terminal in which we ran the <code>mongo</code> command to run queries against our database. Let's go ahead and create that database, we'll call it url_shortener:</p>
|
||
|
<pre><code class="lang-bash hljs">> <span class="hljs-keyword">use</span> url_shortener
|
||
|
switched <span class="hljs-keyword">to</span> db url_shortener
|
||
|
</code></pre>
|
||
|
<p>You can check what database you're currently <code>use</code>-ing in your session by typing</p>
|
||
|
<pre><code class="lang-bash hljs"><span class="hljs-quote">> db</span>
|
||
|
url_shortener
|
||
|
</code></pre>
|
||
|
<p>and you can list all your databases:</p>
|
||
|
<pre><code class="lang-bash hljs">> <span class="hljs-keyword">show</span> dbs
|
||
|
<span class="hljs-keyword">local</span> <span class="hljs-number">0.078</span>GB
|
||
|
</code></pre>
|
||
|
<p>You won't see the url_shortener database we just "created" because we need to insert at least one document in it first. So let's go ahead and create the counters collection with a counter called <em>url_count</em> starting at 0:</p>
|
||
|
<pre><code class="lang-bash hljs">> db.counters.<span class="hljs-keyword">insert</span>({ _id: <span class="hljs-string">'url_count'</span>, seq: <span class="hljs-number">1</span> })
|
||
|
WriteResult({ <span class="hljs-string">"nInserted"</span> : <span class="hljs-number">1</span> })
|
||
|
</code></pre>
|
||
|
<p>That's it! Let's go back into our NodeJS code and create the schema and models to be able to use these collections via our application for insertions and lookups. NodeJS has a great package for connecting to a MongoDB called <a target="_blank" href="https://web.archive.org/web/20160411185424/http://mongoosejs.com/">mongoose</a> which is a very intuitive ODM (Object Data Modeling) library. We'll install it via NPM to use in our project by typing:</p>
|
||
|
<pre><code class="lang-bash hljs">npm <span class="hljs-keyword">install</span> mongoose <span class="hljs-comment">--save</span>
|
||
|
</code></pre>
|
||
|
<p>We'll create our URL model in our models directory within the url-shortener project, called <strong>url.js</strong>. Start off with our requires to grab mongoose:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-keyword">var</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mongoose'</span>);
|
||
|
<span class="hljs-keyword">var</span> Schema = mongoose.Schema;
|
||
|
</code></pre>
|
||
|
<p>Next we need to define a schema for our collection and using that schema, we create our model. The counters collection is rather straightforward:</p>
|
||
|
<pre><code class="lang-javascript hljs">// <span class="hljs-keyword">create</span> the counters <span class="hljs-keyword">schema</span> <span class="hljs-keyword">with</span> an _id <span class="hljs-keyword">field</span> <span class="hljs-keyword">and</span> a seq <span class="hljs-keyword">field</span>
|
||
|
<span class="hljs-keyword">var</span> CounterSchema = <span class="hljs-keyword">Schema</span>({
|
||
|
_id: {<span class="hljs-keyword">type</span>: <span class="hljs-keyword">String</span>, <span class="hljs-keyword">required</span>: <span class="hljs-literal">true</span>},
|
||
|
seq: { <span class="hljs-keyword">type</span>: <span class="hljs-built_in">Number</span>, <span class="hljs-keyword">default</span>: <span class="hljs-number">0</span> }
|
||
|
});
|
||
|
|
||
|
// <span class="hljs-keyword">create</span> a <span class="hljs-keyword">model</span> <span class="hljs-keyword">from</span> that <span class="hljs-keyword">schema</span>
|
||
|
<span class="hljs-keyword">var</span> counter = mongoose.<span class="hljs-keyword">model</span>(<span class="hljs-string">'counter'</span>, CounterSchema);
|
||
|
</code></pre>
|
||
|
<p>We'll do the same thing for our urls collection by defining it's schema as we did for the counters collection:</p>
|
||
|
<pre><code class="lang-javascript hljs">// <span class="hljs-keyword">create</span> a <span class="hljs-keyword">schema</span> <span class="hljs-keyword">for</span> our links
|
||
|
<span class="hljs-keyword">var</span> urlSchema = <span class="hljs-keyword">new</span> <span class="hljs-keyword">Schema</span>({
|
||
|
_id: {<span class="hljs-keyword">type</span>: <span class="hljs-built_in">Number</span>, <span class="hljs-keyword">index</span>: <span class="hljs-literal">true</span>},
|
||
|
long_url: <span class="hljs-keyword">String</span>,
|
||
|
created_at: <span class="hljs-built_in">Date</span>
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>Now this is the crucial part where we use the findAndModify function we talked about earlier. What we're trying to achieve is the following:</p>
|
||
|
<blockquote>
|
||
|
<p>Before saving an entry in the urls collection, increment the global url_count in the counters collection and use that as the <em>_id</em> field of the urls collection</p>
|
||
|
</blockquote>
|
||
|
<p>Putting that into code:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// The pre('save', callback) middleware executes the callback function</span>
|
||
|
<span class="hljs-comment">// every time before an entry is saved to the urls collection.</span>
|
||
|
urlSchema.pre(<span class="hljs-string">'save'</span>, function(<span class="hljs-keyword">next</span>){
|
||
|
var doc = this<span class="hljs-comment">;</span>
|
||
|
<span class="hljs-comment">// find the url_count and increment it by 1</span>
|
||
|
counter.findByIdAndUpdate({_id: <span class="hljs-string">'url_count'</span>}, {$inc: {seq: <span class="hljs-number">1</span>} }, function(error, counter) {
|
||
|
<span class="hljs-keyword">if</span> (error)
|
||
|
<span class="hljs-keyword">return</span> <span class="hljs-keyword">next</span>(error)<span class="hljs-comment">;</span>
|
||
|
<span class="hljs-comment">// set the _id of the urls collection to the incremented value of the counter</span>
|
||
|
doc._id = counter.seq<span class="hljs-comment">;</span>
|
||
|
doc.created_at = new Date()<span class="hljs-comment">;</span>
|
||
|
<span class="hljs-keyword">next</span>()<span class="hljs-comment">;</span>
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
</code></pre>
|
||
|
<p>We have the pre-save functionality in place to automatically increment the counter and assign it as the unique identifier of the url entry which we will be encoding and decoding for shortening. Let's create the model from the URL schema:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-keyword">var</span> Url = mongoose.model(<span class="hljs-string">'Url'</span>, urlSchema);
|
||
|
</code></pre>
|
||
|
<p>and finally export the Url model for use in our app.js:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-function"><span class="hljs-keyword">module</span>.<span class="hljs-title">exports</span> =</span> Url;
|
||
|
</code></pre>
|
||
|
<h1 id="saving-the-shortened-urls">Saving the shortened URLs</h1>
|
||
|
<p>In this section we'll be:</p>
|
||
|
<ol>
|
||
|
<li>Taking long URLs submitted by the user via the front end</li>
|
||
|
<li>Storing it in the database using the models we created in the previous section</li>
|
||
|
<li>Encoding the <em>_id</em> of the newly inserted object</li>
|
||
|
<li>Returning the shortened version of the URL to the user</li>
|
||
|
</ol>
|
||
|
<p>With that being said, let's add the front end javascript that will take what the user entered in the form and POST it to our <code>/api/shorten</code> route. Create a new file in the <em>public/javascripts</em> directory in your project and call it <em>shorten.js</em> since that's what we used in the HTML file:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-comment">// add an event listener to the shorten button for when the user clicks it</span>
|
||
|
$(<span class="hljs-string">'.btn-shorten'</span>).on(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
|
||
|
<span class="hljs-comment">// AJAX call to /api/shorten with the URL that the user entered in the input box</span>
|
||
|
$.ajax({
|
||
|
url: <span class="hljs-string">'/api/shorten'</span>,
|
||
|
type: <span class="hljs-string">'POST'</span>,
|
||
|
dataType: <span class="hljs-string">'JSON'</span>,
|
||
|
data: {url: $(<span class="hljs-string">'#url-field'</span>).val()},
|
||
|
success: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{
|
||
|
<span class="hljs-comment">// display the shortened URL to the user that is returned by the server</span>
|
||
|
<span class="hljs-keyword">var</span> resultHTML = <span class="hljs-string">'<a class="result" href="'</span> + data.shortUrl + <span class="hljs-string">'">'</span>
|
||
|
+ data.shortUrl + <span class="hljs-string">'</a>'</span>;
|
||
|
$(<span class="hljs-string">'#link'</span>).html(resultHTML);
|
||
|
$(<span class="hljs-string">'#link'</span>).hide().fadeIn(<span class="hljs-string">'slow'</span>);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>We now have to add the server side logic in our app.js file that will take that data (the long url) from the AJAX call when the user submits the form and return the shortened URL. To get the data submitted in the body of the POST request, we will use a middleware called <em>body-parser</em>, we can install it via NPM:</p>
|
||
|
<pre><code class="lang-bash hljs">npm <span class="hljs-keyword">install</span> <span class="hljs-keyword">body</span>-parser <span class="hljs-comment">--save</span>
|
||
|
</code></pre>
|
||
|
<p>and we'll require and configure our application to use this middleware:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-keyword">var</span> bodyParser = <span class="hljs-keyword">require</span>(<span class="hljs-string">'body-parser'</span>);
|
||
|
|
||
|
<span class="hljs-comment">// handles JSON bodies</span>
|
||
|
app.<span class="hljs-keyword">use</span>(bodyParser.json());
|
||
|
<span class="hljs-comment">// handles URL encoded bodies</span>
|
||
|
app.<span class="hljs-keyword">use</span>(bodyParser.urlencoded({ extended: <span class="hljs-keyword">true</span> }));
|
||
|
</code></pre>
|
||
|
<p>The incoming POST requests can now be parsed and handled by that middleware for our routes.</p>
|
||
|
<p>Let's go ahead and create a config file to store our connection information and URL shortener host so we can use it throughout our application without having to hard code it and change it in many different places. We'll call this file <strong>config.js</strong> and save it in the root of our url-shortener project folder:</p>
|
||
|
<pre><code class="lang-javascript hljs">var <span class="hljs-built_in">config</span> = {};
|
||
|
|
||
|
<span class="hljs-built_in">config</span>.db = {};
|
||
|
<span class="hljs-comment">// the URL shortening host - shortened URLs will be this + base58 ID</span>
|
||
|
<span class="hljs-comment">// i.e.: http://localhost:3000/3Ys</span>
|
||
|
<span class="hljs-built_in">config</span>.webhost = <span class="hljs-string">'http://localhost:3000/'</span>;
|
||
|
|
||
|
<span class="hljs-comment">// your MongoDB host and database name</span>
|
||
|
<span class="hljs-built_in">config</span>.db.host = <span class="hljs-string">'localhost'</span>;
|
||
|
<span class="hljs-built_in">config</span>.db.name = <span class="hljs-string">'url_shortener'</span>;
|
||
|
|
||
|
module.exports = <span class="hljs-built_in">config</span>;
|
||
|
</code></pre>
|
||
|
<p>Grab the remaining imports that we'll use in our app.js:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-keyword">var</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mongoose'</span>);
|
||
|
<span class="hljs-keyword">var</span> config = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./config'</span>);
|
||
|
<span class="hljs-comment">// base58 for encoding and decoding functions</span>
|
||
|
<span class="hljs-keyword">var</span> base58 = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./base58.js'</span>);
|
||
|
|
||
|
<span class="hljs-comment">// grab the url model</span>
|
||
|
<span class="hljs-keyword">var</span> Url = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./models/url'</span>);
|
||
|
</code></pre>
|
||
|
<p>We now have all the tools, modules and configurations in place and our app.js should look something like this:</p>
|
||
|
<pre><code class="lang-javascript hljs"><span class="hljs-keyword">var</span> express = <span class="hljs-keyword">require</span>(<span class="hljs-string">'express'</span>);
|
||
|
<span class="hljs-keyword">var</span> app = express();
|
||
|
<span class="hljs-keyword">var</span> path = <span class="hljs-keyword">require</span>(<span class="hljs-string">'path'</span>);
|
||
|
<span class="hljs-keyword">var</span> bodyParser = <span class="hljs-keyword">require</span>(<span class="hljs-string">'body-parser'</span>);
|
||
|
<span class="hljs-keyword">var</span> mongoose = <span class="hljs-keyword">require</span>(<span class="hljs-string">'mongoose'</span>);
|
||
|
<span class="hljs-keyword">var</span> config = <span class="hljs-keyword">require</span>(<span class="hljs-string">'./config'</span>);
|
||
|
<span class="hljs-keyword">var</span> base58 = <span class="hljs-keyword">require</span>(<span class="hljs-string">'./base58.js'</span>);
|
||
|
|
||
|
<span class="hljs-comment">// grab the url model</span>
|
||
|
<span class="hljs-keyword">var</span> Url = <span class="hljs-keyword">require</span>(<span class="hljs-string">'./models/url'</span>);
|
||
|
|
||
|
<span class="hljs-comment">// create a connection to our MongoDB</span>
|
||
|
mongoose.connect(<span class="hljs-string">'mongodb://'</span> + config.db.host + <span class="hljs-string">'/'</span> + config.db.name);
|
||
|
|
||
|
app.<span class="hljs-keyword">use</span>(bodyParser.json());
|
||
|
app.<span class="hljs-keyword">use</span>(bodyParser.urlencoded({ extended: <span class="hljs-keyword">true</span> }));
|
||
|
|
||
|
<span class="hljs-comment">// tell Express to serve file from our public folder</span>
|
||
|
app.<span class="hljs-keyword">use</span>(express.<span class="hljs-keyword">static</span>(path.join(__dirname, <span class="hljs-string">'public'</span>)));
|
||
|
|
||
|
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-comment">// route to serve up the homepage (index.html)</span>
|
||
|
res.sendFile(path.join(__dirname, <span class="hljs-string">'views/index.html'</span>));
|
||
|
});
|
||
|
|
||
|
<span class="hljs-keyword">var</span> server = app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
|
||
|
console.log(<span class="hljs-string">'Server listening on port 3000'</span>);
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>The route that will handle the incoming POST request:</p>
|
||
|
<pre><code class="lang-javascript hljs">app.post(<span class="hljs-string">'/api/shorten'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>What we want to do in this route once we receive a shortening request is:</p>
|
||
|
<ul>
|
||
|
<li>Check if the URL has already been shortened to avoid creating duplicates:<ul>
|
||
|
<li>If it has been shortened, return the base58 encoded ID right away</li>
|
||
|
<li>If it hasn't been shortened, we will create a new entry for it</li>
|
||
|
</ul>
|
||
|
</li>
|
||
|
</ul>
|
||
|
<p>Let's grab the long URL submitted by the user from our POST body:</p>
|
||
|
<pre><code class="lang-javascript hljs">app.post(<span class="hljs-string">'/api/shorten'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-keyword">var</span> longUrl = req.body.url;
|
||
|
<span class="hljs-keyword">var</span> shortUrl = <span class="hljs-string">''</span>; <span class="hljs-comment">// the shortened URL we will return</span>
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>To check if a URL has already been shortened, we can use the <code>findOne</code> method to search for the long URL in our database. This function will return a single entry if there is a match:</p>
|
||
|
<pre><code class="lang-javascript hljs">app.post(<span class="hljs-string">'/api/shorten'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-keyword">var</span> longUrl = req.body.url;
|
||
|
<span class="hljs-keyword">var</span> shortUrl = <span class="hljs-string">''</span>;
|
||
|
|
||
|
<span class="hljs-comment">// check if url already exists in database</span>
|
||
|
Url.findOne({long_url: longUrl}, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(err, doc)</span></span>{
|
||
|
<span class="hljs-keyword">if</span> (doc){
|
||
|
<span class="hljs-comment">// URL has already been shortened</span>
|
||
|
} <span class="hljs-keyword">else</span> {
|
||
|
<span class="hljs-comment">// The long URL was not found in the long_url field in our urls</span>
|
||
|
<span class="hljs-comment">// collection, so we need to create a new entry</span>
|
||
|
}
|
||
|
});
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>In the case that the URL has already been shortened, we will use the <code>doc</code> parameter passed in the callback of the <code>findOne</code> method to grab the <em>_id</em> of that entry in the urls collection and base58 encode it:</p>
|
||
|
<pre><code class="lang-javascript hljs">app.post(<span class="hljs-string">'/api/shorten'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-keyword">var</span> longUrl = req.body.url;
|
||
|
<span class="hljs-keyword">var</span> shortUrl = <span class="hljs-string">''</span>;
|
||
|
|
||
|
<span class="hljs-comment">// check if url already exists in database</span>
|
||
|
Url.findOne({long_url: longUrl}, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(err, doc)</span></span>{
|
||
|
<span class="hljs-keyword">if</span> (doc){
|
||
|
<span class="hljs-comment">// base58 encode the unique _id of that document and construct the short URL</span>
|
||
|
shortUrl = config.webhost + base58.encode(doc._id);
|
||
|
|
||
|
<span class="hljs-comment">// since the document exists, we return it without creating a new entry</span>
|
||
|
res.send({<span class="hljs-string">'shortUrl'</span>: shortUrl});
|
||
|
} <span class="hljs-keyword">else</span> {
|
||
|
<span class="hljs-comment">// The long URL was not found in the long_url field in our urls</span>
|
||
|
<span class="hljs-comment">// collection, so we need to create a new entry</span>
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>When the long URL is not found, we will have to create a new Url object and use the <code>save</code> method to save it to our urls collection. Incrementing the counters collection and assigning it to the <em>_id</em> of the urls_collection is already taken care of for us in the pre save middleware within url.js model we defined.</p>
|
||
|
<pre><code class="lang-javascript hljs">app.post(<span class="hljs-string">'/api/shorten'</span>, function(req, res){
|
||
|
var longUrl = req.body.url<span class="hljs-comment">;</span>
|
||
|
var shortUrl = <span class="hljs-string">''</span><span class="hljs-comment">;</span>
|
||
|
|
||
|
<span class="hljs-comment">// check if url already exists in database</span>
|
||
|
Url.findOne({long_url: longUrl}, function (<span class="hljs-keyword">err</span>, doc){
|
||
|
<span class="hljs-keyword">if</span> (doc){
|
||
|
<span class="hljs-comment">// base58 encode the unique _id of that document and construct the short URL</span>
|
||
|
shortUrl = config.webhost + base58.encode(doc._id)<span class="hljs-comment">;</span>
|
||
|
|
||
|
<span class="hljs-comment">// since the document exists, we return it without creating a new entry</span>
|
||
|
res.send({<span class="hljs-string">'shortUrl'</span>: shortUrl})<span class="hljs-comment">;</span>
|
||
|
} <span class="hljs-keyword">else</span> {
|
||
|
<span class="hljs-comment">// The long URL was not found in the long_url field in our urls</span>
|
||
|
<span class="hljs-comment">// collection, so we need to create a new entry:</span>
|
||
|
var newUrl = Url({
|
||
|
long_url: longUrl
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
|
||
|
<span class="hljs-comment">// save the new link</span>
|
||
|
newUrl.save(function(<span class="hljs-keyword">err</span>) {
|
||
|
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">err</span>){
|
||
|
console.log(<span class="hljs-keyword">err</span>)<span class="hljs-comment">;</span>
|
||
|
}
|
||
|
|
||
|
<span class="hljs-comment">// construct the short URL</span>
|
||
|
shortUrl = config.webhost + base58.encode(newUrl._id)<span class="hljs-comment">;</span>
|
||
|
|
||
|
res.send({<span class="hljs-string">'shortUrl'</span>: shortUrl})<span class="hljs-comment">;</span>
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
}
|
||
|
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
|
||
|
})<span class="hljs-comment">;</span>
|
||
|
</code></pre>
|
||
|
<p>And that's it! We now have the URL saving functionality in our application and all that's left is to redirect the visitor when they visit a shortened URL, so on to the final section.</p>
|
||
|
<h1 id="redirecting-the-visitor">Redirecting the visitor</h1>
|
||
|
<p>When someone visits a URL shortened by our service such as coligo.io/3Ys, we want to:</p>
|
||
|
<ul>
|
||
|
<li>Take the <em>3Ys</em> from the URL</li>
|
||
|
<li>Decode it to get the unique <em>_id</em> of our document in the urls collection</li>
|
||
|
<li>Redirect them to the associated long_url in that document</li>
|
||
|
</ul>
|
||
|
<p>Thankfully Express makes dynamic URL parameters really easy:</p>
|
||
|
<pre><code class="lang-javascript hljs">app.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/:encoded_id'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<p>Express will take the base58 encoded ID at the end of our URL and assign it to a variable called <code>encoded_id</code> for us to use in our callback. For example, if a user visits coligo.io/3Ys, the variable <code>encoded_id</code> will hold the value <em>3Ys</em>.</p>
|
||
|
<p>We will decode the base58 ID to get it's base10 equivalent and look it up in the database using the <code>findOne</code> method. If we manage to find that <em>_id</em> in the database, we will then redirect the visitor to their actual destination with Express's <code>res.redirect</code> method with a 301 redirect status. However, if we don't find anything in the database with that <em>_id</em> we can simply redirect them to the homepages (or potentially a 404 page).</p>
|
||
|
<pre><code class="lang-javascript hljs">app.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/:encoded_id'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(req, res)</span></span>{
|
||
|
<span class="hljs-keyword">var</span> base58Id = req.params.encoded_id;
|
||
|
<span class="hljs-keyword">var</span> id = base58.decode(base58Id);
|
||
|
|
||
|
<span class="hljs-comment">// check if url already exists in database</span>
|
||
|
Url.findOne({_id: id}, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(err, doc)</span></span>{
|
||
|
<span class="hljs-keyword">if</span> (doc) {
|
||
|
<span class="hljs-comment">// found an entry in the DB, redirect the user to their destination</span>
|
||
|
res.redirect(doc.long_url);
|
||
|
} <span class="hljs-keyword">else</span> {
|
||
|
<span class="hljs-comment">// nothing found, take 'em home</span>
|
||
|
res.redirect(config.webhost);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
});
|
||
|
</code></pre>
|
||
|
<h1 id="conclusion">Conclusion</h1>
|
||
|
<p>In this tutorial we covered the underlying logic and concepts of how a URL shortening service works and implemented a solution using NodeJs, Express, and MongoDB. There are plenty of other ways you can go about creating a URL shortener and perhaps even more efficient ways of doing it such as swapping out MongoDB for <a target="_blank" href="https://web.archive.org/web/20160411185424/http://redis.io/">redis</a>.</p>
|
||
|
<p>The purpose of this tutorial is to get you started and familiar with the concepts of a URL shortener, so I'd encourage you to take what you learned a step further and implement more optimal solutions and try out different stacks and algorithms. I'd love to hear about your ideas and how you chose to implement a URL shortener, so leave a comment below!</p>
|
||
|
<p>Feel free to download the <a target="_blank" href="https://web.archive.org/web/20160411185424/https://github.com/coligo-io/url-shortener-node-mongo-express">full code</a> for this tutorial to see the full picture of how everything works together and customize it for your own needs.</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="extra content">
|
||
|
<div id="disqus_thread"></div>
|
||
|
<script>
|
||
|
|
||
|
var disqus_config = function () {
|
||
|
this.page.url = "https://web.archive.org/web/20160411185424/http://coligo.io/create-url-shortener-with-node-express-mongo/";
|
||
|
this.page.identifier = "create-url-shortener-with-node-express-mongo";
|
||
|
};
|
||
|
|
||
|
(function() { // DON'T EDIT BELOW THIS LINE
|
||
|
var d = document, s = d.createElement('script');
|
||
|
|
||
|
s.src = '//web.archive.org/web/20160411185424/http://coligo.disqus.com/embed.js';
|
||
|
|
||
|
s.setAttribute('data-timestamp', +new Date());
|
||
|
(d.head || d.body).appendChild(s);
|
||
|
})();
|
||
|
</script>
|
||
|
<noscript>Please enable JavaScript to view the <a href="https://web.archive.org/web/20160411185424/https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="four wide computer only column">
|
||
|
<div class="ui sticky cards">
|
||
|
<div class="card">
|
||
|
<div class="content">
|
||
|
<h3 class="ui center aligned icon header">
|
||
|
<i class="circular share alternate icon"></i>
|
||
|
Share This Article
|
||
|
</h3>
|
||
|
</div>
|
||
|
<div class="extra center aligned content">
|
||
|
<button data-url="https://www.facebook.com/sharer/sharer.php?u=http://coligo.io/create-url-shortener-with-node-express-mongo/" class="ui circular facebook icon button share-modal">
|
||
|
<i class="facebook icon"></i>
|
||
|
</button>
|
||
|
<button data-url="https://twitter.com/intent/tweet?text=Creating a URL Shortener with NodeJs, Express, and MongoDB&url=http://coligo.io/create-url-shortener-with-node-express-mongo/" class="ui circular twitter icon button share-modal">
|
||
|
<i class="twitter icon"></i>
|
||
|
</button>
|
||
|
<button data-url="https://plus.google.com/share?url=http://coligo.io/create-url-shortener-with-node-express-mongo/" class="ui circular google plus icon button share-modal">
|
||
|
<i class="google plus icon"></i>
|
||
|
</button>
|
||
|
<button data-url="https://www.linkedin.com/shareArticle?mini=true&url=http://coligo.io/create-url-shortener-with-node-express-mongo/&title=Creating a URL Shortener with NodeJs, Express, and MongoDB" class="ui circular linkedin icon button share-modal">
|
||
|
<i class="linkedin icon"></i>
|
||
|
</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="card">
|
||
|
<div class="content">
|
||
|
<h3 class="ui center aligned header">
|
||
|
Reddit this!
|
||
|
</h3>
|
||
|
</div>
|
||
|
<div class="extra center aligned content">
|
||
|
<script type="text/javascript">
|
||
|
reddit_url = "https://web.archive.org/web/20160411185424/http://coligo.io/create-url-shortener-with-node-express-mongo/";
|
||
|
reddit_title = "Creating a URL Shortener with NodeJs, Express, and MongoDB";
|
||
|
reddit_target='webdev';
|
||
|
reddit_newwindow = '1';
|
||
|
</script>
|
||
|
<script type="text/javascript" src="data:application/javascript;base64,KGZ1bmN0aW9uKCkgewogIHZhciB3cml0ZV9zdHJpbmc9IjxpZnJhbWUgc3JjPVwiLy93d3cucmVkZGl0c3RhdGljLmNvbS9idXR0b24vYnV0dG9uMy5odG1sP3VybD0iOwoKICBpZiAod2luZG93LnJlZGRpdF91cmwpICB7IAogICAgICB3cml0ZV9zdHJpbmcgKz0gZW5jb2RlVVJJQ29tcG9uZW50KHJlZGRpdF91cmwpOyAKICB9CiAgZWxzZSB7IAogICAgICB3cml0ZV9zdHJpbmcgKz0gZW5jb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5sb2NhdGlvbi5ocmVmKTsKICB9CiAgaWYgKHdpbmRvdy5yZWRkaXRfdGl0bGUpIHsKICAgICAgIHdyaXRlX3N0cmluZyArPSAnJnRpdGxlPScgKyBlbmNvZGVVUklDb21wb25lbnQod2luZG93LnJlZGRpdF90aXRsZSk7CiAgfQogIGlmICh3aW5kb3cucmVkZGl0X3RhcmdldCkgewogICAgICAgd3JpdGVfc3RyaW5nICs9ICcmc3I9JyArIGVuY29kZVVSSUNvbXBvbmVudCh3aW5kb3cucmVkZGl0X3RhcmdldCk7CiAgfQogIGlmICh3aW5kb3cucmVkZGl0X2NzcykgewogICAgICB3cml0ZV9zdHJpbmcgKz0gJyZjc3M9JyArIGVuY29kZVVSSUNvbXBvbmVudCh3aW5kb3cucmVkZGl0X2Nzcyk7CiAgfQogIGlmICh3aW5kb3cucmVkZGl0X2JnY29sb3IpIHsKICAgICAgd3JpdGVfc3RyaW5nICs9ICcmYmdjb2xvcj0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5yZWRkaXRfYmdjb2xvcik7IAogIH0KICBpZiAod2luZG93LnJlZGRpdF9ib3JkZXJjb2xvcikgewogICAgICB3cml0ZV9zdHJpbmcgKz0gJyZib3JkZXJjb2xvcj0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5yZWRkaXRfYm9yZGVyY29sb3IpOyAKICB9CiAgaWYgKHdpbmRvdy5yZWRkaXRfbmV3d2luZG93KSB7IAogICAgICB3cml0ZV9zdHJpbmcgKz0gJyZuZXd3aW5kb3c9JyArIGVuY29kZVVSSUNvbXBvbmVudCh3aW5kb3cucmVkZGl0X25ld3dpbmRvdyk7fQogIHdyaXRlX3N0cmluZyArPSAiXCIgaGVpZ2h0PVwiNTJcIiB3aWR0aD1cIjY5XCIgc2Nyb2xsaW5nPSdubycgZnJhbWVib3JkZXI9JzAnPjwvaWZyYW1lPiI7CiAgZG9jdW1lbnQud3JpdGUod3JpdGVfc3RyaW5nKTsKfSkoKQoKLyoKICAgICBGSUxFIEFSQ0hJVkVEIE9OIDE4OjAxOjA4IEFwciAxMSwgMjAxNiBBTkQgUkVUUklFVkVEIEZST00gVEhFCiAgICAgSU5URVJORVQgQVJDSElWRSBPTiAxMjo1NDozNiBBcHIgMTcsIDIwMjAuCiAgICAgSkFWQVNDUklQVCBBUFBFTkRFRCBCWSBXQVlCQUNLIE1BQ0hJTkUsIENPUFlSSUdIVCBJTlRFUk5FVCBBUkNISVZFLgoKICAgICBBTEwgT1RIRVIgQ09OVEVOVCBNQVkgQUxTTyBCRSBQUk9URUNURUQgQlkgQ09QWVJJR0hUICgxNyBVLlMuQy4KICAgICBTRUNUSU9OIDEwOChhKSgzKSkuCiovCi8qCnBsYXliYWNrIHRpbWluZ3MgKG1zKToKICBsb2FkX3Jlc291cmNlOiA5MC4xMjgKICBSZWRpc0NEWFNvdXJjZTogMTA2LjExOAogIGV4Y2x1c2lvbi5yb2JvdHM6IDAuMTQKICBjYXB0dXJlc19saXN0OiA2NDIuNDQ3CiAgUGV0YWJveExvYWRlcjMucmVzb2x2ZTogMTg1LjU5OSAoMykKICBDRFhMaW5lcy5pdGVyOiA0MS40NiAoMykKICBQZXRhYm94TG9hZGVyMy5kYXRhbm9kZTogMTQyLjA5NSAoNCkKICBlc2luZGV4OiAwLjAxNwogIExvYWRTaGFyZEJsb2NrOiAyODEuNDA4ICgzKQogIGV4Y2x1c2lvbi5yb2JvdHMucG9saWN5OiAwLjEyOQoqLw=="></script>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="card">
|
||
|
<div class="content">
|
||
|
<a target="_blank" href="https://web.archive.org/web/20160411185424/https://m.do.co/c/611328388d98">
|
||
|
<img class="ui image" src="data:image/png;base64,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
|
||
|
</a>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="ui inverted vertical footer segment">
|
||
|
<div class="ui center aligned container">
|
||
|
<div class="ui stackable inverted divided grid">
|
||
|
<div class="eight wide column">
|
||
|
<h4 class="ui inverted header">coligo.io</h4>
|
||
|
<div class="ui inverted link list">
|
||
|
|
||
|
<a href="https://web.archive.org/web/20160411185424/http://coligo.io/cdn-cgi/l/email-protection#7a1c1b1e033a191516131d15541315" class="item">contact us</a>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="eight wide column">
|
||
|
<h4 class="ui inverted header">Follow Us!</h4>
|
||
|
<a href="https://web.archive.org/web/20160411185424/https://www.facebook.com/coligo.io">
|
||
|
<button class="ui facebook button">
|
||
|
<i class="facebook icon"></i>
|
||
|
Facebook
|
||
|
</button>
|
||
|
</a>
|
||
|
<a href="https://web.archive.org/web/20160411185424/https://twitter.com/coligo_io">
|
||
|
<button class="ui twitter button">
|
||
|
<i class="twitter icon"></i>
|
||
|
Twitter
|
||
|
</button>
|
||
|
</a>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<script src="data:application/javascript;base64,IWZ1bmN0aW9uKGUsdCl7Im9iamVjdCI9PXR5cGVvZiBtb2R1bGUmJiJvYmplY3QiPT10eXBlb2YgbW9kdWxlLmV4cG9ydHM/bW9kdWxlLmV4cG9ydHM9ZS5kb2N1bWVudD90KGUsITApOmZ1bmN0aW9uKGUpe2lmKCFlLmRvY3VtZW50KXRocm93IG5ldyBFcnJvcigialF1ZXJ5IHJlcXVpcmVzIGEgd2luZG93IHdpdGggYSBkb2N1bWVudCIpO3JldHVybiB0KGUpfTp0KGUpfSgidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdz93aW5kb3c6dGhpcyxmdW5jdGlvbihlLHQpe2Z1bmN0aW9uIG4oZSl7dmFyIHQ9ISFlJiYibGVuZ3RoImluIGUmJmUubGVuZ3RoLG49b2UudHlwZShlKTtyZXR1cm4iZnVuY3Rpb24iPT09bnx8b2UuaXNXaW5kb3coZSk/ITE6ImFycmF5Ij09PW58fDA9PT10fHwibnVtYmVyIj09dHlwZW9mIHQmJnQ+MCYmdC0xIGluIGV9ZnVuY3Rpb24gcihlLHQsbil7aWYob2UuaXNGdW5jdGlvbih0KSlyZXR1cm4gb2UuZ3JlcChlLGZ1bmN0aW9uKGUscil7cmV0dXJuISF0LmNhbGwoZSxyLGUpIT09bn0pO2lmKHQubm9kZVR5cGUpcmV0dXJuIG9lLmdyZXAoZSxmdW5jdGlvbihlKXtyZXR1cm4gZT09PXQhPT1ufSk7aWYoInN0cmluZyI9PXR5cGVvZiB0KXtpZihnZS50ZXN0KHQpKXJldHVybiBvZS5maWx0ZXIodCxlLG4pO3Q9b2UuZmlsdGVyKHQsZSl9cmV0dXJuIG9lLmdyZXAoZSxmdW5jdGlvbihlKXtyZXR1cm4gWi5jYWxsKHQsZSk+LTEhPT1ufSl9ZnVuY3Rpb24gaShlLHQpe2Zvcig7KGU9ZVt0XSkmJjEhPT1lLm5vZGVUeXBlOyk7cmV0dXJuIGV9ZnVuY3Rpb24gbyhlKXt2YXIgdD17fTtyZXR1cm4gb2UuZWFjaChlLm1hdGNoKHdlKXx8W10sZnVuY3Rpb24oZSxuKXt0W25dPSEwfSksdH1mdW5jdGlvbiBzKCl7Ry5yZW1vdmVFdmVudExpc3RlbmVyKCJET01Db250ZW50TG9hZGVkIixzKSxlLnJlbW92ZUV2ZW50TGlzdGVuZXIoImxvYWQiLHMpLG9lLnJlYWR5KCl9ZnVuY3Rpb24gYSgpe3RoaXMuZXhwYW5kbz1vZS5leHBhbmRvK2EudWlkKyt9ZnVuY3Rpb24gdShlLHQsbil7dmFyIHI7aWYodm9pZCAwPT09biYmMT09PWUubm9kZVR5cGUpaWYocj0iZGF0YS0iK3QucmVwbGFjZShEZSwiLSQmIikudG9Mb3dlckNhc2UoKSxuPWUuZ2V0QXR0cmlidXRlKHIpLCJzdHJpbmciPT10eXBlb2Ygbil7dHJ5e249InRydWUiPT09bj8hMDoiZmFsc2UiPT09bj8hMToibnVsbCI9PT1uP251bGw6K24rIiI9PT1uPytuOlNlLnRlc3Qobik/b2UucGFyc2VKU09OKG4pOm59Y2F0Y2goaSl7fU5lLnNldChlLHQsbil9ZWxzZSBuPXZvaWQgMDtyZXR1cm4gbn1mdW5jdGlvbiBsKGUsdCxuLHIpe3ZhciBpLG89MSxzPTIwLGE9cj9mdW5jdGlvbigpe3JldHVybiByLmN1cigpfTpmdW5jdGlvbigpe3JldHVybiBvZS5jc3MoZSx0LCIiKX0sdT1hKCksbD1uJiZuWzNdfHwob2UuY3NzTnVtYmVyW3RdPyIiOiJweCIpLGM9KG9lLmNzc051bWJlclt0XXx8InB4IiE9PWwmJit1KSYmQWUuZXhlYyhvZS5jc3MoZSx0KSk7aWYoYyYmY1szXSE9PWwpe2w9bHx8Y1szXSxuPW58fFtdLGM9K3V8fDE7ZG8gbz1vfHwiLjUiLGMvPW8sb2Uuc3R5bGUoZSx0LGMrbCk7d2hpbGUobyE9PShvPWEoKS91KSYmMSE9PW8mJi0tcyl9cmV0dXJuIG4mJihjPStjfHwrdXx8MCxpPW5bMV0/YysoblsxXSsxKSpuWzJdOituWzJdLHImJihyLnVuaXQ9bCxyLnN0YXJ0PWMsci5lbmQ9aSkpLGl9ZnVuY3Rpb24gYyhlLHQpe3ZhciBuPSJ1bmRlZmluZWQiIT10eXBlb2YgZS5nZXRFbGVtZW50c0J5VGFnTmFtZT9lLmdldEVsZW1lbnRzQnlUYWdOYW1lKHR8fCIqIik6InVuZGVmaW5lZCIhPXR5cGVvZiBlLnF1ZXJ5U2VsZWN0b3JBbGw/ZS5xdWVyeVNlbGVjdG9yQWxsKHR8fCIqIik6W107cmV0dXJuIHZvaWQgMD09PXR8fHQmJm9lLm5vZGVOYW1lKGUsdCk/b2UubWVyZ2UoW2VdLG4pOm59ZnVuY3Rpb24gZihlLHQpe2Zvcih2YXIgbj0wLHI9ZS5sZW5ndGg7cj5uO24rKylFZS5zZXQoZVtuXSwiZ2xvYmFsRXZhbCIsIXR8fEVlLmdldCh0W25dLCJnbG9iYWxFdmFsIikpfWZ1bmN0aW9uIHAoZSx0LG4scixpKXtmb3IodmFyIG8scyxhLHUsbCxwLGQ9dC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCksaD1bXSxnPTAsbT1lLmxlbmd0aDttPmc7ZysrKWlmKG89ZVtnXSxvfHwwPT09bylpZigib2JqZWN0Ij09PW9lLnR5cGUobykpb2UubWVyZ2UoaCxvLm5vZGVUeXBlP1tvXTpvKTtlbHNlIGlmKFJlLnRlc3Qobykpe2ZvcihzPXN8fGQuYXBwZW5kQ2hpbGQodC5jcmVhdGVFbGVtZW50KCJkaXYiKSksYT0oT2UuZXhlYyhvKXx8WyIiLCIiXSlbMV0udG9Mb3dlckNhc2UoKSx1PVBlW2FdfHxQZS5fZGVmYXVsdCxzLmlubmVySFRNTD11WzFdK29lLmh0bWxQcmVmaWx0ZXIobykrdVsyXSxwPXVbMF07cC0tOylzPXMubGFzdENoaWxkO29lLm1lcmdlKGgscy5jaGlsZE5vZGVzKSxzPWQuZmlyc3RDaGlsZCxzLnRleHRDb250ZW50PSIifWVsc2UgaC5wdXNoKHQuY3JlYXRlVGV4dE5vZGUobykpO2ZvcihkLnRleHRDb250ZW50PSIiLGc9MDtvPWhbZysrXTspaWYociYmb2UuaW5BcnJheShvLHIpPi0xKWkmJmkucHVzaChvKTtlbHNlIGlmKGw9b2UuY29udGFpbnMoby5vd25lckRvY3VtZW50LG8pLHM9YyhkLmFwcGVuZENoaWxkKG8pLCJzY3JpcHQiKSxsJiZmKHMpLG4pZm9yKHA9MDtvPXNbcCsrXTspRmUudGVzdChvLnR5cGV8fCIiKSYmbi5wdXNoKG8pO3JldHVybiBkfWZ1bmN0aW9uIGQoKXtyZXR1cm4hMH1mdW5jdGlvbiBoKCl7cmV0dXJuITF9ZnVuY3Rpb24gZygpe3RyeXtyZXR1cm4gRy5hY3RpdmVFbGVtZW50fWNhdGNoKGUpe319ZnVuY3Rpb24gbShlLHQsbixyLGksbyl7dmFyIHMsYTtpZigib2JqZWN0Ij09dHlwZW9mIHQpeyJzdHJpbmciIT10eXBlb2YgbiYmKHI9cnx8bixuPXZvaWQgMCk7Zm9yKGEgaW4gdCltKGUsYSxuLHIsdFthXSxvKTtyZXR1cm4gZX1pZihudWxsPT1yJiZudWxsPT1pPyhpPW4scj1uPXZvaWQgMCk6bnVsbD09aSYmKCJzdHJpbmciPT10eXBlb2Ygbj8oaT1yLHI9dm9pZCAwKTooaT1yLHI9bixuPXZvaWQgMCkpLGk9PT0hMSlpPWg7ZWxzZSBpZighaSlyZXR1cm4gdGhpcztyZXR
|
||
|
<script>
|
||
|
$(document).ready(function(){
|
||
|
$('.ui.dropdown').dropdown();
|
||
|
$('.ui.sticky').sticky({context: '.post-content'});
|
||
|
});
|
||
|
</script>
|
||
|
<script>
|
||
|
$(document).ready(function(){
|
||
|
$('.share-modal').click(function(event) {
|
||
|
var width = 650,
|
||
|
height = 450,
|
||
|
left = ($(window).width() - width) / 2,
|
||
|
top = ($(window).height() - height) / 2,
|
||
|
url = $(this).data('url'),
|
||
|
opts = 'status=yes' +
|
||
|
',width=' + width +
|
||
|
',height=' + height +
|
||
|
',top=' + top +
|
||
|
',left=' + left
|
||
|
',menubar=no,toolbar=no,resizable=yes,scrollbars=yes';
|
||
|
|
||
|
window.open(url, 'Share Modal', opts);
|
||
|
|
||
|
return false;
|
||
|
});
|
||
|
});
|
||
|
</script>
|
||
|
<script>
|
||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
|
})(window,document,'script','//web.archive.org/web/20160411185424/http://www.google-analytics.com/analytics.js','ga');
|
||
|
|
||
|
ga('create', 'UA-73436966-1', 'auto');
|
||
|
ga('send', 'pageview');
|
||
|
|
||
|
</script>
|
||
|
<script type="text/javascript">/* <![CDATA[ */(function(d,s,a,i,j,r,l,m,t){try{l=d.getElementsByTagName('a');t=d.createElement('textarea');for(i=0;l.length-i;i++){try{a=l[i].href;s=a.indexOf('/cdn-cgi/l/email-protection');m=a.length;if(a&&s>-1&&m>28){j=28+s;s='';if(j<m){r='0x'+a.substr(j,2)|0;for(j+=2;j<m&&a.charAt(j)!='X';j+=2)s+='%'+('0'+('0x'+a.substr(j,2)^r).toString(16)).slice(-2);j++;s=decodeURIComponent(s)+a.substr(j,m-j)}t.innerHTML=s.replace(/</g,'<').replace(/>/g,'>');l[i].href='mailto:'+t.value}}catch(e){}}}catch(e){}})(document);/* ]]> */</script>
|
||
|
|
||
|
|
||
|
</body></html><!--
|
||
|
FILE ARCHIVED ON 18:54:24 Apr 11, 2016 AND RETRIEVED FROM THE
|
||
|
INTERNET ARCHIVE ON 12:54:33 Apr 17, 2020.
|
||
|
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
|
||
|
|
||
|
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
|
||
|
SECTION 108(a)(3)).
|
||
|
--><!--
|
||
|
playback timings (ms):
|
||
|
PetaboxLoader3.resolve: 108.518 (2)
|
||
|
load_resource: 62.517
|
||
|
LoadShardBlock: 105.074 (3)
|
||
|
PetaboxLoader3.datanode: 56.106 (4)
|
||
|
exclusion.robots.policy: 0.289
|
||
|
esindex: 0.016
|
||
|
exclusion.robots: 0.303
|
||
|
captures_list: 127.857
|
||
|
CDXLines.iter: 13.406 (3)
|
||
|
RedisCDXSource: 5.925
|
||
|
-->
|