Open Graph link preview image according to the document to open
- Dominant language
- TypeScript
- Stars
- 213
- Forks
- 143
- Avg merge
- 13h 57m
- Merged PRs (30d)
- 11
Description
# OpenGraph Studio issue
### Reason/Context
Thanks to the `?url=` and `?base64=` query param, Studio can load most of files (yes, not all of them, see [https://github.com/asyncapi/studio/issues/127](https://github.com/asyncapi/studio/issues/127)). I expect users will use that to share their AsyncAPI docs.
Whenever a link to Studio (with or without those query params) is pasted into social media (Twitter, Linkedin, Facebook, Slack...), the preview image is this one:

It is a great pic, however it says nothing about the file being shared.
What if we could dynamically generate the preview image based on the file being shared? For example, the title, description and some stats could be shown.
I created a POC based on [https://github.com/vercel/og-image](https://github.com/vercel/og-image) (deprecated atm), available in [my fork](https://github.com/smoya/og-image) (It's just a POC) which is a server that generates dynamic images for being used on [Open Graph](https://ogp.me/) image meta tags. This works by generating a dynamic HTML, making an screenshot of it through headless Chromium, and serving the resulting image.
The server accepts a `?base64=` query param, and generates an image that contains the AsyncAPI doc Title, Description, number of servers, channels and messages.
Despite the horrible design, the service is able to generate the following:
Based on the following AsyncAPI doc:
See
```yaml
asyncapi: '2.2.0'
info:
title: Account Service
version: 1.0.0
description: This service is in charge of processing user signups
channels:
user/signedup:
subscribe:
message:
$ref: '#/components/messages/UserSignedUp'
components:
messages:
UserSignedUp:
payload:
type: object
properties:
displayName:
type: string
description: Name of the user
email:
type: string
format: email
description: Email of the user
```
[Open in Studio](https://studio.asyncapi.com/?base64=YXN5bmNhcGk6ICcyLjIuMCcKaW5mbzoKICB0aXRsZTogQWNjb3VudCBTZXJ2aWNlCiAgdmVyc2lvbjogMS4wLjAKICBkZXNjcmlwdGlvbjogVGhpcyBzZXJ2aWNlIGlzIGluIGNoYXJnZSBvZiBwcm9jZXNzaW5nIHVzZXIgc2lnbnVwcwpjaGFubmVsczoKICB1c2VyL3NpZ25lZHVwOgogICAgc3Vic2NyaWJlOgogICAgICBtZXNzYWdlOgogICAgICAgICRyZWY6ICcjL2NvbXBvbmVudHMvbWVzc2FnZXMvVXNlclNpZ25lZFVwJwpjb21wb25lbnRzOgogIG1lc3NhZ2VzOgogICAgVXNlclNpZ25lZFVwOgogICAgICBwYXlsb2FkOgogICAgICAgIHR5cGU6IG9iamVjdAogICAgICAgIHByb3BlcnRpZXM6CiAgICAgICAgICBkaXNwbGF5TmFtZToKICAgICAgICAgICAgdHlwZTogc3RyaW5nCiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiBOYW1lIG9mIHRoZSB1c2VyCiAgICAgICAgICBlbWFpbDoKICAgICAgICAgICAgdHlwZTogc3RyaW5nCiAgICAgICAgICAgIGZvcm1hdDogZW1haWwKICAgICAgICAgICAgZGVzY3JpcHRpb246IEVtYWlsIG9mIHRoZSB1c2Vy)
Studio will need to modify the `og:image` tag so it points to this new service.
```html
```
The preview image would then look like (note that [https://shaggy-stingray-56.loca.lt/](https://shaggy-stingray-56.loca.lt/) was a local tunnel to my localhost serving a simple html with the `og-image` tag):

By the way, all of this could run on serverless functions such as the Netlify functions (which are AWS Lambda) available in free tier :)
### Description
Here is a sequence diagram showing the big picture of the flow a request made by an Open Graph crawler (crawlers used for querying the open graph image whenever you share a link) will follow:
```mermaid
sequenceDiagram
Open Graph Crawler->>+Studio: /?base64=
Studio->>Studio: Set og:title, and og:description metatags. Set og:image to /generate.png?title=foo&description=bar&operations=4&servers=2
Studio->>-Open Graph Crawler: Pre-rendered Studio HTML webpage
Open Graph Crawler->>+OpenGraph Generator: /generate.png?title=foo&description=bar&operations=4&servers=2
OpenGraph Generator->>-Open Graph Crawler: og-image.png
```
Note that, as explained in [this comment](https://github.com/asyncapi/studio/issues/224#issuecomment-1009208353), we would need to configure pre-rendering in Netlify for doing the og:image content URL replacement on each request made by a crawler.
Alternatively, whatever technology we use (for example NextJS), the flow for rendering the Studio page would be something like the following:
```mermaid
flowchart TD
A[User] --> B(https://studio.asyncapi.com)
B --> C{contains ?base64 or ?url}
C -->|No| D[Static rendering]
C -->|Yes| E[Dynamic rendering]
E --> F(Parsing AsyncAPI doc + etc)
```
In case the image can't be generated due to whatever reason, the default AsyncAPI Studio should be served instead: https://studio.asyncapi.com/img/meta-studio-og-image.jpeg
## What you will need to do
Note that the design of the Open Graph image card is also part of this task. Ask @Mayaleeeee for help on this (Thanks! 🙌 ).
### Prerequisites
1. Fork [Studio](https://github.com/asyncapi/studio).
2. Deploy it to your own [Netlify](https://netlify.com/) free account. I recommend you to do it via Netlify’s website UI and not via Netlify CLI. With few clicks your site will be configured to be deployed on each push to the branch you specify.
3. Enable [Prerendering](https://docs.netlify.com/site-deploys/post-processing/prerendering/#set-up-prerendering) in your new Netlify site. This will allow web crawlers (such as the ones used for fetching the OpenGraph meta tags) receive a fully rendered version of the website, including content loaded by Javascript.
### Work to do
1. Create a new Github repository where your Open Graph image generator service will be tracked.
2. Create then a new service that exposes an HTTP API that generates an Open Graph image based on few query params (use the names you want, the following are just suggestions):
1. doc_url: a URL pointing to a raw AsyncAPI document.
2. doc_base64: an AsyncAPI document encoded in base64.
Some hints:
- You will need to use the AsyncAPI [Parser-JS](https://github.com/asyncapi/parser-js) to parse your document and extract the data you need from it
- In order to generate the image, you can use [@vercel/og](https://www.npmjs.com/package/@vercel/og) package. ([og-image](https://github.com/vercel/og-image) is deprecated now). Documentation on how to use it is available [here](https://vercel.com/docs/functions/og-image-generation). Alternatively, if that package is not compatible in a non-vercel world, you might want to take a look to https://github.com/vercel/satori, which is what that package uses under the hood.
3. Deploy this new service somewhere. I recommend you to deploy it via [Netlify Functions](https://www.netlify.com/platform/core/functions/). Or even better if we can get it as a [Netlify Edge Function](https://docs.netlify.com/edge-functions/overview/) (support of npm packages is still experimental) since I believe we will be able to implement a caching mechanism easily.
4. Once we have a public URL of that service, include a Javascript code somewhere in the Studio website that modifies the `og:image` meta tag content to point to the new service URL including the `doc_url` or `doc_base64` query param with the right content. That will be the trick that will make the OpenGraph image shown dynamically based on such parameters.
5. Performance is a must. Both serving the Open Graph tags + generating the image should not take more than few secs (~3), otherwise crawlers will timeout (for example, Slack's crawler timeouts at 5 secs)
6. Investigate about caching. If hosted as a Netlify function, I believe we could just trust in cached responses. See https://docs.netlify.com/platform/caching/#supported-cache-control-headers. Otherwise, we could give a try to [Netlify Blobs](https://docs.netlify.com/blobs/overview/) and store each generated image using the base64 hash (or a reproducible and atomic hash) so every new request first check if that image is already generated and in the case it is, serve the blob directly (not 100% if this use case can be supported, but I guess it is).
Anyway, more investigation on how to implement the service should be taken, so please do not take my words here as the right way to do it as I didn’t spent time on it when I created this issue.
## GSoC 2024
This issue got accepted as part of the GSoC 2024. @helios2003 is assigned as mentee.
We are using the following read-only Project board to track the current status of it's work: https://github.com/orgs/asyncapi/projects/49/views/1
Contributor guide
Research direction
Start with the Studio Open Graph metadata handling and Netlify prerendering configuration described in the issue, then review AsyncAPI Parser-JS and @vercel/og for the separate image-generation service. Done means dynamic previews work for document URL and base64 inputs, fall back to the default image on failure, and meet the stated response-time and caching requirements.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- backend, cloud, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 20/100