This project is a URL Shortener service implemented in Go. It provides RESTful APIs to shorten long URLs and retrieve original URLs using a shortened identifier. The project uses an extensible design with support for multiple storage backends, caching, and configuration management. This is a Go 🐹 learning project for me
- Shorten URLs: Generate a short, unique URL for any valid original URL.
- Retrieve Original URLs: Retrieve (get re-directed to) the original URL using the shortened identifier.
- In-Memory Storage: Default storage backend for rapid prototyping.
- PostgreSQL Support: Optional storage backend for persistence.
- Caching with Redis: Reduce database loads by caching
- Environment Configurations: Support for development and production environments.
- Extensible Design: Easily swap or add new storage backends.
- Go 1.20+
- Redis for caching (optional, but recommended)
- PostgreSQL for persistent storage (optional)
- Make - optional
- Docker - optional
Clone the repository:
$ git clone https://github.com/khaled4vokalz/gourl_shortener.git
-
Bare Metal:
-
get into server directory
$ cd gourl_shortener/server
-
install dependencies:
$ go mod tidy
-
set up configuration: create a configuration file in yaml format or use environment variables for settings like the port, database, and cache.
-
If you're using postgres as storage option, then run the
scripts/init-db-script.sh
script, so that needed schemas are created. Make sure to pass in theDB_USER
,DB_PASSWORD
andDB_NAME
envs. -
run the application:
$ go run ./cmd/gourl_shortener
Alternatively, we can use
make
. If you're in theserver
directory thencd
out of it first$ cd .. $ make run-server
-
-
Docker
-
build the docker image
$ docker build --tag go-url-shortener-server .
-
run the container
$ docker run --detach --env GOURLAPP_storage_type=in-memory --env GOURLAPP_cache_enabled=false --name gourl_shortener --publish 8082:8080 go-url-shortener-server
-
-
Bare Metal:
-
Get into the client directory
$ cd client
-
Install dependencies (use node 20+)
$ nvm use 20.0.0 $ npm ci
-
Start the app
We can set
REACT_APP_BACKEND_URL
to the url where the backend api is available, e.g.REACT_APP_BACKEND_URL=http://localhost:8080
before runningnpm start
. By default the app useshttp://localhost:8082
as the backend url.$ npm start
Alternatively, we can use
make
. If you're in theclient
directory thencd
out of it$ cd .. $ make run-client
App should be running in development mode. Open http://localhost:3000 to view it in the browser. It's using Hot reload.
-
-
Docker
-
Get into the client directory
$ cd client
-
build the docker image
Build time arg
BACKEND_URL
may be set to override the defaulthttp://localhost:8082
that the app uses as backend url.$ docker build --build-arg BACKEND_URL=http://url-where-backend-is-running:port --tag go-url-shortener-client .
-
run the container
$ docker run --detach --name gourl_shortener_client --publish 3000:80 go-url-shortener-client
Open http://localhost:3000 to view it in the browser.
-
Running the below command should spin up the containers with needed database and tables in it.
- The postgres db is listening at port
5433
- The redis db is listening at port
6380
- The backend is listening at port
8082
- The frontend app is listening at
3000
$ POSTGRES_PASSWORD=<your-postgres-pass> DB_PASSWORD=<your-db-pass> docker compose up --detach
POST /shorten
-
without
Host
headerRequest:
curl --verbose --request POST --data '{"url": "http://example.com"}' localhost:8082/shorten
Response:
{ "shortened_url": "http://localhost:8082/CBXqmaO8" }
-
with
Host
headerRequest:
curl --verbose --request POST --header "Host: https://foo.com" --data '{"url": "http://example.com"}' localhost:8082/shorten
Response:
{ "shortened_url": "https://foo.com/CBXqmaO8" }
GET /CBXqmaO8
Response headers:
< HTTP/1.1 308 Permanent Redirect
< Content-Type: text/html; charset=utf-8
< Location: http://example.com
GET /health/live
Response (with status code 200
):
{ "databaes_is_live": true, "cache_is_alive": true }
Response (with status code 500
, in case either/all of the components are not alive):
{ "databaes_is_live": true, "cache_is_alive": false }
- Configuration can be provided via YAML files or environment variables, currently it only supports config file in the
configuration
directory having the same name of the ENVIRONMENT env. Example YAML configuration:
server:
host: localhost
port: 8080
storage:
type: postgres
dbConnString: "user=shortener password=<pass> dbname=gourl_shortener sslmode=disable"
cache:
enabled: true
host: localhost
port: 6379
database: 0
shortenerProps:
length: 6 # the total bytes that should be considered from the SHA256 hash of the url
maxAttempt: 5 # maximum attempt the service should take when key collision happens for a url
environment: dev # the environment of the application, either `dev` or `prod`.
- Override configurations using Environment Variables
Any configuration mentioned above can be overridden using Environment variables using GOURLAPP_
prefix and use the property tree separated by underscores (_
). e.g. If we want to override maxAttempt
configuration, we can set it like GOURLAPP_shortenerProps_maxAttempt=10
- only
REACT_APP_BACKEND_URL
environment variable that can override the backend url the app should talk to
Run unit tests with:
$ make test-server
If you don't have make
you can get inside the server directory and run
$ cd gourl_shortener/server
$ go test ./...
- [] Implement analytics for shortened URLs (e.g., number of clicks)
- Add expiration time
- Add a web UI for managing shortened URLs
- Add support for Environment variables in the config files
- Add docker support
- Add logging
- Health-check
- [] House keeping
- [] Provide default values for the configurations, probably my having a separate config manager
This project is licensed under the MIT License. See the LICENSE file for details.