10 分钟内使用 Next.js 和 MongoDB 创建您自己的 URL 缩短器
动机
几周前,我正在开发一个 Twitter 机器人来发布我的热门文章,我发现有些文章的链接在推文中解析得不太好。不过,使用Rebrandly缩短了链接长度,效果很好。
所以,我决定为自己制作一个 URL 缩短器。
分解
我们需要一个
- 为每个长 URL 创建唯一哈希的服务
- 数据库保留长到短 URL 映射
- 将短链接重定向到其目的地的服务
与往常一样,Next.js是我构建完整服务的首选,而MongoDB则是存储链接的首选。
发展
现在我们已经弄清楚了步骤,让我们一步一步来
设置项目
让我们使用该npx create-next-app url-shortener
命令为我们的应用程序生成样板。
./.env.local
DB_NAME=url-shortner
ATLAS_URI_PROD=mongodb+srv://<user>:<password><cluster>.mongodb.net/url-shortner?retryWrites=true&w=majority
API_KEY=<a-long-random-string>
HOST=http://localhost:3000
这些环境变量也应该存储在您的 Vercel 项目中。
托管此项目时,应将的值
HOST
设置为您的域名。如果您没有公共域名,则只需使用NEXT_PUBLIC_VERCEL_URL
环境变量而不是HOST
。
设置 MongoDB
- 跑步
npm i --save mongodb
mongodb.ts
在 repo 的根目录创建一个文件。
// ./mongodb.ts
import { Db, MongoClient } from "mongodb";
import { formatLog } from "./utils";
// Create cached connection variable
let cachedDB: Db | null = null;
// A function for connecting to MongoDB,
export default async function connectToDatabase(): Promise<Db> {
// If the database connection is cached, use it instead of creating a new connection
if (cachedDB) {
console.info(formatLog("Using cached client!"));
return cachedDB;
}
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
console.info(formatLog("No client found! Creating a new one."));
// If no connection is cached, create a new one
const client = new MongoClient(process.env.ATLAS_URI_PROD as string, opts);
await client.connect();
const db: Db = client.db(process.env.DB_NAME);
cachedDB = db;
return cachedDB;
}
添加创建短链接服务
继续添加一个./api/create-link.ts
文件来为该服务创建 REST 端点。
我们需要注意以下几点
- 创建短 URL 需要唯一的哈希值。我以前
nanoid
会为长 URL 生成一个随机的短哈希值。 - 此端点只能通过 POST 方法访问。
- 我们应该设置 API-KEY 身份验证来保护端点。这可以通过生成一个长字符串并将其用作 API-KEY 标头来实现。
// ./api/create-link.ts
import { NextApiRequest, NextApiResponse } from "next";
import connectToDatabase from "../../mongodb";
import { customAlphabet } from "nanoid";
import { COLLECTION_NAMES } from "../../types";
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const getHash = customAlphabet(characters, 4);
export default async function CreateLink(
request: NextApiRequest,
response: NextApiResponse
) {
const apiKey = request.headers["api-key"] as string;
if (request.method !== "POST" || apiKey !== process.env.API_KEY) {
return response.status(405).json({
type: "Error",
code: 405,
message: "Only POST method is accepted on this route",
});
}
const { link } = request.body;
if (!link) {
response.status(400).send({
type: "Error",
code: 400,
message: "Expected {link: string}",
});
return;
}
try {
const database = await connectToDatabase();
const urlInfoCollection = database.collection(COLLECTION_NAMES["url-info"]);
const hash = getHash();
const linkExists = await urlInfoCollection.findOne({
link,
});
const shortUrl = `${process.env.HOST}/${hash}`;
if (!linkExists) {
await urlInfoCollection.insertOne({
link,
uid: hash,
shortUrl: shortUrl,
createdAt: new Date(),
});
}
response.status(201);
response.send({
type: "success",
code: 201,
data: {
shortUrl: linkExists?.shortUrl || shortUrl,
link,
},
});
} catch (e: any) {
response.status(500);
response.send({
code: 500,
type: "error",
message: e.message,
});
}
}
将短链接重定向到目标
现在我们可以创建短链接,让我们添加逻辑以将用户重定向到实际目的地。
为此,我们可以在 Next.js 应用程序中创建动态路由并在服务器端编写重定向逻辑。
// ./pages/[hash].tsx
import { NextApiRequest, NextApiResponse, NextPage } from "next";
import Head from "next/head";
import connectToDatabase from "../mongodb";
import { COLLECTION_NAMES } from "../types";
export async function getServerSideProps(request: NextApiRequest) {
const hash = request.query.hash as string;
const database = await connectToDatabase();
const campaign = await database
.collection(COLLECTION_NAMES["url-info"])
.findOne({ uid: hash });
if (campaign) {
return {
redirect: {
destination: campaign.link,
permanent: false,
},
};
}
return {
props: {},
};
}
const HashPage: NextPage = () => {
return (
<div>
<Head>
<title>URL Shortener</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1>Requested link not found</h1>
</div>
);
};
export default HashPage;
hash
如果数据库中有该值,则此页面将把用户重定向到目的地,否则将显示“未找到链接”消息。
托管
托管这个项目非常简单,因为 Next.js 与 Vercel 的集成非常出色。
简化的步骤列表:
- 将 Next.js 项目推送到 GitHub 存储库
- 前往https://vercel.app并使用您的 GitHub 帐户登录
url-shortener
通过单击 Vercel 仪表板上的“新建项目”按钮来导入存储库。
您还可以在此处阅读有关此内容的详细信息。
完成上述步骤后,前往项目设置并将我们在.env.local
文件中定义的环境变量添加到 Vercel 项目的环境变量中。
您还可以从设置中将自定义域连接到此项目。
🎉 太棒了!您的 URL 缩短器已准备就绪并托管。
下一步是什么?
好吧,您可以像我一样继续将此项目用作 REST API,或者您可以创建一个前端以使其成为 Web 应用程序。
在将其作为公共网络应用程序之前,请确保采取额外的安全措施。
您可以从此GitHub Repo克隆此项目。
本文不适用于生产,仅供学习之用。
上述方法可以进行许多优化,例如使用更好的数据库或正确索引以加快速度。
希望这篇文章对您有所帮助!如果您有任何反馈或疑问,欢迎在下方评论区留言。
欲了解更多此类内容,请在Twitter上关注我
直到下一次
