blog.petitviolet.net

Build oEmbed API for Gatsby blog

2020-05-22

GatsbyGo

Previously, I wrote a post about that this blog has a capability to expand oEmbed articles using raae/gatsby-remark-oembed.

oEmbed expansion in Gatsby oEmbed expansion in Gatsby oEmbed 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…

As it is embed here, this blog is also able to expand self posts as well based on oEmbed specification.
Though the style is not sophisticated for the time being.
This post describes how to build an API to embed blog posts in oEmbed format that is powered by Gatsby.

Also see: https://oembed.com/

tl;dr

Source code is here: https://gist.github.com/petitviolet/a2a601f06ae20f98d1306e8690d358bd

How it works

To offer an API to return response following the oEmbed specification, at first, it needs to collect metadata of requested page. Which page is requested is given by the url URL parameter such as /oembed?url=https://blog.petitviolet.net/post/2020-03-04/hello-world. And metadata of each post is available at ./public/page-data/path/to/post/page-data.json that Gatsby generates in its build process. Go code to retrieve the page-data.json for the specified blog post is below.

var urlPattern = regexp.MustCompile(`^https://blog.petitviolet.net/post/(\d{4}-\d{2}-\d{2})/(.+)$`)

func findPost(url string) (*Post, error) {
	res := urlPattern.FindStringSubmatch(url)
	if len(res) != 3 {
		return nil, fmt.Errorf("Invalid url. url: %s", url)
	}
	pageDataPath := fmt.Sprintf("public/page-data/post/%s/%s/page-data.json", res[1], res[2])
	raw, err := ioutil.ReadFile(pageDataPath)
	if err != nil {
		err = fmt.Errorf("Failed to read file. file = %s, err = %s", pageDataPath, err.Error())
		return nil, err
	}
    // ...
}

Next, build a Post struct as a source of HTTP response from found page-data.json. It uses mattn/go-jsonpointer to get data from json object.

func buildPost(raw []byte) *Post {
	var pageData map[string]interface{}
	json.Unmarshal(raw, &pageData)

	title, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/title")
	description, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/description")
	excerpt, _ := jsonpointer.Get(pageData, "/result/data/post/excerpt")
	date, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/date")
	_tags, _ := jsonpointer.Get(pageData, "/result/data/post/frontmatter/tags")
	tagIs := _tags.([]interface{})
	tagStrs := make([]string, len(tagIs))
	for i, v := range tagIs {
		tagStrs[i] = v.(string)
	}
	post := Post{
		Title:       title.(string),
		Description: description.(string),
		Excerpt:     excerpt.(string),
		Date:        date.(string),
		Tags:        tagStrs,
	}
	return &post
}

The last step is building a HTML. If you’re interested in, please refer the source code at gist.