{"id":21524042,"url":"https://github.com/helloakn/url-shortener-service","last_synced_at":"2025-04-09T22:51:28.169Z","repository":{"id":62738246,"uuid":"561828384","full_name":"helloakn/url-shortener-service","owner":"helloakn","description":"URL-shortener service to shorten URLs. API clients will be able to create short URLs from a full length URL.","archived":false,"fork":false,"pushed_at":"2023-03-10T03:11:53.000Z","size":4394,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T22:51:20.930Z","etag":null,"topics":["docker","mysql","redis","typescript","url-shortener"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/helloakn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-04T15:26:07.000Z","updated_at":"2025-02-24T08:12:29.000Z","dependencies_parsed_at":"2023-01-23T00:31:10.619Z","dependency_job_id":null,"html_url":"https://github.com/helloakn/url-shortener-service","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helloakn%2Furl-shortener-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helloakn%2Furl-shortener-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helloakn%2Furl-shortener-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helloakn%2Furl-shortener-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/helloakn","download_url":"https://codeload.github.com/helloakn/url-shortener-service/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125643,"owners_count":21051766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["docker","mysql","redis","typescript","url-shortener"],"created_at":"2024-11-24T01:20:26.290Z","updated_at":"2025-04-09T22:51:28.144Z","avatar_url":"https://github.com/helloakn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# URL Shortener Service\n[![Star Count](https://img.shields.io/badge/dynamic/json?color=brightgreen\u0026label=Star\u0026query=stargazers_count\u0026url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Furl-shortener-service)](https://github.com/helloakn/url-shortener-service) [![Licence](https://img.shields.io/badge/dynamic/json?color=informational\u0026label=LICENCE\u0026query=license.name\u0026url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Furl-shortener-service)](https://github.com/helloakn/url-shortener-service) [![Language](https://img.shields.io/badge/dynamic/json?color=blueviolet\u0026label=Language\u0026query=language\u0026url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Furl-shortener-service)](https://github.com/helloakn/url-shortener-service) \n\n[![NodeJs](https://img.shields.io/badge/nodejs-v18.6.0-green)](https://github.com/helloakn/url-shortener-service) \n[![Express](https://img.shields.io/badge/express-v^4.18.1-green)](https://github.com/helloakn/url-shortener-service) \n## Table of content\n- [1] Application Description\n- [2] Application Functionalities\n- [3] Git Conventional Commits Message\n- [4] Flow Diagram\n- [5] File Structure\n- [6] How To Run\n  - [6.1] local machine without docker \n    - [6.1.1] configuration\n    - [6.1.2] installation\n    - [6.1.3] run\n  - [6.2] local machine with docker ( Recommended )\n    - [6.2.1] create docker network\n    - [6.2.2] build images\n    - [6.2.3] Create Containers\n- [7] How To Test\n- [8] OpenApi\n- [9] Acknowledgments\n---\n\n### [1] Application Description\nURL-shortener service to shorten URLs.  \nAPI clients will be able to create short URLs from a full length URL.  \nIt will also support redirecting the short urls to the correct url.\n\n### [2] Application Functionalities\n\n\u003cdetails\u003e\n \u003csummary\u003e[2.1] Url Shortening\u003c/summary\u003e\n \n- An API Client can send a url and be returned a shortened URL. \n- An API Client can specify an expiration time for URLs when creating shortened URL \n- Handle visits to Shortened URLs, they must redirect to the original URL with a HTTP \n302 redirect, 404 if not found. \n- Visiting expired URLs must return HTTP 410 \n- Input URL should be validated and respond with error if not a valid URL \n- Regex based blacklist for URLs, urls that match the blacklist respond with an error \n- Hit counter for shortened URLs (increment with every hit) \n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e [2.2] Admin API\u003c/summary\u003e \n\n- Admin api (requiring token) to list \n  - Short Code \n  - Full Url \n  - Expiry (if any) \n  - Number of hits \n- Above list can filter by Short Code and keyword on origin url. \n- Admin api to delete a URL (after deletion shortened URLs must return HTTP 410 on visit) \n\u003c/details\u003e\n\n---\n\n### [3] Git Conventional Commits Message\n\u003cpre\u003e\n\u003cb\u003e\u003ca href=\"#body\"\u003e[Type] : Message\u003c/a\u003e\u003c/b\u003e\n\u003csub\u003eType =\u003e  [Create], [Modify], [Fix], [Delete]\u003c/sub\u003e\n\u003csub\u003eMessage =\u003e  Describe about the commit\u003c/sub\u003e\n\n\u003cb\u003e\u003ca href=\"#body\"\u003eExample\u003c/a\u003e\u003c/b\u003e\n\u003csub\u003e[Create] message event for bla bla bla\u003c/sub\u003e\n\u003csub\u003e[Modify] message event for bla bla bla\u003c/sub\u003e\n\u003csub\u003e[Fix] message event for bla bla bla\u003c/sub\u003e\n\u003c/pre\u003e\n\n---\n\n### [4] Flow Diagram\n\n#### [4.1] Chache Layer Flow\n\u003cimg src=\"resources/cache-layer-flow.png\" width=\"70%\"\u003e\n\n#### [4.2] Admin API Flow\n\u003cimg src=\"resources/admin-api-flow.png\" width=\"70%\"\u003e\n\n---\n\n### [5] File Structure\n```\n-api\n├── __tests__                         # for testing\n│   └── integration                   # for all the integration tests \n├── src                               # for typescripts\n│   ├── app                           # AS AS Open/Close Principle\n│   │   ├── controllers\n│   │   │    └─ ...\n│   │   │       ├─ controller.ts\n│   │   │       └─ events             \n│   │   ├── middlewares               \n│   │   ├── models                    # for database table\n│   │   └── routes                    # for admin / user / swigger api routes\n│   ├── core                          # AS Single Responsibility Principle\n│   │   ├── common\n│   │   │    ├── cache                # redis\n│   │   │    ├── database             # mysql\n│   │   │    └── validator  \n│   │   │         └── rules           # we can add additional rules\n│   │   │             └── ...\n│   │   ├── functions                 # For Resuable functions\n│   │   ├── http                      # For Express\n│   │   ├── types\n├...\n├docker\n├── config                            #\n│   └── ...\n├...\n├── resources                         # images for readme file\n│   └── ...\n```\n\n---\n\n### [6] How To Run\nWe have to choose our environment that where we will host our application.  \nIn this moment, I will make cover for two environment.  \n\n\u003cdetails\u003e\n\u003csummary\u003e [6.1] local machine without docker \u003c/summary\u003e \n\nfirst we need already installed node , mysql into machine.  \nfor second, we have to configure.  \n\n\u003cdetails\u003e\n\u003csummary\u003e [6.1.1] configuration \u003c/summary\u003e \n\ncreate **.env** file ./api/.env with the following content  \n\n(you have to replace with yours.)\n```\nREST_SERVER_HOST=localhost\nREST_SERVER_PORT=9090\n\nDB_SERVER_HOST=localhost\nDB_SERVER_PORT=3306\nDB_SERVER_USR=username\nDB_SERVER_PASSWD=password\nDB_SERVER_DB_NAME=databasename\n\nREDIS_SERVER_HOST=redis-host\nREDIS_SERVER_PORT=redis-port\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e [6.1.2] installation \u003c/summary\u003e \n\n```\ncd api\nnpm install\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e [6.1.3] run \u003c/summary\u003e \n\n```\ncd api\nnpm run dev\n```\n\u003c/details\u003e\nthen we can access to http://localhost:9090 , you may need to check the port \n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e [6.2] local machine with docker ( Recommended ) \u003c/summary\u003e \n\n\n\u003cimg src=\"resources/local-archie.png\" width=\"70%\"\u003e\n\n##### [6.2.1] create docker network\n```\ndocker network create \\\n  --driver=bridge \\\n  --subnet=172.3.0.0/16 \\\n  --ip-range=172.3.0.0/24 \\\n  urlshortenernetwork\n```\n\n##### [6.2.2] build images\n\u003cdetails\u003e\n\u003csummary\u003e database image \u003c/summary\u003e \n\n```\ndocker build -t urlshortener:databaselayer \\\n  --build-arg db_host=localhost \\\n  --build-arg db_port=3306 \\\n  --build-arg  db_user=root \\\n  --build-arg db_password=password \\\n  --build-arg db_name=urlshortenerservice \\\n  --no-cache -f ./docker/databaseDockerfile .\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e application image \u003c/summary\u003e \n\n```\ndocker build -t urlshortener:application \\\n  --no-cache -f ./docker/applicationDockerfile .\n```\n\u003c/details\u003e\n\n##### [6.2.3] Create Containers\n\n\u003cdetails\u003e\n\u003csummary\u003e cache container \u003c/summary\u003e \n\n```\ndocker run --name cachecontainer \\\n--network=urlshortenernetwork \\\n--ip 172.3.0.15 \\\n-d redis\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e database container \u003c/summary\u003e \n\n```\ndocker run -i -t -d --name databasecontainer \\\n  --network=urlshortenernetwork \\\n  --ip 172.3.0.10 \\\n  --privileged urlshortener:databaselayer\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e application container \u003c/summary\u003e \n\n```\ndocker run -i -t -d --name applicationcontainer \\\n  -p 9090:9090 \\\n  --network=urlshortenernetwork \\\n  --ip 172.3.0.12 \\\n  --privileged urlshortener:application\n```\n\u003c/details\u003e\n\nclean all images of urlshortener\n```\ndocker rmi $(docker images urlshortener -q) -f\n```\n\n\u003c/details\u003e\n\n\n\n\n\nFor future, I will make cover for \n- ECR/ECS  \n  \u003cimg src=\"resources/cloud-archie.png\" width=\"70%\"\u003e\n- Lambda(Function/Layer/APIGateway)\n\n---\n\n### [7] How to Test\nThis test support only for \"local machine without docker\"  \n\u003cdetails\u003e\n \u003csummary\u003e Unit Test\u003c/summary\u003e\n \n command\n ```\n cd api\n npm run test:unit\n ```\n Screen Shoot for \"Unit Testing\"  \n \u003cimg src=\"resources/unit-test.png\" width=\"70%\"\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003e Integration Test\u003c/summary\u003e\n \n command\n ```\n cd api\n npm run test:e2e\n ```\n Screen Shoot for \"Integration Testing\"  \n \u003cimg src=\"resources/integration-test.png\" width=\"70%\"\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003e Test all in once\u003c/summary\u003e\n \n command\n ```\n cd api\n npm run test\n ```\n Screen Shoot for \"All In One Testing\"  \n \u003cimg src=\"resources/all-test.png\" width=\"70%\"\u003e\n\u003c/details\u003e\n\n---\n\n#### [8] OpenApi\nPls let me assume our api is http://localhost:9090 .  \nSo, our swagger url will be http://localhost:9090/swagger/  \nScreen Shoot :  \n\u003cimg src=\"resources/swagger-ss.png\" width=\"70%\"\u003e\n\n### [9] Acknowledgments\nHello Visitor,\nI just want to let you know what a pleasure it was.  \n  \nI am truly grateful for the opportunity to speak with you and I look forward to hearing from you soon.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelloakn%2Furl-shortener-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhelloakn%2Furl-shortener-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelloakn%2Furl-shortener-service/lists"}