{"id":16540562,"url":"https://github.com/gkeesh7/url-shortner","last_synced_at":"2026-05-02T03:32:01.578Z","repository":{"id":42002165,"uuid":"233231370","full_name":"gkeesh7/url-shortner","owner":"gkeesh7","description":"URL shortening Web Service","archived":false,"fork":false,"pushed_at":"2025-10-16T13:06:47.000Z","size":270,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-17T15:55:28.912Z","etag":null,"topics":["go","golang","mysql","rest-api"],"latest_commit_sha":null,"homepage":"https://gkeesh7.github.io/url-shortner","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gkeesh7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-11T12:53:12.000Z","updated_at":"2025-02-06T19:34:26.000Z","dependencies_parsed_at":"2025-04-04T20:45:44.009Z","dependency_job_id":"0cb2132e-d7fd-4151-b972-5033d3c6f637","html_url":"https://github.com/gkeesh7/url-shortner","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gkeesh7/url-shortner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkeesh7%2Furl-shortner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkeesh7%2Furl-shortner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkeesh7%2Furl-shortner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkeesh7%2Furl-shortner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gkeesh7","download_url":"https://codeload.github.com/gkeesh7/url-shortner/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkeesh7%2Furl-shortner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32522245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","mysql","rest-api"],"created_at":"2024-10-11T18:52:47.310Z","updated_at":"2026-05-02T03:32:01.553Z","avatar_url":"https://github.com/gkeesh7.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# URL-Shortner\nThis is a Url Shortening Web Service similar to the likes of [bit.ly](https://bit.ly), [tinyurl.com](https://tinyurl.com/) or [goo.gl](https://goo.gl) albeit highly simplified\n\nFeatures include\n1. An API to shorten a long URL into a smaller unique URL or alias.\n2. Redirection to the original long URL if the short url is accessed.\n3. Automatic Expiration and Deletion of old URLs once expiration time is reached.\n4. Fairly scalable backend that can shorten thousands of Long URLs a second (documented in perf test).\n\n## Installation and Getting Started\n\nClone this repo to your ``$GOPATH/src`` on your local machine using \n```bash\ngit clone https://github.com/gkeesh7/url-shortner.git\n``` \n\n#### Package management\nThe project uses Go Modules now earlier it used to rely on glide\n\n```bash\ngo build main.go\n````\n\n#### Database Schema Creation\nThe application uses a [Mysql](https://dev.mysql.com/downloads/installer/) based Persistent Data Store and connects to it once booting up. \n\nIt's hence necessary that mysql is running on your machine and the corresponding Table schema is pre-existing in your Database.\n\nPlease follow the link in order install and configure MYSQL for your system\n\nExecute the following SQL script in your Mysql shell in order to create the necessary schema \n\n```mysql\nCREATE DATABASE url_shortner;\n\nUSE url_shortner;\n\nDROP TABLE IF EXISTS `redirection`;\n\nCREATE TABLE `redirection`\n(\n    `id`         INT(11)       NOT NULL AUTO_INCREMENT,\n    `url_id`     varchar(20)   NOT NULL UNIQUE,\n    `url`        varchar(1024) NOT NULL,\n    `created_at` TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    `updated_at` TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    `expiry`     TIMESTAMP     NULL     DEFAULT NULL,\n    PRIMARY KEY (`id`)\n);\n\n\nDROP TABLE IF EXISTS `url_stats`;\n\nCREATE TABLE `url_stats`\n(\n    `id`         INT(11)     NOT NULL AUTO_INCREMENT,\n    `url_id`     varchar(20) NOT NULL,\n    `count`      INT(11)     NOT NULL,\n    `created_at` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    `updated_at` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`)\n);\n\nINSERT INTO `redirection` (`id`, `url_id`, `url`, `created_at`, `updated_at`, `expiry`) VALUES ('1', 'test', 'https://youtu.be/dQw4w9WgXcQ?t=43', '2020-01-08 14:05:37', '2020-01-08 14:05:37', '2030-01-19 14:05:37');\n```\nThe above SQL script can also be found in the resources package for reference \n`resources/sql_scripts/url_shortner.sql`\n\nIn case you need to configure DSN for your local MYSQL installation. \n\nPlease do remember to make changes into\n`config/database/gorm.go`\n\nThe default DSN given is \n``\"root:@tcp(localhost:3306)/url_shortner?parseTime=true\"``\n\nThe DSN pattern is \n``\"username:password@tcp(localhost:3306)/database_name?parseTime=true\"``\n\n####  Running the Web Service\nSince the Service is written in [Golang](https://golang.org/), Please make sure your ``GOPATH,GOROOT`` variables are configured properly \n\nFollow the link above to install and configure Golang for your system\n\nExecute the following commands from the project directory to run the web service Backend on your system\n\n```bash\ngo build main.go\n./main ulimit -n 120000\n```\n\nTo build the docker image and run the docker container.\n1. First naviate to the project directory and execute the following commands\n\n```bash\n#To build the image\ndocker build -t url-shortner .\n\n#To run the image\ndocker run -d -p 8080:8080 --name url-shortner  url-shortner \n```\n\nIf you want to run the service along with monitoring using prometheus and grafana just execute\n```bash\ndocker-compose up\n```\n\n\nOnce the service starts running \n\ntry visiting ``http://0.0.0.0:8080/redirect/test``\n\nThank me Later :)\n\nNOTE:- If you are facing issues in configuration of ulimit for your MacOS please follow [this](http://blog.mact.me/2014/10/22/yosemite-upgrade-changes-open-file-limit) guideline  \n\n#### Deploying to Kubernetes\nThe app can be deployed to kubernetes by running the following commands\n```bash\n##Create the Deployment\nkubectl apply -f deployment.yaml\n\n##Create the Service\nkubectl apply -f service.yaml\n\n##Port forward the 8080\nkubectl port-forward deployment/url-shortner-deployment 8080:8080\n```\nThe guide to make sure that the docker image gets built and pulled locally is [here](https://stackoverflow.com/questions/42564058/how-to-use-local-docker-images-with-minikube)\n\nFor windows machines incase ```eval $(minikube docker-env)``` might not work so save the image to a tar file and put into minikube\n```\ndocker image save -o image.tar my_image:tag\nminikube image load image.tar\n```\n#### Unit Testing and Perf Testing\nFor unit testing you can run the go test command and get the test coverage for the project\n\n```bash\ngo test ./... -cover\n```\nAs we can see that the logic and and utils are fairly well covered with unit Test Cases \n\n![](resources/images/test_coverage.png)\n\nFor running a perf test you can execute the following go test command when the Service is running and it would execute a sample stress test on the running web service\n```bash\ngo test resources/perftest/perf_test.go -v\n```\n\nAs we can see that the backend took 0.5 seconds to shorten 1000 URLs \n\n![](resources/images/perf_test_result.png)\n\nYou can tinker around with the given perf test which doesn't use any Performance testing framework, just plain Golang concurrency primitives \n\n## Rest APIs\nThe following are rest APIs that the service provides\n\n\nTo redirect from the shortened url_id to actual long URL \n```bash\ncurl -X GET \\\n  http://localhost:8080/redirect/{url_id} \\\n  -H 'cache-control: no-cache' \n``` \n\nTo shorten a URL \n```bash\ncurl -X POST \\\n  http://localhost:8080/shorten \\\n  -H 'cache-control: no-cache' \\\n  -H 'content-type: application/json' \\\n  -d '{\n\t\"url\":\"https://www.google.com/search?q=do+a+barrel+roll\",\n\t\"request_id\":\"asdlfjhlaksdjffsajkflkjghjasfflkg\"\n}'\n```\nThe JSON response format for the above Curl would  be like\n```json\n{\n    \"request_id\": \"asdlfjhlaksdjffsajkflkjghjasfflkg\",\n    \"short_url\": \"http://0.0.0.0:8080/redirect/POnv0XFu9P\",\n    \"redirect_url\": \"https://www.google.com/search?q=do+a+barrel+roll\",\n    \"expiry\": \"2020-01-12T19:45:51.295175+05:30\"\n}\n```\n\nBy Default all links expire 24 hours from creation time but, if you want a custom expiry time you can provide that as well with an additional ``expiry`` field of Date type in the json\n\n```bash\ncurl -X POST \\\n  http://localhost:8080/shorten \\\n  -H 'cache-control: no-cache' \\\n  -H 'content-type: application/json' \\\n  -d '{\n\t\"url\":\"https://www.google.com/search?q=do+a+barrel+roll\",\n\t\"request_id\":\"asdlfjhlaksdjffsajkflkjghjasfflkg\",\n        \"expiry\": \"2020-01-12T19:45:51.295175+05:30\"\n}'\n```\n\n\nTo get URL opening stats (top 10 most frequently visited URLs in the last 24 hours in descending order)\n````bash\ncurl -X GET \\\n  http://localhost:8080/stats \\\n  -H 'cache-control: no-cache' \n````\n\nThe JSON response format for the above Curl would be like\n```json\n{\n    \"message\": \"The most frequent URL clicks/redirects in last 24 hours\",\n    \"url_stats\": [\n        {\n            \"url\": \"http://0.0.0.0:8080/redirect/POnv0XFu9P\",\n            \"count\": 4\n        },\n        {\n            \"url\": \"http://0.0.0.0:8080/redirect/vYyVeiUtUd\",\n            \"count\": 3\n        },\n        {\n            \"url\": \"http://0.0.0.0:8080/redirect/test\",\n            \"count\": 1\n        }\n    ]\n}\n```\n\n## Design\n#### Overview\n1. The overall backend design is fairly simple we have a Golang based backend running an http server.\n2. The backend connects to a persistent storage in our case MYSQL where all CRUD operations happen.\n3. The URL expiry logic is handled by a cron which resides along with the application backend and runs periodically.\n\n#### Architecture diagram\n\n![](resources/images/URL-Shortner.png)\n\n#### Design choices and justifications\n##### Influencing factors for taking these design decisions are \n1. The requirements for scale (shortening thousands of URLs in a second).\n2. The requirement for providing statistics on URL visits (extrapolated this to more complex analytical requirements in the future).\n3. Implementation of an expiration logic to expire URLs.\n4. Developer familiarity.\n5. Time constraint (As this is a weekend project :) \n\n##### Why Golang was used as the Backend Language ?\n1. Highly scalable.\n2. Less amount of boiler plate code.\n3. Inbuilt concurrency primitives such as go routines and channels.\n4. Inbuilt monitoring and profiling tools like pprof (which has been integrated to provide cpu,memory metrics etc.)\n\n##### Why Database was chosen to be MYSQL ?\n1. Relational Database.\n2. Can perform joins etc if any complex statistical or analytical requirements and use-cases come up.\n3. Fairly scalable.\n\n##### Why use Cron for expiry ?\n1. Automated and Periodic in nature.\n2. Fire and forget.\n\n## Contributing\nPull requests are welcome for a few features that are in the TODO pipeline \n1. Addition of Cache to reduce redirection search time\n2. Implementation of a distributed lock before execution of delete queries by Cron.\n3. Addition of metrics using graphite etc.\n4. Making the Backend more configurable.\n5. Dockerization of the service.\n\n\n\n## License\n[MIT](https://choosealicense.com/licenses/mit/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgkeesh7%2Furl-shortner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgkeesh7%2Furl-shortner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgkeesh7%2Furl-shortner/lists"}