Automatically create shortlinks for your Astro site
1# astro-shortlinks
2
3Automatically create shortlinks for your [Astro](https://astro.build) site. This
4integration hooks into your build process to generate short URLs for your pages
5using various shortlink services.
6
7## Installation
8
9```bash
10npm install astro-shortlinks
11# or
12pnpm add astro-shortlinks
13# or
14yarn add astro-shortlinks
15```
16
17## Basic Usage
18
19Add the integration to your `astro.config.mjs`:
20
21```javascript
22import { defineConfig } from "astro/config";
23import shortlinks, { chhotoUrl } from "astro-shortlinks";
24
25export default defineConfig({
26 site: "https://example.com", // Required for shortlinks
27 integrations: [
28 shortlinks(
29 chhotoUrl({
30 domain: "https://short.example.com",
31 apiKey: process.env.CHHOTO_API_KEY,
32 }),
33 {
34 getPageMapping: (pages) =>
35 pages.map((page) => ({
36 longlink: page.toString(),
37 shortlink: page.pathname.replace(/^\//, ""),
38 })),
39 }
40 ),
41 ],
42});
43```
44
45## Built-in Shortlinkers
46
47### chhoto-url
48
49Creates shortlinks using a
50[chhoto-url](https://github.com/SinTan1729/chhoto-url) instance.
51
52```javascript
53import { chhotoUrl } from "astro-shortlinks";
54
55const shortlinker = chhotoUrl({
56 domain: "https://short.example.com",
57 apiKey: process.env.CHHOTO_API_KEY, // Optional
58 resetHits: false, // Reset hit counter when updating links (default: false)
59});
60```
61
62### worker-links
63
64Creates shortlinks using a [worker-links](https://github.com/erisa/worker-links)
65Cloudflare Worker.
66
67```javascript
68import { workerLinks } from "astro-shortlinks";
69
70const shortlinker = workerLinks({
71 domain: "https://worker-links.example.workers.dev",
72 secret: process.env.WORKER_LINKS_SECRET, // Required
73});
74```
75
76### multi-shortlinker
77
78Runs multiple shortlinkers in sequence, to create shortlinks on multiple
79services.
80
81```javascript
82import { multiShortlinker, chhotoUrl, workerLinks } from "astro-shortlinks";
83
84const shortlinker = multiShortlinker([
85 chhotoUrl({
86 domain: "https://short1.example.com",
87 apiKey: process.env.CHHOTO_API_KEY,
88 }),
89 workerLinks({
90 domain: "https://short2.example.workers.dev",
91 secret: process.env.WORKER_LINKS_SECRET,
92 }),
93 // Add more shortlinkers as needed
94]);
95```
96
97## Configuration
98
99The second argument for `shortlinks` provides general configuration for
100shortlinkers.
101
102### `getPageMapping`
103
104The `getPageMapping` function is required and determines how your page URLs are
105mapped to shortlinks:
106
107```javascript
108{
109 getPageMapping: (pages) => {
110 // pages is an array of URL objects
111 return pages.map((page) => ({
112 longlink: page.toString(), // Full URL of the page
113 shortlink: page.pathname.replace(/^\//, ""), // Path without leading slash
114 }));
115 };
116}
117```
118
119You can customize this to:
120
121- Use custom shortlink patterns
122- Filter out certain pages
123- Add custom prefixes or suffixes
124
125### Example: Custom Shortlink Mapping
126
127```javascript
128{
129 getPageMapping: (pages) => {
130 return pages
131 .filter((page) => !page.pathname.startsWith("/admin"))
132 .map((page) => ({
133 longlink: page.toString(),
134 shortlink: "blog/" + page.pathname.split("/").pop(),
135 }));
136 };
137}
138```
139
140## Creating a Custom Shortlinker
141
142You can create your own shortlinker by implementing the `Shortlinker` interface:
143
144```javascript
145import type { Shortlinker } from 'astro-shortlinks';
146
147interface MyShortlinkerOptions {
148 apiKey: string;
149 domain: string;
150}
151
152export const myShortlinker = ({ apiKey, domain }: MyShortlinkerOptions): Shortlinker => {
153 return {
154 name: 'my-shortlinker',
155 async run(mappings, logger) {
156 try {
157 // Your implementation here
158 for (const { longlink, shortlink } of mappings) {
159 const response = await fetch(`${domain}/api/create`, {
160 method: 'POST',
161 headers: {
162 'Authorization': `Bearer ${apiKey}`,
163 'Content-Type': 'application/json'
164 },
165 body: JSON.stringify({
166 short: shortlink,
167 long: longlink
168 })
169 });
170
171 if (!response.ok) {
172 logger.error(`Failed to create shortlink ${shortlink}: ${response.statusText}`);
173 return false;
174 }
175 }
176
177 logger.info(`Successfully created ${mappings.length} shortlinks`);
178 } catch (error) {
179 logger.error(`Error creating shortlinks: ${error}`);
180 return false;
181 }
182 }
183 };
184};
185```
186
187### Shortlinker Interface
188
189```typescript
190interface Shortlinker {
191 name: string;
192 run(
193 mappings: PageMapping[],
194 logger: AstroIntegrationLogger
195 ): Promise<void | boolean>;
196}
197
198type PageMapping = {
199 longlink: string; // Full URL to redirect to
200 shortlink: string; // Short URL path
201};
202```
203
204**Important:**
205
206- Return `false` from `run()` if the shortlinker fails and provides its own
207 logging
208- The logger is namespaced to your shortlinker for better debugging
209- Handle errors gracefully and provide meaningful log messages
210- Consider supporting both creating new links and updating existing ones
211
212## Requirements
213
214- Astro 5.0.0 or higher
215- The `site` configuration option must be set in your Astro config
216
217## Development
218
219A [Nix flake](./flake.nix) is provided to easily set up a development
220environment, otherwise read it to see the current required system dependencies
221for developing astro-shortlinks.
222
223```bash
224# Install dependencies
225pnpm install
226
227# Build the project
228pnpm build
229
230# Development mode with TypeScript watch
231pnpm dev
232```
233
234## License
235
236[MIT License](./LICENSE)