Creating custom embeds with an oEmbed API using Cloudflare Workers

If you have a SaaS product that you want to embed into other platforms like Notion, you might find yourself struggling with the limitations of existing embed options. Fortunately, you can create your own custom embeds by creating an oEmbed API using Cloudflare Workers. In this post, we'll walk you through the steps of creating a serverless oEmbed API using Cloudflare Workers, and how we used it to embed our product into Notion.

1. What is oEmbed?

Before we dive into creating our oEmbed API, let's first define what oEmbed is. oEmbed is a format for allowing an embedded representation of a URL on third-party sites. The simple API allows a website to display embedded content (such as photos or videos) when a user posts a link to that resource, without having to parse the resource directly. You can learn more about the oEmbed specification by visiting the official oEmbed website at oembed.com

2. Setting up Cloudflare Workers

To set up our oEmbed API, we'll be using Cloudflare Workers. Cloudflare Workers is a serverless platform that allows you to run JavaScript code on the edge, meaning the code is executed as close to the end user as possible. To get started with Cloudflare Workers, you'll need to create a Cloudflare account and follow these steps to set up your worker.

3: Creating the oEmbed API

To utilize oEmbed, it is necessary to include a link tag in the <head> section of your HTML that indicates the URL of your oEmbed endpoint. Below is an example of how the tag may appear:

<link
  rel="alternate"
  type="application/json+oembed"
  title="Jam | Cool!"
  href="https://jam.dev/oembed?url=${encodedURL}"
/>

Now that we have our Cloudflare Worker set up and link tag included, it's time to create our oEmbed API. The first thing we'll need to do is define our route. We'll use the route /oembed to indicate that any URL that starts with /oembed will be handled by our API.

import { Router, IHTTPMethods } from "itty-router";

const router = Router<Request, IHTTPMethods>();

router.get("/oembed", oEmbedRoute);

We'll then create a function that takes in the URL and returns the oEmbed data. We'll use the request API to retrieve necessary info and format it into oEmbed response.

import { IttyRequest } from "../utils/request";
import { AttachmentType } from "./sharePage";

export default async function oEmbedRoute(
  request: IttyRequest,
): Promise<Response | undefined> {
  const { searchParams } = new URL(request.url);
  const encodedThreadUrl = searchParams.get("url");

  if (!encodedThreadUrl) {
    return new Response("Resource not found", { status: 404 });
  }

  const threadUrl = decodeURIComponent(encodedThreadUrl);
  const jamId = threadUrl.split("/").pop();

  const html = `
    <iframe 
      src="https://jam.dev/mini/${jamId}" 
      width="640" 
      height="360" 
      frameborder="0"
    ></iframe>
  `;

  const data = {
    width: 640,
    height: 360,
    html,
    provider_name: "Jam",
    provider_url: `https://jam.dev/`,
    title: "Jam | The fastest bug reporting on Earth",
    type: "rich",
    version: "1.0",
  };

  const json = JSON.stringify(data, null, 2);

  return new Response(json, {
    headers: {
      "content-type": "application/json;charset=UTF-8",
    },
  });
}

Here is an example of an oEmbed response generated by our serverless API:

curl -H "user-agent: iframely" https://jam.dev/c/e4629f45-0acb-4798-8517-928173e72c3b
<link
  rel="alternate"
  type="application/json+oembed"
  title="Jam | Cool!"
  href="https://jam.dev/oembed?url=https%253A%252F%252Fjam.dev%252Fc%252Fe4629f45-0acb-4798-8517-928173e72c3b&type=screenshot"
/>
{
  "width": 640,
  "height": 360,
  "html": "<iframe src=\"https://jam.dev/mini/e4629f45-0acb-4798-8517-928173e72c3b\" width=\"640\" height=\"360\" frameborder=\"0\"></iframe>",
  "provider_name": "Jam",
  "provider_url": "https://jam.dev/",
  "title": "Jam | The fastest bug reporting on Earth",
  "type": "rich",
  "version": "1.0"
}

4. Embedding into Notion

Now that we have our oEmbed API set up, we can embed our SaaS product into Notion. However, before we can do that, there's one extra step we need to take. Notion uses Iframely to fetch metadata for embeds, so we had to reach out to Iframely and ask them to whitelist our domain. Once they whitelisted our domain, we were able to see results in Notion.

Real world example

To embed our product into Notion, we'll need to get the URL of the page we want to embed, and then create an embed block in Notion using that URL. Notion will automatically recognize that the URL is an oEmbed compatible URL, and will display the embedded content like this one below.

Conclusion:

Creating a serverless oEmbed API using Cloudflare Workers is a great way to create custom embeds for your SaaS product. By following the steps outlined in this post, you can create an oEmbed API that allows you to embed your product into any platform that supports oEmbed. Just remember to reach out to Iframely if you plan to embed into Notion, and ask them to whitelist your domain.

Dealing with bugs is 💩, but not with Jam.

Capture bugs fast, in a format that thousands of developers love.
Get Jam for free