Subiect

Imagini OG în Vanilla Node.js (Fără Framework)

Generează imagini OG fără un framework folosind API-ul Dinamic bazat pe abonament al FastOG — cu un abonament la API-ul Dinamic (creează-ți cheia API în dashboard). Sari peste boilerplate, previzualizează în Free OG Image Tester și construiește imagini Open Graph dinamice cu Node.js simplu și URL-uri semnate HMAC.

Modulul Signer

javascript
// og-signer.js
const crypto = require("crypto");
const { URL } = require("url");

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;

    // 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();
}

module.exports = { generateSignedOgUrl };

Utilizare

javascript
// index.js
const { generateSignedOgUrl } = require("./og-signer");

const ogUrl = generateSignedOgUrl({
    template: "blog",
    title: "My Blog Post",
});

console.log("OG Image URL:", ogUrl);

Randează-l: pune ogUrl într-o etichetă <meta property="og:image" content="..."> în HTML-ul tău <head>, sau servește-l de la un endpoint /api/og (vezi mai jos).

Cu serverul HTTP

javascript
// server.js
const http = require("http");
const { generateSignedOgUrl } = require("./og-signer");

const server = http.createServer((req, res) => {
    if (req.url === "/api/og") {
        const url = new URL(req.url, `http://${req.headers.host}`);
        const title = url.searchParams.get("title");

        if (!title) {
            res.writeHead(400, { "Content-Type": "application/json" });
            return res.end(JSON.stringify({ error: "Missing title" }));
        }

        const ogUrl = generateSignedOgUrl({
            template: "default",
            title,
        });

        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ ogImageUrl: ogUrl }));
        return;
    }

    res.writeHead(404);
    res.end("Not Found");
});

server.listen(3000, () => {
    console.log("Server running on http://localhost:3000");
});

Versiunea cu Module ES

javascript
// og-signer.mjs
import crypto from "crypto";
import { URL } from "url";

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;

    // 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();
}
javascript
// index.mjs
import { generateSignedOgUrl } from "./og-signer.mjs";

const ogUrl = generateSignedOgUrl({
    template: "blog",
    title: "Hello World",
});

Variabile de mediu

bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here

Referință completă API

Încearcă gratuit — nu este necesară înregistrarea

Previzualizează și testează imagini OG dinamice în câteva secunde.

Deschide Testerul gratuit de imagini OG

A BeCaSimulations site