SmallPlate

Examples in JavaScript

JavaScript and TypeScript examples using the Plate-Link resolve endpoint

These examples resolve links with:

GET /{plateId}/resolve/{id}

Then redirect from your own app/server.

Node.js (Express)

import express from "express";

const app = express();
const LINK_BASE = process.env.PLATE_LINK_BASE || "https://link.example.com";
const PLATE_ID = process.env.PLATE_ID || "1";

app.get("/u/:id/*tail", async (req, res) => {
  const id = req.params.id;
  const rawTail = req.params.tail || "";
  const tail = Array.isArray(rawTail) ? rawTail.join("/") : rawTail;
  const query = new URLSearchParams(req.query).toString();

  const resolveUrl = `${LINK_BASE}/${PLATE_ID}/resolve/${id}${tail ? `/${tail}` : ""}${query ? `?${query}` : ""}`;
  const resolved = await fetch(resolveUrl, { headers: { Accept: "application/json" } });
  if (!resolved.ok) {
    return res.status(resolved.status).json({ error: "link_not_found" });
  }

  const body = await resolved.json();
  const destination = body?.data?.destination;
  if (!destination) {
    return res.status(502).json({ error: "invalid_resolve_payload" });
  }

  return res.redirect(307, destination);
});

Next.js Route Handler

import { NextRequest, NextResponse } from "next/server";

const LINK_BASE = process.env.PLATE_LINK_BASE || "https://link.example.com";
const PLATE_ID = process.env.PLATE_ID || "1";

export async function GET(req: NextRequest, ctx: { params: { parts: string[] } }) {
  const parts = ctx.params.parts || [];
  const [id, ...tail] = parts;
  if (!id) return NextResponse.json({ error: "missing_id" }, { status: 400 });

  const query = req.nextUrl.searchParams.toString();
  const resolvePath = `/${PLATE_ID}/resolve/${id}${tail.length ? `/${tail.join("/")}` : ""}${query ? `?${query}` : ""}`;
  const resolved = await fetch(`${LINK_BASE}${resolvePath}`, {
    headers: { Accept: "application/json" },
    cache: "no-store",
  });

  if (!resolved.ok) {
    return NextResponse.json({ error: "link_not_found" }, { status: resolved.status });
  }

  const body = await resolved.json();
  return NextResponse.redirect(body.data.destination, { status: 307 });
}

Cloudflare Workers

const LINK_BASE = "https://link.example.com";
const PLATE_ID = "1";

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const parts = url.pathname.replace(/^\/+/, "").split("/");
    if (parts[0] !== "u" || !parts[1]) {
      return new Response("Not found", { status: 404 });
    }

    const id = parts[1];
    const tail = parts.slice(2).join("/");
    const resolveUrl = `${LINK_BASE}/${PLATE_ID}/resolve/${id}${tail ? `/${tail}` : ""}${url.search}`;
    const resolved = await fetch(resolveUrl, { headers: { Accept: "application/json" } });

    if (!resolved.ok) return new Response("Link not found", { status: resolved.status });
    const body = await resolved.json();
    return Response.redirect(body.data.destination, 307);
  },
};

Nitro (H3)

import { defineEventHandler, getRequestURL, sendRedirect } from "h3";

const LINK_BASE = process.env.PLATE_LINK_BASE || "https://link.example.com";
const PLATE_ID = process.env.PLATE_ID || "1";

export default defineEventHandler(async (event) => {
  const url = getRequestURL(event);
  if (!url.pathname.startsWith("/u/")) return;

  const parts = url.pathname.split("/").filter(Boolean);
  const id = parts[1];
  const tail = parts.slice(2).join("/");
  if (!id) return;

  const resolveUrl = `${LINK_BASE}/${PLATE_ID}/resolve/${id}${tail ? `/${tail}` : ""}${url.search}`;
  const resolved = await fetch(resolveUrl, { headers: { Accept: "application/json" } });
  if (!resolved.ok) return new Response("Link not found", { status: resolved.status });

  const body = await resolved.json();
  return sendRedirect(event, body.data.destination, 307);
});

On this page