petitviolet blog

    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](https://oembed.com/) > 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.

    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=

    Hello World New Blog using Gatsby Created a new blog with using [GatsbyJS](https://www.gatsbyjs.org/) and [gatsby-starter-blog](https://github.com/gatsbyjs/gatsby-starter-blog). The end of the fiscal year is a good time to build something new. 1 year ago, I renewed my portfolio as well using Gatsby. https://petitviolet.hatenabl
    . 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.