svelte-short-qrcode
v0.0.4
Published
URL Shortener and QR Code generator for Svelte
Downloads
12
Maintainers
Readme
svelte-short-qrcode
A URL Shortener with QR Code support, for SvelteKit
Overview
Do you need an easy way for mobile users to quickly copy a link to a page on your site? While it's fairly easy to create a QR code for a URL, unless those URLs are quite short the QR code can easily end up fairly complex which means it needs to be bigger to be useful. By shortening URLs they can be made consistently small, which will result in simpler and more reliable QR Codes that are quicker and easier to recognize and scan. You then need somewhere to store the two-way mapping between the shortened URL and the original, plus protection against short URLs containing unfortunate bad-words, and ideally some caching system for performance.
If you need that, then this package is for you.
Features
- ✅ Create QR Codes for pages on your site
- ✅ URL shortening system for compact QR Codes (easier to scan)
- ✅ Easy to add to SvelteKit App via hooks.server or selectively to desired routes
- ✅ Avoids short codes containing unfortunate 'bad words'
- ✅ Self-contained - no external services to rely on, works on your domains
- ✅ QR codes created as crisp SVGs but downloadable as images, easy to size
- ✅ Server-generated SVGs reduces JS payload size
- ✅ In Memory storage for development, persistent storage via Redis or Firestore implementations
- ✅ LRU caching to avoid DB lookups and increase performance
- ✅ Customizable disctionary and short-code length
Usage
Installation
Add to your project using your package manager of choice (tip: pnpm
is excellent):
pnpm install svelte-short-qrcode
Create shortening parameters
The short codes used to redirect back to the original URLs are generated using the Nano ID package. The length and dictionary of characters to use for the short codes determines how many different URLs can be stored. Use this Nano ID Collision Calculator to calculate suitable parameters - if you have a limited number of static URLs, you only need a short code and may want to limit the characters to alphanumeric only for readability, if you have a dynamic site with thousands of URLs you'll want to have a longer size and maybe allow additional characters.
Create an instance of IDParameters
and return the match
method which can be used to limit route matches by the redirect route handler. The instance of the parameters object is also exported so it can be used by the URL shortening service later. This example will create short codes of 6 characters using the standard Nano ID characters.
src/params/nano.ts
import { IDParameters } from 'svelte-short-qrcode/parameters'
export const id_parameters = new IDParameters(6)
export const match = id_parameters.match
You may want to define the length using environment variables, and you can also override the characters to use. Note that this file is referenced on both the server and the client, so any environment variables used need to be public whether static or dynamic:
import { IDParameters } from 'svelte-short-qrcode/parameters'
import { PUBLIC_ID_SIZE, PUBLIC_ALPHABET } from '$env/static/public'
export const id_parameters = new IDParameters(parseInt(PUBLIC_ID_SIZE), PUBLIC_ALPHABET)
export const match = id_parameters.match
Examples of different environment settings:
PUBLIC_ID_SIZE="8"
# PUBLIC_ALPHABET="0123456789"
# PUBLIC_ALPHABET="abcdefghijklmnopqrstuvwxyz"
PUBLIC_ALPHABET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-"
# PUBLIC_ALPHABET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-$.+!*'(),"
Create shortening service
Now we have the parameters to use we can create an instance of our URL shortening service. This requires the parameters, a base URL which we'll get from an environment variable (required because you could use a different short domain name for the redirects), and a storage provider - for development and testing, an in-memory implementation is easiest.
src/lib/shortener.ts
import { URLShortener, InMemoryStorage } from 'svelte-short-qrcode'
import { REDIRECT_BASE } from '$env/static/private'
import { id_parameters } from './params/nano'
const base = new URL(REDIRECT_BASE)
const storage = new InMemoryStorage()
export const shortener = new URLShortener(storage, base, id_parameters)
For production use, it's important to use persistent storage. There are Firestore and Redis implementations provided - other databases can be supported by implementing a simple interface to store the 2-way mapping between short id codes and URLs.
Firestore Storage
Pass an instance of the Firestore service from firebase-admin/firestore
. To improve performance and reduce costs, Firestore will benefit from an in-memory LRU cache. The default collection name is urls
which can be overridden in the constructor.
import { CacheStorage, FirestoreStorage } from 'svelte-short-qrcode'
import { firestore } from './firebase.server'
const storage = new CacheStorage(new FirestoreStorage(firestore))
Redis Storage
Pass a connected instance of the node Redis client to the static .create
method - this is used to create a server-side Redis custom function that the provider requires. You can also override the default prefix (urls:
), module name (URLShortener
) and function name (put
) in by passing additional parameters.
import { RedisStorage } from 'svelte-short-qrcode'
import { createClient } from 'redis'
const redis = createClient()
await redis.connect()
const storage = await RedisStorage.create(redis)
Create a Redirect Handler
Any URL that the URL shortening service creates (and what the QR Code will take people to) will need to redirect back to the original URL that matches the short code. To do this, we create a handler to get the matching URL and redirect to it. You can chose the type or redirect response you want to save, and might decide to render a message page or ad first, but a simple redirect would look like this.
NOTE: the [id=nano]
segment means it will only match routes that match the nano
param matcher we created earlier.
src/routes/[id=nano]/+server.ts
import { error, redirect } from '@sveltejs/kit'
import { shortener } from '$lib/shortener'
export async function GET({ locals, params, setHeaders }) {
const { id } = params
const url = await shortener.get(id)
if (!url) {
error(404, 'not found')
}
setHeaders({ 'cache-control': 'public, max-age=3600' })
redirect(301, url)
}
Generate and Display QR Codes
Finally, we get to generate and display a QR Code! Depending on whether you want to do this for specific routes, groups of routes, or the entire site, you can chose to generate QR codes in an appropriate +page.server.ts
or +layout.server.ts
load function.
src/routes/+layout.server.ts
import { shortener } from '$lib/shortener'
export async function load({ cookies, url }) {
const qr = await shortener.generate(url)
return { qr }
}
The qr
property can be called whatever you want - this contains the SVG string that can be rendered on the page and styled however you want.
src/routes/+layout.svelte
<script lang="ts">
export let data
</script>
<img src={data.qr} alt="" />
NOTE: you may want to normalize the URL prior to generating the QR Code if you don't care about having separate redirect URLs for certain search params - any difference in the url
parameter passed will result in a new short code being created which may be unnecessary.
TODO
Ideas for enhancements
- Default to running on the current domain (to save having to specify a base URL)
- MySQL or RDBMS ORM storage implementation
- Normalize URLs (https://github.com/sindresorhus/normalize-url)
- Shared / Single mode (whether full domain is stored, or just root-relative)
- Store root-relative URL in database, allow changing domain (for shared, store account ID)
- URL prefix / route to shortened redirect URL (if different to site?)
- Querystring parameters whitelist / blacklist (e.g. product page filtering or sort options, utm params)
- Auto increase nanoid length if too many collisions
- Track clicks, creation stats, etc..
- Messaging / ads on redirect pages (rather than http server redirects)
- Setup vanity redirects in advance
- Support pre-rendered static sites (generate IDs / QRCodes when pre-rendering?)
- Stats of QR Code count / clicks, cache hits etc...