A URL shortener service in Deno
Introduction
URL shortening is one of the commonly used web app. Shortening a URL & then redirecting the caller fully suits the async model of runtimes like Deno. This is because shortening & redirecting isn’t a CPU intensive operation.
In this article, we’ll build a simple URL shortening server in Deno. All the code is present in GitHub repo here.
Features
The features of the URL shortener service are:
- Simple end points POST /shorten and GET /short-url
- Uses only native code and Deno’s standard library
- Uses localStorage for persistent storage
Endpoints
The URL shortener service has two endpoints:
- POST /shorten: To shorten a given URL
- GET /short-url: To redirect caller to the long URL for a given short URL
Shorten a URL (POST)
To shorten a URL, POST /shorten?target=<normal-url> need to be used. As shown in the URL, a mandatory query param target needs to be passed.
curl http://sho.rt/shorten?target=https://doc.deno.land/builtin/stable -X POST
The shorten URL API returns the shortened URL in the response body:
{"shortUrl":"http://sho.rt/001c09e5"}
Redirect a short URL (GET)
To redirect from a shortened URL to original URL, GET /short-url need to be used. This is the URL that was returned in the response of the POST /shorten API call.
> curl http://sho.rt/001c09e5 -v
* Connected to sho.rt (127.0.0.1) port 80 (#0)
GET /001c09e5 HTTP/1.1
Host: sho.rt
HTTP/1.1 302 Found
location: https://doc.deno.land/builtin/stable
The response to the GET /short-url is redirection using 302 to the original URL.
Service
The URL shortener service is structured in the usual way:
- Server
- Router
- Controller
- Service
For shortening a URL, the unique ID & original URL mapping is stored in the localStorage instead of a database. The localStorage is a simple KV storage that internally uses SQLite db. This is sufficient for our needs. For more details on localStorage, visit the article here.
Server
The server checks the sandbox access, initializes the listener, and accepts the HTTP connection. It uses native HTTP server (hyper based).
Router
The router is very simple. As there are two simple endpoints, there is no need for a router framework. The router checks the request URL and method, and then directs the request to the appropriate function of the controller. For no match, the router responds with either 404 or 405.
Controller
For shortening a URL, the controller gets the target URL and calls the add function provided by the service. For processing a short URL, the controller gets the short URL and calls the get function provided by the service. Upon receiving a response from the service, the controller sends an appropriate HTTP response to the caller.
Service
The service provides two functions: get and add. The add function generates an ID and stores ID -> target mapping in the localStorage. The get function retrieves the target from the localStorage using ID. There is no delete function (usually there isn’t).
Others
To support all the above, there are some utilities & constants.
Run & test
The URL shortener service can be started with the following command:
> deno run --allow-net=0.0.0.0:80 --unstable --location http://sho.rt server.ts
It needs permission to allow serving sockets on port 80, and it also needs a mandatory command line parameter — location. The location is useful in keying the localStorage.
Here are some curl commands to test the service: Some are about shortening a target, while others are about accessing a shortened URL.
> curl http://sho.rt
HTTP/1.1 404 Not Found> curl http://sho.rt/shorten -X POST
HTTP/1.1 400 Bad Request> curl http://sho.rt/shorten?target -X POST
HTTP/1.1 400 Bad Request> curl http://sho.rt/shorten?target=https://doc.deno.land/builtin/stable -X POST
HTTP/1.1 200 OK
{"shortUrl":"http://sho.rt/4e298182"}> curl http://sho.rt/4e298182
HTTP/1.1 302 Found
location: https://doc.deno.land/builtin/stable> curl http://sho.rt/4e298183 //wrong URL
HTTP/1.1 404 Not Found
Let’s see it in action in the browser:
That’s all about URL shortener service. Here are some other services built in Deno: