How to make a URL Shortener from scratch

TZGyn - Sep 15 - - Dev Community

Making applications from scratch is my favorite way to learn how they work. This post will be discussing on how to make a URL Shortener from scratch.

url shortener example

URL shortener is extremely easy to make, great way to learn a language as a beginner in my opinion. The harder part is adding custom domains, analytics (such as getting user device information), grouping links and other features added on top of the URL shortener service. So here is how you can make one from scratch.

In this tutorial we will be using hono (nodejs), drizzle orm and postgres, but it can be done with any language and framework, check out my sveltekit/golang implementation kon.sh, source code in github.

Start by creating a new hono project

npm create hono@latest
Enter fullscreen mode Exit fullscreen mode

Then fill in the following information

create-hono@0.13.1
Ok to proceed? (y)
create-hono version 0.13.1
? Target directory url-shortener
? Which template do you want to use? nodejs
? Do you want to install project dependencies? yes
? Which package manager do you want to use? npm
Enter fullscreen mode Exit fullscreen mode

Make sure you have your postgres database ready and create a new database called url-shortener.

Under src/ folder there should be a index.ts file, this file contains the code to run your api server. Here we need to add 2 api routes.

  • create shortener
  • catch-all route to redirect incoming requests

src/index.ts

import { serve } from "@hono/node-server";
import { Hono } from "hono";

const app = new Hono();

app.post("/api/shortener", async (c) => {
    // create shortener route
    return c.text("Not yet implemented");
});

app.get("/:code", async (c) => {
    // redirect
    return c.text("Not yet implemented");
});

const port = 3000;
console.log(`Server is running on port ${port}`);

serve({
    fetch: app.fetch,
    port,
});
Enter fullscreen mode Exit fullscreen mode

Now we can install drizzle orm and initialize our database. First, install the required packages

npm i drizzle-orm postgres
npm i -D drizzle-kit
Enter fullscreen mode Exit fullscreen mode

Then we need to create a new db folder under the src folder, and add index.ts for initializing the db client, and schema.ts for the database schema.

src/db/schema.ts

import { pgTable, text, varchar } from "drizzle-orm/pg-core";

export const shortener = pgTable("shortener", {
    id: text("id").primaryKey(),
    link: varchar("link", { length: 255 }).notNull(),
    code: varchar("code", { length: 255 }).notNull().unique(),
});
Enter fullscreen mode Exit fullscreen mode

src/db/index.ts

import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";

const queryClient = postgres(
    "postgres://postgres:password@127.0.0.1:5432/url-shortener"
);
export const db = drizzle(queryClient, { schema });
Enter fullscreen mode Exit fullscreen mode

Then create a drizzle.config.ts file at the root folder.

drizzle.config.ts

// drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
    schema: "./src/db/schema.ts",
    out: "./drizzle",
    dialect: "postgresql",
    dbCredentials: {
        url: "postgres://postgres:password@127.0.0.1:5432/url-shortener",
    },
});
Enter fullscreen mode Exit fullscreen mode

Run npx drizzle-kit push to push the schema to the database.

npx drizzle-kit push
Enter fullscreen mode Exit fullscreen mode

After all the setup, we can finally work on the api, run npm run dev to start the server

npm run dev
Enter fullscreen mode Exit fullscreen mode

First make a random string generator. Create a new folder under src named utils, then create a index.ts file.

src/utils/index.ts

export function generateId(length: number) {
    let result = "";
    const characters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    const charactersLength = characters.length;
    for (let i = 0; i < length; i++) {
        result += characters.charAt(
            Math.floor(Math.random() * charactersLength)
        );
    }
    return result;
}
Enter fullscreen mode Exit fullscreen mode

Then update the create shortener route.

+ import { generateId } from "./utils";
+ import { shortener } from "./db/schema";
+ import { db } from "./db";

app.post("/api/shortener", async (c) => {
-       // create shortener route
+
+       const body = await c.req.json();
+
+       const link = body.link;
+
+       const code = generateId(6);
+
+       await db.insert(shortener).values({
+           id: generateId(8),
+           link,
+           code,
+       });
-       return c.text("Not yet implemented");
+       return c.json({ code });
});
Enter fullscreen mode Exit fullscreen mode

Then you can make a post request to the endpoint containing the link to generate a new shortener. Here's an example using Thunder Client in my VSCode:
thunder client example

Finally, update the redirect api.

+ import { eq } from "drizzle-orm";

app.get("/:code", async (c) => {
-       // redirect
+
+       const code = c.req.param("code");
+
+       const link = await db
+           .select()
+           .from(shortener)
+           .where(eq(shortener.code, code));
+
+       if (link.length === 0) {
+           return c.text("Invalid code");
+       }
-       return c.text("Not yet implemented");
+       return c.redirect(link[0].link);
});
Enter fullscreen mode Exit fullscreen mode

Then navigate to http://localhost:3000/{code} on your browser and you will be redirected to the original link.

That's it. URL shortener is a great way to start learning a new language, you get to use the language's popular backend framework and learn to communicate with database with the language.

There are so much more to explore, such as generating QR code for your shortener, recording redirects for analytics, custom domains and more. These are not covered in this tutorial.

Check out my github to see my works, all my projects are open source and free for anyone to learn and contribute.

I am also open for new ideas, although my current skill may not match the difficulty. Feel free to share them in the comments.

. .
Terabox Video Player