Recursos
Links curtos
Links curtos no seu próprio domínio são contados como visitas e podem ser importados do Umami, Dub, Bitly, Short.io, Rebrandly ou de um CSV.
O Runlight cria links curtos que redirecionam para onde você quiser. Cada clique é contado como uma visita (origem, país, dispositivo, navegador e quaisquer tags UTM do link curto) e fica separado dos seus números de visitantes e visualizações. Bots são redirecionados sem ser contados.
Links no domínio do seu app
De início, os links ficam em /go no seu app, como em example.com/go/sale. Sirva-os com mais uma rota, mostrada aqui para o Next.js.
app/go/[slug]/route.ts
import { rl } from "@/lib/runlight";
export const GET = rl.linkHandler();Mude o caminho com runlight({ linkPath: "/l" }). No Express, Connect, NestJS, Fastify e Koa, o middleware shortLinks mostrado em Domínios personalizados também serve /go. Nas outras linguagens, /go é servido junto com o resto das rotas do Runlight, como mostra a página de cada linguagem.
Criar links
Novo link no quadro Links recebe o destino e mantém o slug aleatório ou usa um que você digitar. Gerenciar lista todos os links para você buscar, ordenar por mais recentes, cliques ou nome, e editar, copiar ou excluir cada um. Os slugs são únicos entre todos os seus domínios, e milhares de links funcionam bem.
Clique num link para ver os cliques ao longo do tempo e de onde vieram.
Domínios personalizados
Os links também podem ficar num domínio próprio, como t.example.com/sale. Configure um em Configurações, Domínios personalizados.
- Aponte o domínio para o seu app com um registro CNAME, para um subdomínio, ou um registro A ou ALIAS, para um domínio sem subdomínio.
- Faça sua hospedagem aceitar o domínio e servir HTTPS para ele (na Vercel ou na Netlify, adicione-o aos domínios do projeto).
- Adicione o domínio em Configurações. O Runlight verifica se ele chega ao seu app e avisa.
Um domínio de links responde a todos os caminhos dele, então ele nunca pode ser o domínio do próprio app nem um dos seus sites. Ele também precisa ser um nome público, então nomes reservados para redes privadas, como .internal ou .local, são recusados. Passe origin para routes() com o endereço do seu app, e a primeira regra vale qualquer que seja o cabeçalho Host de uma requisição. Um hub conectado só pode adicionar domínios de links depois que origin estiver definido, já que ele não tem como saber em quais nomes seu app responde. Os caminhos dentro do painel, como /runlight, sempre chegam ao seu app, então você pode abrir o painel em qualquer um dos nomes dele para remover um domínio.
Remover um domínio mantém os links dele, junto com os cliques e as estatísticas. Eles passam para /go/<slug> até você adicionar o domínio de novo, e então voltam para ele.
As requisições nesse domínio precisam chegar ao Runlight antes que as rotas do seu app as vejam. rl.linkDomainResponse(request) responde a uma requisição em um dos seus domínios de links e retorna null para todo o resto, então o restante do seu app não é afetado. Chame-o primeiro no que roda antes das suas rotas. As outras linguagens têm a mesma verificação, muitas vezes embutida no middleware que serve o Runlight, e a página de cada linguagem mostra onde ela entra.
Next.js
O Next.js a roda em proxy.ts a partir do Next.js 16.
proxy.ts
import { rl } from "@/lib/runlight";
export async function proxy(request: Request) {
return (await rl.linkDomainResponse(request)) ?? undefined;
}No Next.js 15, chame o arquivo de middleware.ts e renomeie a função para middleware. A partir do Next.js 15.5, adicione também export const config = { runtime: "nodejs" }; para que o driver do seu banco de dados possa carregar. Antes do Next.js 15.5, o middleware só roda no runtime Edge, então ele precisa de um store que funcione lá, como o Turso por HTTP. Sem um, use links /go/<slug>, que o route handler já serve.
O SvelteKit faz o mesmo no hook handle de src/hooks.server.ts, com (await rl.linkDomainResponse(event.request)) ?? resolve(event), e o Astro em src/middleware.ts, com (await rl.linkDomainResponse(context.request)) ?? next().
Express, Connect e NestJS
shortLinks de @runlight/sdk/node responde aos domínios de links e a /go/<slug>, e repassa qualquer outra requisição sem ler o corpo. Coloque-o antes das suas rotas.
server.ts
import { shortLinks, toNodeHandler } from "@runlight/sdk/node";
app.use(shortLinks(rl));
app.use(toNodeHandler(rl.routes().handler));Um slug sem link no seu próprio domínio segue para o seu app, então a sua própria página 404 responde. Num servidor http simples, chame-o sem next, e ele resolve para true quando já respondeu.
ts
const links = shortLinks(rl);
createServer(async (req, res) => {
if (await links(req, res)) return;
// ...your app
});Fastify
Rode-o num hook onRequest e entregue a resposta assim que ele tiver respondido.
ts
const links = shortLinks(rl);
app.addHook("onRequest", async (req, reply) => {
if (await links(req.raw, reply.raw)) reply.hijack();
});Koa
ts
const links = shortLinks(rl);
app.use(async (ctx, next) => {
if (await links(ctx.req, ctx.res)) ctx.respond = false;
else await next();
});Hono
ts
app.use(async (c, next) => (await rl.linkDomainResponse(c.req.raw)) ?? next());Nuxt e h3
Um server middleware roda antes de cada rota. Ele monta a requisição só a partir do endereço e dos cabeçalhos, porque o toWebRequest do h3 começaria a ler o corpo de uma requisição destinada ao seu app.
server/middleware/links.ts
export default defineEventHandler(async (event) => {
const link = await rl.linkDomainResponse(new Request(getRequestURL(event), { method: event.method, headers: event.headers }));
if (link) return link;
});Bun, Deno e Cloudflare Workers
Verifique antes de qualquer outra coisa em fetch.
ts
async fetch(request) {
const link = await rl.linkDomainResponse(request);
if (link) return link;
// ...the routes and your app
}Um Worker precisa que o domínio de links seja roteado para ele, além de /runlight/*.
Importar links
Configurações, Importar, Links curtos traz os links de outro serviço, com o histórico de cliques quando o serviço o compartilha.
| De | O que vem junto |
|---|---|
| Umami | Todos os links e todos os cliques, com país, dispositivo, navegador e referência. Entre com uma chave de API, ou com seu usuário e senha no Umami auto-hospedado. |
| Dub | Todos os cliques nos planos Business; cliques por dia no Pro; só os links no Free. |
| Bitly | Cliques por dia, até onde o seu plano os guarda. |
| Short.io | Cliques por dia. O Short.io permite 60 requisições de estatísticas por minuto, então contas grandes levam um tempo. |
| Rebrandly | Só os links, porque a API dele não fornece cliques com data. |
| Qualquer CSV | Colunas name, url e, opcionalmente, slug e domain. |
Sua chave ou senha é enviada a cada etapa da importação e nunca é salva. As importações rodam poucos links por vez, então funcionam em hospedagens serverless, e é seguro rodá-las de novo, porque os links que já estão aqui são pulados. Os links no domínio do próprio serviço antigo (bit.ly, dub.sh, rebrand.ly, short.gy) mantêm o slug e passam para o seu próprio caminho de links; os links nos seus domínios personalizados mantêm o domínio, que é adicionado em Domínios personalizados.