# Create a Tinyurl, short shareable URL without any shortner packages on your own.

*<mark>I wrote the entire post myself and then used AI to redirect so no doubt on the originality of the post</mark>*

# **Building Short, Shareable URLs in Your Application (Base62 + Drizzle ORM)**

When building modern applications, long URLs can look messy — especially when sharing them on social media or messaging apps. For example, your post URL might currently look like:

```javascript
http://localhost:3000/users/posts/?postid=2
```

or

```javascript
https://yourdomain.com/users/posts/?postid=2
```

This exposes unnecessary route structure like `/users/posts/`. Instead, wouldn’t it be much cleaner if you could share something like:

```javascript
https://yourdomain.com/f4sxdx
```

Just like GoFundMe, each post can have multiple short, unique URLs generated any time a user clicks **Share**.

In this guide, we’ll walk through how to build this entire system using:

* **Base62 encoding** for generating short codes
    
* **Drizzle ORM**
    
* **Next.js API Routes**
    
* **PostgreSQL**
    

---

# 🎯 What We Need to Achieve

Each short URL must:

1. **Uniquely identify a post or record**
    
2. **Redirect to the correct full/original URL**
    
3. **Allow multiple different short URLs for the same post**
    
4. **Store visit history for analytics**
    

We’ll use the popular library **base62-ts**:  
[https://www.npmjs.com/package/base62-ts](https://www.npmjs.com/package/base62-ts)

Base62 encoding converts the numeric ID from the database (1, 2, 3…) into a compact, shareable string like:  
`0 → 0`, `10 → A`, `61 → z`, `125 → 21`, etc.

---

# 🗄️ Step 1 — Create the Database Table

We’ll store each short link in its own row:

```javascript
import { serial, varchar, integer, timestamp, jsonb } from "drizzle-orm/pg-core";
import { pgTable } from "drizzle-orm/pg-core";
import { posts } from "./posts";

export const urls = pgTable("urls", {
  id: serial("id").primaryKey(),
  short_url: varchar("short_url"),
  redirect_url: varchar("redirect_url"),
  visited_history: jsonb("visited_history").$type<string[]>().default([]),
  post_id: integer("post_id")
    .notNull()
    .references(() => posts.id, { onDelete: "cascade" }),
  created_at: timestamp("created_at").defaultNow().notNull()
});
```

### Meaning of each column:

* **short\_url** → The Base62 + padded string (e.g., `/fundA12`)
    
* **redirect\_url** → The original full URL
    
* **visited\_history** → Array of timestamps (useful for analytics)
    
* **post\_id** → Foreign key to your posts table
    
* **id** → Auto-incremented number we will encode
    

---

# ⚙️ Step 2 — Generate a Short URL

When the user triggers **Share**, we hit a POST endpoint that:

1. Inserts a row containing the **full redirect URL**
    
2. Reads the inserted row’s ID
    
3. Encodes that ID using Base62
    
4. Pads the string to a fixed length (optional)
    
5. Updates the row with the generated `short_url`
    
6. Returns the new short URL
    

Ready-to-use API code:

```javascript
import { NextRequest, NextResponse } from "next/server";
import base62 from "base62-ts";
import { db } from "@/lib/drizzle-client";
import { urls } from "@/db/schema/urls";
import { eq } from "drizzle-orm";

export async function POST(req: NextRequest) {
  const reqPostID = await req.json();
  const { postID } = reqPostID;

  if (!postID) {
    return NextResponse.json(
      { error: "Post ID is required" },
      { status: 400 }
    );
  }

  // Construct the original redirect URL
  const redirect_url = `${process.env.BASE_URL}/donate/${postID}`;

  try {
    // Insert row and get the unique auto-incremented ID
    const [row] = await db
      .insert(urls)
      .values({ redirect_url, post_id: postID })
      .returning({ insertedId: urls.id });

    const insertedId = Number(row.insertedId);

    // Encode the ID using Base62
    let encoded62 = base62.encode(insertedId);
    encoded62 = encoded62.toString();

    // Ensure a fixed length (e.g., 6 chars)
    const encoded = encoded62.padStart(6, "fund");

    // Update with the short URL
    await db
      .update(urls)
      .set({ short_url: `/${encoded}` })
      .where(eq(urls.id, insertedId));

    return NextResponse.json({
      message: "Short URL created",
      shortURL: `/${encoded}`
    });
  } catch (error) {
    return NextResponse.json(
      { error: error instanceof Error ? error.message : "Unknown error" },
      { status: 500 }
    );
  }
}
```

### Why `padStart(6, "fund")`?

`padStart` ensures that even tiny Base62 values (like `"1"`) become 6-character strings:

```javascript
"1" → "fundf1"
"23" → "fund23"
"i8" → "fundi8"
```

This makes URLs consistent and harder to guess.

---

# 🔗 Step 3 — Redirect Short URL → Original URL

Your redirect route must:

1. Extract the short code from the URL
    
2. Look it up in the `urls` table
    
3. Log the visit time
    
4. Redirect the user to the real page
    

Example:

```javascript
interface ResponseData {
  id: number;
  short_url: string | null;
  redirect_url: string | null;
  visited_history: string[] | null;
  created_at: Date;
}

export async function GET(
  req: NextRequest,
  { params }: { params: Promise<{ shortURL: string }> }
) {
  const { shortURL } = await params;

  try {
    const result: ResponseData[] = await db
      .select()
      .from(urls)
      .where(eq(urls.short_url, `/${shortURL}`));

    if (result.length === 0) {
      return NextResponse.json({ message: "No URL found" }, { status: 404 });
    }

    const row = result[0];

    // Append visit timestamp
    const updatedHistory = [
      ...(row.visited_history || []),
      new Date().toISOString()
    ];

    await db
      .update(urls)
      .set({ visited_history: updatedHistory })
      .where(eq(urls.id, row.id));

    if (!row.redirect_url) {
      return NextResponse.json(
        { message: "Invalid redirect URL" },
        { status: 500 }
      );
    }

    return NextResponse.redirect(row.redirect_url);
  } catch (error) {
    return NextResponse.json(
      { message: "Unexpected error" },
      { status: 500 }
    );
  }
}
```

---

# 🧠 Summary of the Redirect Logic

1. Receive the short code from the URL
    
2. Query the database
    
3. Drizzle always returns an **array**, even for one record
    
4. If found → log the visit timestamp
    
5. Redirect the user
    

If the short URL doesn’t exist, simply return a 404.
