Téma

OG obrázky v SvelteKitu

Generujte OG obrázky ve SvelteKitu pomocí FastOG API — s předplatným Dynamic API (vytvořte si API klíč v dashboardu), na základě předplatného, zabezpečené přes HMAC. Tento průvodce vám ukáže, jak vytvořit nástroj pro podepisování (signer) pro HMAC-podepsané URL adresy OG obrázků a dynamicky renderovat Open Graph obrázky za běhu. Před integrací si můžete náhled vyzkoušet pomocí Free OG Image Testeru.

Rychlý start

1. Vytvořte nástroj pro podepisování (signer utility)

javascript
// src/lib/og-image-signer.js
import crypto from "node:crypto";

export function generateSignedOgUrl(params, options = {}) {
    const {
        baseUrl = "https://fastog.com/api/v1/og",
        apiKey = process.env.FASTOG_API_KEY,
        hmacSecret = process.env.FASTOG_HMAC_SECRET,
    } = options;

    if (!apiKey || !hmacSecret) {
        throw new Error("Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET");
    }

    // RFC-3986 percent-encoding: keep A-Za-z0-9-._~
    const enc = (s) =>
        encodeURIComponent(s).replace(
            /[!'()*]/g,
            (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
        );

    // Every query param except the signature, INCLUDING key
    const all = { ...params, key: apiKey };

    // Canonical query: sorted enc(key)=enc(value) pairs joined with &
    const pairs = [];
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? v : [v]) {
            pairs.push(`${enc(k)}=${enc(vv)}`);
        }
    }
    pairs.sort();
    const canonical = pairs.join("&");

    // Sign the exact bytes the server will verify
    const signature = crypto
        .createHmac("sha256", hmacSecret)
        .update(`GET/api/v1/og\n${canonical}`)
        .digest("hex");

    // Build the URL from the same pairs so bytes match the canonical form
    const url = new URL(baseUrl);
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? v : [v]) {
            url.searchParams.append(Array.isArray(v) ? `${k}[]` : k, vv);
        }
    }
    url.searchParams.set("s", signature);

    return url.toString();
}

2. Proměnné prostředí

bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here

Stránka s metadaty

svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
    export let data;

    $: ({ post, ogImageUrl } = data);
</script>

<svelte:head>
    <title>{post.title}</title>
    <meta property="og:image" content={ogImageUrl} />
    <meta property="og:title" content={post.title} />
    <meta property="og:description" content={post.excerpt} />
</svelte:head>

<article>
    <h1>{post.title}</h1>
    <p>{post.excerpt}</p>
</article>
javascript
// src/routes/blog/[slug]/+page.server.js
import { generateSignedOgUrl } from "$lib/og-image-signer";

export async function load({ params }) {
    const post = await getPost(params.slug);

    const ogImageUrl = generateSignedOgUrl({
        template: "blog",
        title: post.title,
        subtitle: post.excerpt,
        author: post.author.name,
    });

    return {
        post,
        ogImageUrl,
    };
}

API trasa

javascript
// src/routes/api/og/+server.js
import { json } from "@sveltejs/kit";
import { generateSignedOgUrl } from "$lib/og-image-signer";

export async function GET({ url }) {
    const title = url.searchParams.get("title");
    const template = url.searchParams.get("template") || "default";

    if (!title) {
        return json({ error: "Missing title" }, { status: 400 });
    }

    const ogImageUrl = generateSignedOgUrl({
        template,
        title,
    });

    return json({ ogImageUrl });
}

Příklad e-commerce

javascript
// src/routes/products/[id]/+page.server.js
import { generateSignedOgUrl } from "$lib/og-image-signer";

export async function load({ params }) {
    const product = await getProduct(params.id);

    const ogImageUrl = generateSignedOgUrl({
        template: "ecommerce",
        title: product.name,
        price: product.price,
        image: product.image,
    });

    return { product, ogImageUrl };
}
svelte
<!-- src/routes/products/[id]/+page.svelte -->
<script>
    export let data;
    $: ({ product, ogImageUrl } = data);
</script>

<svelte:head>
    <meta property="og:image" content={ogImageUrl} />
    <meta property="og:title" content={product.name} />
</svelte:head>

<main>
    <h1>{product.name}</h1>
    <p>{product.price}</p>
</main>

Uživatelský profil (šablona blogu)

FastOG nemá profile šablonu — pro kartu ve stylu profilu použij blog, přičemž jméno uživatele předáš jako title, biografii jako subtitle a jméno jako author.

javascript
// src/routes/users/[username]/+page.server.js
import { generateSignedOgUrl } from "$lib/og-image-signer";

export async function load({ params }) {
    const user = await getUser(params.username);

    const ogImageUrl = generateSignedOgUrl({
        template: "blog",
        title: user.name,
        subtitle: user.bio,
        author: user.name,
    });

    return { user, ogImageUrl };
}

Komponenta s dynamickým OG obrázkem

svelte
<!-- src/lib/ShareButtons.svelte -->
<script>
    import { onMount } from "svelte";

    export let post;
    let ogImageUrl = null;

    onMount(async () => {
        const res = await fetch(`/api/og?title=${encodeURIComponent(post.title)}`);
        const data = await res.json();
        ogImageUrl = data.ogImageUrl;
    });
</script>

<div class="share-buttons">
    <button on:click={() => shareToTwitter(ogImageUrl)}>
        Share on Twitter
    </button>
</div>

Časté chyby

Podepisování na straně klienta

Nikdy nepodepisujte OG URL v kódu prohlížeče — došlo by k odhalení HMAC tajemství.

❌ Špatně:

svelte
<script>
    import { generateSignedOgUrl } from "$lib/og-image-signer";
    // This runs in the browser!
    const ogUrl = generateSignedOgUrl({ title: post.title });
</script>

✅ Správně:

javascript
// +page.server.js (runs on server only)
import { generateSignedOgUrl } from "$lib/og-image-signer";
export async function load({ params }) {
    const ogImageUrl = generateSignedOgUrl({ title: post.title });
    return { ogImageUrl };
}

Podepisování jiných bajtů, než které odesíláte

Podpis pokrývá GET/api/v1/og plus nový řádek plus kanonický dotaz — seřazené enc(key)=enc(value) dvojice všech parametrů kromě s, včetně key. Sestavte výslednou URL ze stejných zakódovaných dvojic, jinak server vrátí 401 invalid_signature.

Úplný referenční přehled API

Vyzkoušejte zdarma – bez registrace

Náhled a testování dynamických OG obrázků během pár sekund.

Otevřít bezplatný tester OG obrázků

A BeCaSimulations site