Skip to content

Simple, but blazingly fast, url shortener in various languages and frameworks

License

Notifications You must be signed in to change notification settings

neoxelox/shortr

Repository files navigation

Shortr 🚀

Simple, but blazingly fast, url shortener in various languages and frameworks

Banner

Note: this is just a simple service to learn various languages and frameworks. Efficiency nor security or mantainabilty are the main intention in this project.

Languages

Screenshots

Home page Stats page Error page

Features

  • 🚀 SIMPLE, FAST AND ROBUST
  • CUSTOM OR RANDOM UNIQUE URLS (ACCEPTS UNICODE, SPACES... EVEN EMOJIS! 🔝)
  • 🌺 BEAUTIFUL BY DEFAULT AND CUSTOMIZABLE
  • 🛡️ AUTOMATIC-SSL READY
  • 🏗️ CONTAINERIZED AND EASY TO DEPLOY
  • 📱 RESPONSIVE
  • 🍃 LIGHTWEIGHT, BUILT-IN CACHE AND HA
  • 😃 SEO FRIENDLY AND CUSTOM LINK PREVIEWS

Setup

Important as all WS containers map to port 80, in order to run another language/framework run make stop and then make $language/$framework. See makefile for further commands.

Usage

Use the frontend localhost or interact directly with the shorterner via API calls described below.

API

GET /

Request

Nothing

Response

  • default
    Serves /static
    

GET /:name

Request

  • path param name

Response

  • default
    Redirects to url specified by name
    HTTP code 307 in order not to get urls cached by browsers
    
  • error default
    Serves 404.html page
    
  • error application/json
    {
        "message": "error message"
    }

POST /:name?url=:url

Request

  • path param name nullable
  • query param url

Response

  • default
    {
        "id": 33,
        "name": "shortr",
        "url": "https://github.com/neoxelox/shortr",
        "hits": 1,
        "last_hit_at": "2020-07-27T00:50:42.027431Z", // ( or null )
        "created_at": "2020-07-26T23:36:14.896767Z",
        "modified_at": "2020-07-26T23:36:14.900672Z"
    }
  • error default
    {
        "message": "error message"
    }

DELETE /:name

Request

  • path param name

Response

  • default
    {
        "id": 33,
        "name": "shortr",
        "url": "https://github.com/neoxelox/shortr",
        "hits": 1,
        "last_hit_at": "2020-07-27T00:50:42.027431Z", // ( or null )
        "created_at": "2020-07-26T23:36:14.896767Z",
        "modified_at": "2020-07-26T23:36:14.900672Z"
    }
  • error default
    {
        "message": "error message"
    }

PUT /:name?url=:url

Request

  • path param name
  • query param url

Response

  • default
    {
        "id": 33,
        "name": "shortr",
        "url": "https://github.com/neoxelox/shortr",
        "hits": 1,
        "last_hit_at": "2020-07-27T00:50:42.027431Z", // ( or null )
        "created_at": "2020-07-26T23:36:14.896767Z",
        "modified_at": "2020-07-26T23:36:14.900672Z"
    }
  • error default
    {
        "message": "error message"
    }

GET /:name/stats

Request

  • path param name

Response

  • default
    Serves stats.<renderer>.html page
    
  • application/json
    {
        "id": 33,
        "name": "shortr",
        "url": "https://github.com/neoxelox/shortr",
        "hits": 1,
        "last_hit_at": "2020-07-27T00:50:42.027431Z", // ( or null )
        "created_at": "2020-07-26T23:36:14.896767Z",
        "modified_at": "2020-07-26T23:36:14.900672Z"
    }
  • error default
    Serves 404.html page
    
  • error application/json
    {
        "message": "error message"
    }

GET /health

Request

Nothing

Response

  • default
    OK
    
  • error default
    {
        "message": "error message"
    }

ERROR /*

Request

Any

Response

  • default if template
    Serves <code>.html page
    
  • application/json
    {
        "message": "error message"
    }

Database

The project uses the latest Postgres version available and automatically initializes a pgadmin4 instance localhost:5433 to navigate through the database. Default user is admin@admin.com and password admin. The server group is called URLs and the default database password is postgres.

Model

URL:
    id:          integer
    name:        string
    url:         string
    hits:        integer
    last_hit_at: datetime   nullable
    created_at:  datetime
    modified_at: datetime

Benchmarks

The load testing tool is Locust using 1 master and 4 worker containers, which are automatically created. All follow the locustfile with 2500 users cap and a 25 spawn rate. The benchmark must not be taken as fully good comparison, but gives a quick overview at language and framework efficiency for this purpose.

The minimum number of simultaneous files open for Locust to work is 10000. You can check your SO's default using ulimit -Sn and then increasing it with ulimit -S -n 10000.

The benchmark was run on (sudo lshw -short):

H/W path         Device           Class          Description
============================================================
                                  system         MS-7B29 (Default string)
/0                                bus            H310M PRO-VDH (MS-7B29)
/0/39/0                           memory         8GiB DIMM DDR4 Synchronous 2667 MHz (0,4 ns)
/0/39/2                           memory         8GiB DIMM DDR4 Synchronous 2667 MHz (0,4 ns)
/0/46                             processor      Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
/0/100/1/0                        display        GM206 [GeForce GTX 960]

Mixed API usage

Language Framework Mean requests per second Maximum requests per second Slowest request
Go Echo 6650 rps 7050 rps 810 ms

Contribute

Feel free to contribute to this project by adding more languages/frameworks, the only requirement is that it has to provide the minimum endpoints described above : ) .

License

This project is licensed under the MIT License - read the LICENSE file for details.