{"id":17087719,"url":"https://github.com/btkostner/shorty","last_synced_at":"2026-04-10T01:09:50.190Z","repository":{"id":47152119,"uuid":"401200356","full_name":"btkostner/shorty","owner":"btkostner","description":"A simple URL Shortener","archived":false,"fork":false,"pushed_at":"2021-09-11T05:58:42.000Z","size":189,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-28T20:46:47.720Z","etag":null,"topics":["docker","elixir","phoenix","url-shortener"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/btkostner.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":"2021-08-30T03:06:02.000Z","updated_at":"2022-03-10T00:11:10.000Z","dependencies_parsed_at":"2022-08-19T21:11:19.415Z","dependency_job_id":null,"html_url":"https://github.com/btkostner/shorty","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/btkostner%2Fshorty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btkostner%2Fshorty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btkostner%2Fshorty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btkostner%2Fshorty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/btkostner","download_url":"https://codeload.github.com/btkostner/shorty/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245119574,"owners_count":20563763,"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","elixir","phoenix","url-shortener"],"created_at":"2024-10-14T13:34:41.346Z","updated_at":"2025-12-30T23:31:47.494Z","avatar_url":"https://github.com/btkostner.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003cimg src=\"./priv/static/favicon-192.png\" alt=\"Shorty URL Shortener\" width=\"192\" height=\"192\"\u003e\n\n  \u003ch1\u003eShorty\u003c/h1\u003e\n  \u003ch2\u003eA simple URL Shortener\u003c/h2\u003e\n  \u003cbr\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/btkostner/shorty/commits/master\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/last-commit/btkostner/shorty.svg?style=flat-square\u0026logo=github\u0026logoColor=white\" alt=\"GitHub last commit\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/btkostner/shorty/issues\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/issues-raw/btkostner/shorty.svg?style=flat-square\u0026logo=github\u0026logoColor=white\" alt=\"GitHub issues\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/btkostner/shorty/pulls\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/issues-pr-raw/btkostner/shorty.svg?style=flat-square\u0026logo=github\u0026logoColor=white\" alt=\"GitHub pull requests\"\u003e\n  \u003c/a\u003e  \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#about\"\u003eAbout\u003c/a\u003e •\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e •\n  \u003ca href=\"#running\"\u003eRunning\u003c/a\u003e •\n  \u003ca href=\"#developing\"\u003eDeveloping\u003c/a\u003e •\n  \u003ca href=\"#faq\"\u003eFAQ\u003c/a\u003e\n\u003c/p\u003e\n\n## About\n\nShorty is a dead simple URL shortener written in Elixir. It uses [hashids](https://hashids.org/) to create unique hashes based on the PostgreSQL auto increment pkey. This project was made to be dead simple and easily readable, with an easy path to scaling due to it's only dependency being PostgreSQL.\n\n## Features\n\n- A simple interface for adding URLs\n- Exact same URL gets the exact same hash\n- A single PostgreSQL dependency\n- No JavaScript required (though it does add some nice helper features)\n- Easy to deploy docker image\n\n## Running\n\nFor running this project, we have published a production ready docker image available at `ghcr.io/btkostner/shorty:latest`. Because every deployment platform is different, we will not cover how to deploy shorty to any specific platform, but we can give a quick run down on how to run it locally. For configuration, you can view the [environmental variables section](#Environmental-Variables).\n\n### Docker\n\nTo start out, you will need these dependencies installed and usable.\n\n  1. [Docker](https://docs.docker.com/get-docker/)\n  2. A [PostgreSQL](https://www.postgresql.org/download/) instance with a _database already created_.\n  3. A domain with SSL enabled. The docker image does not include SSL termination, but will include an `https://` on all full URLs it generates.\n\nNext up, you will need to pull the docker image with:\n\n```sh\ndocker pull ghcr.io/btkostner/shorty:latest\n```\n\nOnce that is done, you will need to set three (or more) configuration variables.\n\n  1. The `DATABASE_URL` to connect to postgreSQL.\n  2. The `HOST` variable to create full URLs.\n  3. The `SECRET_KEY_BASE` which needs to be 64 character random string.\n\nThe easiest way is to create a `.env` file that looks something like this:\n\n```txt\nDATABASE_URL=\nHOST=\nSECRET_KEY_BASE=\n```\n\nMore information can be found in the [environmental variables section](#Environmental-Variables) below.\n\nThen you will want to migrate your database with this command:\n\n```sh\ndocker run --rm --network host --env-file .env -it ghcr.io/btkostner/shorty:latest eval Shorty.Release.migrate\n```\n\nAnd then finally, you can run the web server on port `8080` with:\n\n```sh\ndocker run --rm --network host --env-file .env -it ghcr.io/btkostner/shorty:latest\n```\n\n### Environmental Variables\n\n- `DATABASE_URL` - A full PostgreSQL connection URL. It should be in the form of `ecto://username:password@hostname/database`.\n\n- `HASH_ID_SALT` - A random salt used in creating the short URL hash. Changing this after you have already inserted URLs into the database will result in all old hashes being incorrect.\n\n- `HOST` - The domain people will access Shorty from. This is used to create full length URLs shown to users.\n\n- `POOL_SIZE` - The amount of connections to pool for the database. Defaults to `10`.\n\n- `SECRET_KEY_BASE` - Secret key for hashing web server cookies. This should be a 64 character random string generated with strong entropy.\n\n## Developing\n\nFor developing, a local `docker-compose.yml` file and a `Makefile` has been included for convenience.\n\nFirstly, you will need `docker` and `docker-compose` installed. Both have [excellent guides online for installing](https://docs.docker.com/compose/install/).\n\nNext, simply run `make setup` to build the docker images needed for development. Any time you make changes to a dependency, you will need to re-run this command.\n\nNext, you can run `make server` to start the development server. Most code should hot reload and be available without having to restart the server.\n\nIf you want to run the unit tests, you can do `make test`. And if you want to do the linting and formatting, you can do `make lint`. All of these will be ran when you open a PR.\n\n## FAQ\n\n### URL validation is really lax.\n\nYes. Yes it is. I could add more validation, ensure the URL is properly encoded on entry. Make sure there are no spaces or weird backslash stuff. It really doesn't matter though. Phoenix will encode the URL correctly when we redirect, so I left validation to the built in URI module. I could roll my own logic here, but I'm sure the people who have wrote the URI module know much more about the official spec, edge cases, and \"the correct way of doing things\" more than I do. Plus, the easy of entry adds to usability and UX.\n\n### Why PostgreSQL as a data store?\n\nIt's the simplest data store to get up and running, has plenty of features (unique index on a text field with no character limit), and is used by _lots_ of large companies. This means any sort of scaling issue you run into has been blogged about before, any tool that you can think of has already been made, and just generally is a solid base to work on. It just works :tm: without a problem, which is exactly what you want from a database.\n\n### The UI looks familiar.\n\nGood eye. I used tailwind CSS for the styling, and used some tailwind UI templates for pages. They were adjusted to fit the project a bit more, but overall, it has a very stock tailwind feel to it.\n\n### Are you going to add XXX feature?\n\nProbably not. I consider the current state of Shorty pretty feature complete. Luckily, it's pretty easy to fork and add your own features to (like an admin interface.)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtkostner%2Fshorty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbtkostner%2Fshorty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtkostner%2Fshorty/lists"}