{"id":23223805,"url":"https://github.com/guilhermecaz/shurl_express","last_synced_at":"2026-04-18T02:07:19.796Z","repository":{"id":253312537,"uuid":"843122467","full_name":"GuilhermeCAz/shurl_express","owner":"GuilhermeCAz","description":"URL shortening application built with Express. It provides an API for users to create, list, edit and delete short URLs.","archived":false,"fork":false,"pushed_at":"2024-08-17T21:46:10.000Z","size":255,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T16:37:48.296Z","etag":null,"topics":["express","rest-api","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GuilhermeCAz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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":"2024-08-15T20:38:53.000Z","updated_at":"2024-08-17T21:47:37.000Z","dependencies_parsed_at":"2024-12-23T16:46:02.570Z","dependency_job_id":null,"html_url":"https://github.com/GuilhermeCAz/shurl_express","commit_stats":null,"previous_names":["guilhermecaz/shurl_express"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GuilhermeCAz/shurl_express","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuilhermeCAz%2Fshurl_express","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuilhermeCAz%2Fshurl_express/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuilhermeCAz%2Fshurl_express/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuilhermeCAz%2Fshurl_express/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GuilhermeCAz","download_url":"https://codeload.github.com/GuilhermeCAz/shurl_express/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuilhermeCAz%2Fshurl_express/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31953517,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T00:39:45.007Z","status":"online","status_checked_at":"2026-04-18T02:00:07.018Z","response_time":103,"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":["express","rest-api","typescript","url-shortener"],"created_at":"2024-12-18T23:19:26.778Z","updated_at":"2026-04-18T02:07:19.780Z","avatar_url":"https://github.com/GuilhermeCAz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shurl Express\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Node.js Logo](https://img.shields.io/badge/Node.js-%235FA04E?logo=nodedotjs\u0026logoColor=white)](https://nodejs.org/)\n[![TypeScript Logo](https://img.shields.io/badge/TypeScript-%233178C6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/)\n[![Express](https://img.shields.io/badge/Express-black?logo=express)](https://expressjs.com/)\n[![Docker](https://img.shields.io/badge/Docker-%232496ED?logo=docker\u0026logoColor=white)](https://www.docker.com/)\n[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-%234169E1?logo=postgresql\u0026logoColor=white)](https://www.postgresql.org/)\n[![TypeORM](https://img.shields.io/badge/TypeORM-%23FE0803?logo=typeorm)](https://typeorm.io/)\n[![JWT](https://img.shields.io/badge/JWT-black?logo=jsonwebtokens)](https://jwt.io/)\n[![OpenAPI](https://img.shields.io/badge/OpenAPI-%2385EA2D?logo=swagger\u0026logoColor=white\u0026color=null)](https://swagger.io/specification/)\n[![Prettier](https://img.shields.io/badge/Prettier-darkblue?logo=prettier)](https://prettier.io/)\n[![ESLint](https://img.shields.io/badge/ESLint-%234B32C3?logo=eslint)](https://eslint.org/)\n\n## About\n\nShurl Express is a web application that allows you to shorten URLs. It was developed as part of a code challenge in a recruitment process.\n\nThe Node.js version, as specified in the [Dockerfile](Dockerfile), is `22.x`, which was the latest stable version of Node.js as of the launch of this app.\nSince this was a development challenge, tsx was used. Therefore, no building of the app is done on the container.\n\n\u003e Para uma versão em português do README, acesse o [README em português](README_pt-br.md)\n\n### Requirements\n\nHere are some requirements made by the recruiter, simplified:\n\n- Use latest stable version of Node.js\n- Use TypeScript\n- Implement token-based authentication, with user registration and login\n- Shorten URLs with a maximum length of 6 characters for the slug\n- Track the number of times each shortened URL is accessed\n- Implement logical deletion of URLs\n- Registers must have a update timestamp\n- Build endpoints to:\n  - Shorten URLs (token optional)\n  - Access shortened URLs\n  - List shortened URLs by user (token required)\n  - Update shortened URLs (token required)\n  - Delete shortened URLs (token required)\n\n### Extras\n\n- Use Docker Compose to build and run the project\n- Implement Swagger documentation\n- Configure pre-commit hooks\n\n## Setup\n\n### 1. Clone this repository\n\n```sh\ngit clone https://github.com/GuilhermeCAz/shurl_express.git\ncd shurl_express\n```\n\n### 2. Create a `.env` file according to [.env.example](.env.example)\n\n### 3. Build the Docker images and run the containers\n\n```sh\ndocker compose up --build --detach\n```\n\nThe following command can be used instead:\n\n```sh\nnpm run up\n```\n\n**Server should now be accessible at `http://localhost:3000`.**\n\nTo stop the server, use the following command:\n\n```sh\ndocker compose down\n```\n\n## API Endpoints\n\n### Authentication\n\n- `POST /register`: Register a new user.\n- `POST /login`: Log in and obtain a JWT.\n\n### URL Management\n\n- `POST /urls`: Shorten a URL. Requires valid `originalURL` in the request body.\n- `GET /urls`: List all URLs associated with the authenticated user.\n- `PATCH /urls/:slug`: Update the original URL for a specific slug. Requires valid `originalURL` in the request body.\n- `DELETE /urls/:slug`: Logically delete a URL by slug.\n- `GET /:slug`: Redirect to the original URL.\n\n## Usage\n\nThe following commands can be used to interact with the API. **Alternatively, you can use the Swagger UI at `http://localhost:3000/docs`**.\n\n### Register User | `optional`\n\n```sh\ncurl -X 'POST' \\\n  'http://localhost:3000/register' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"email\": \"user@example.com\",\n  \"password\": \"Password123\"\n}'\n```\n\n### Login | `optional` -\u003e returns JWT\n\n```sh\ncurl -X 'POST' \\\n  'http://localhost:3000/login' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"email\": \"user@example.com\",\n  \"password\": \"Password123\"\n}'\n```\n\nAfter logging in, you can use the JWT you received in the Authorization header by adding the following to your request:\n\n```sh\n-H 'Authorization: Bearer ${JWT}'\n```\n\nThis enables associating shortened URLs with the user. Therefore, you are authorized to list, edit and delete URLs shortened by you.\n\n### Shorten URL | `token optional`\n\n```sh\ncurl -X 'POST' \\\n  'http://localhost:3000/urls' \\\n  -H 'accept: application/json' \\\n  -H 'Authorization: Bearer ${JWT}' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"originalURL\": \"https://example.com\"\n}'\n```\n\n### List User URLs | `token required`\n\n```sh\ncurl -X 'GET' \\\n  'http://localhost:3000/urls' \\\n  -H 'accept: application/json' \\\n  -H 'Authorization: Bearer ${JWT}'\n```\n\n### Update URL | `token required`\n\n```sh\ncurl -X 'PATCH' \\\n  'http://localhost:3000/urls/${slug}' \\\n  -H 'accept: application/json' \\\n  -H 'Authorization: Bearer ${JWT}' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"originalURL\": \"https://example.com\"\n}'\n```\n\n### Delete URL | `token required`\n\n```sh\ncurl -X 'DELETE' \\\n  'http://localhost:3000/urls/${slug}' \\\n  -H 'accept: */*' \\\n  -H 'Authorization: Bearer ${JWT}'\n```\n\n### Redirect\n\n```sh\ncurl -X 'GET' \\\n 'http://localhost:3000/${slug}' \\\n -H 'accept: */*' \\\n```\n\n#### Note\n\nSwagger UI `try it out` button does not work for this request due to CORS policy. Use the browser instead.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguilhermecaz%2Fshurl_express","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguilhermecaz%2Fshurl_express","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguilhermecaz%2Fshurl_express/lists"}