How much is that BLÅHAJ in the (terminal) window?
Tags: perl, webdev, api, mojolicious
Published: 2022-06-24 22:17:13

---
title: How much is that BLÅHAJ in the (terminal) window?
published: true
date: 2022-04-12 14:00:00 UTC
tags: Perl,webdev,APIs,Mojolicious
canonical_url: https://phoenixtrap.com/2022/04/12/how-much-is-that-blahaj-in-the-terminal-window/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dju1f2sc0g92mgvb1yn4.jpeg
---

[IKEA’s toy BLÅHAJ shark](https://www.ikea.com/us/en/p/blahaj-soft-toy-shark-90373590/) has become a [beloved Internet icon](https://knowyourmeme.com/memes/subcultures/ikea-blahaj-shark) over the past several years. I thought it might be cute to write a little Perl to get info about it and even display a cuddly picture right in the terminal where I’m running the code. Maybe this will give you some ideas for your own quick web clients. Of course, you could accomplish all of these things using a pipeline of individual command-line utilities like [`curl`](https://curl.se/), [`jq`](https://stedolan.github.io/jq/), and [GNU coreutils](http://www.gnu.org/software/coreutils/)’ [`base64`](https://www.gnu.org/software/coreutils/manual/html_node/base64-invocation.html). These examples focus on [Perl as the glue](https://perldoc.perl.org/perlglossary#glue-language), though.

## Warning: dodgy API ahead

I haven’t found a publicly-documented and ‑supported official API for querying IKEA product information but [others](https://towardsdatascience.com/a-single-line-of-python-code-scraping-dataset-from-webpages-c9d2a8805d61) [have](https://pypi.org/project/ikea-api/) deconstructed the company’s web site [AJAX](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX) requests so we can use that instead. The alternative would be to scrape the IKEA web site directly which, although possible, would be more tedious and prone to failure should their design change. An unofficial API is also unreliable but the simpler client code is easier to change should any errors surface.

## Enter the [Mojolicious](https://mojolicious.org/)

My original goal was to do this in a [single line issued to the `perl` command](https://perldoc.perl.org/perlglossary#one-liner), and luckily the [Mojolicious framework’s ojo module](https://docs.mojolicious.org/ojo) is tailor-made for such things. By adding a `-Mojo` switch to the `perl` command, you get over a dozen quick single-character functions for spinning up a quick web application or, in our case, making and interpreting web requests without a lot of ceremony. Here’s the start of my one-line request to the IKEA API for information on their BLÅHAJ product, using [ojo’s `g` function](https://docs.mojolicious.org/ojo#g) to perform an HTTP GET and displaying the [JSON](https://json.org) from the response body to the terminal.

```shell
perl -Mojo -E 'say g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->body'
```

This currently returns over 2,400 lines of data, so after reading it over I’ll convert the response body JSON to a [Perl data structure](https://perldoc.perl.org/perldsc) and dump only the main product information using [ojo’s `r` function](https://metacpan.org/pod/ojo#r):

```shell
perl -Mojo -E 'say r g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})
  ->json->{searchResultPage}{products}{main}{items}[0]{product}'
```

```json
{
  "availability" => [],
  "breathTaking" => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
  "colors" => [
    {
      "hex" => "0058a3",
      "id" => 10007,
      "name" => "blue"
    },
    {
      "hex" => "ffffff",
      "id" => 10156,
      "name" => "white"
    }
  ],
  "contextualImageUrl" => "https://www.ikea.com/us/en/images/products/blahaj-soft-toy-shark__0877371_pe633608_s5.jpg",
  "currencyCode" => "USD",
  "discount" => "",
  "features" => [],
  "gprDescription" => {
    "numberOfVariants" => 0,
    "variants" => []
  },
  "id" => 90373590,
  "itemMeasureReferenceText" => "39 \x{bc} \"",
  "itemNo" => 90373590,
  "itemNoGlobal" => 30373588,
  "itemType" => "ART",
  "lastChance" => $VAR1->{"breathTaking"},
  "mainImageAlt" => "BL\x{c5}HAJ Soft toy, shark, 39 \x{bc} \"",
  "mainImageUrl" => "https://www.ikea.com/us/en/images/products/blahaj-soft-toy-shark__0710175_pe727378_s5.jpg",
  "name" => "BL\x{c5}HAJ",
  "onlineSellable" => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ),
  "pipUrl" => "https://www.ikea.com/us/en/p/blahaj-soft-toy-shark-90373590/",
  "price" => {
    "decimals" => 99,
    "isRegularCurrency" => $VAR1->{"breathTaking"},
    "prefix" => "\$",
    "separator" => ".",
    "suffix" => "",
    "wholeNumber" => 19
  },
  "priceNumeral" => "19.99",
  "quickFacts" => [],
  "tag" => "NONE",
  "typeName" => "Soft toy"
}
```

If I just want the price I can do:

```
$ perl -Mojo -E 'say g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}
  ->@{qw(currencyCode priceNumeral)}'
USD19.99
```

That `->@{qw(currencyCode priceNumeral)}` towards the end uses the [postfix reference slicing syntax](https://perldoc.perl.org/perlref#Postfix-Reference-Slicing) introduced [experimentally in Perl v5.20](https://perldoc.perl.org/perl5200delta#Experimental-Postfix-Dereferencing) and [made official in v5.24](https://perldoc.perl.org/perl5240delta#Postfix-dereferencing-is-no-longer-experimental). If you’re using an older `perl`, you’d say:

```
$ perl -Mojo -E 'say @{g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}}{qw(currencyCode priceNumeral)}'
USD19.99
```

I prefer the former, though, because it’s easier to read left-to-right.

## But I’m not in the United States! Where’s my native currency?

You can either replace the ”`us/en`” in the URL above or use the [core I18N::LangTags::Detect module](https://perldoc.perl.org/I18N::LangTags::Detect) added in Perl v5.8.5 if you’re really determined to be portable across different users’ locales. This is really stretching [the definition of ”one-liner,”](https://en.wikipedia.org/wiki/One-liner_program) though.

```
$ LANG=de_DE.UTF-8 perl -Mojo -MI18N::LangTags::Detect \
  -E 'my @lang = (split /-/, I18N::LangTags::Detect::detect)[1,0];
  say g("https://sik.search.blue.cdtapps.com/"
  . join("/", @lang == 2 ? @lang : ("us", "en"))
  . "/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}
  ->@{qw(currencyCode priceNumeral)}'
EUR27.99
```

## Window dressing

It’s hard to envision cuddling a number, but luckily the product information returned above links to a JPEG file in the `mainImageUrl` key. My favorite terminal app [iTerm2 can display images inline](https://iterm2.com/documentation-images.html) from either a file or [Base64](https://en.wikipedia.org/wiki/Base64) encoded data, so adding an extra HTTP request and [encoding from the core MIME::Base64 module](https://perldoc.perl.org/MIME::Base64#encode_base64(-%24bytes-)) yields:

```shell
perl -Mojo -MMIME::Base64 \
  -E 'say "\c[]1337;File=inline=1;width=100%:",
  encode_base64(g(g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}{mainImageUrl})->body),
  "\cG"'
```

![Terminal window with a perl one-liner that shows an IKEA BLÅHAJ toy shark](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z235mwlom3n9l7w4kggv.png)

(You could just send the image URL to [iTerm2’s bundled `imgcat` utility](https://iterm2.com/documentation-utilities.html), but where’s the fun in that?)

```shell
imgcat --url `perl -Mojo \
  -E 'print g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}{mainImageUrl}'`
```

## But I don’t have iTerm2 or a Mac!

I got you. At the expense of a number of other dependencies, here’s a version that will work on any terminal that supports [256-color mode with ANSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) using [Image::Term256Color from CPAN](https://metacpan.org/pod/Image::Term256Color) and a Unicode font with [block characters](https://en.wikipedia.org/wiki/Block_Elements?wprov=sfti1). I’ll also use [Term::ReadKey](https://metacpan.org/pod/Term::ReadKey) to size the image for the width of your window. (Again, this stretches the definition of “one-liner.”)

```shell
perl -Mojo -MImage::Term256Color -MTerm::ReadKey \
  -E 'say for Image::Term256Color::convert(g(g("https://sik.search.blue.cdtapps.com/us/en/search-result-page",
  form => {types => "PRODUCT", q => "BLÅHAJ"})->json
  ->{searchResultPage}{products}{main}{items}[0]{product}{mainImageUrl})->body,
  {scale_x => (GetTerminalSize)[0], utf8 => 1})'
```

![Terminal window with a perl one-liner that shows an IKEA BLÅHAJ toy shark as jaggy ANSI graphics](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exbeqcmm3hwlg0hpobj3.png)

## I hate Mojolicious! Can’t you just use core modules?

Fine. Here’s retrieving the product price using [HTTP::Tiny](https://perldoc.perl.org/HTTP::Tiny) and the pure-Perl JSON parser [JSON::PP](https://perldoc.perl.org/JSON::PP), which were [added to core in version 5.14](https://perldoc.perl.org/perl5140delta#New-Modules-and-Pragmata).

```
$ perl -MHTTP::Tiny -MJSON::PP \
  -E 'say @{decode_json(HTTP::Tiny->new->get("https://sik.search.blue.cdtapps.com/us/en/search-result-page?types=PRODUCT&q=BLÅHAJ")
  ->{content})
  ->{searchResultPage}{products}{main}{items}[0]{product}}{qw(currencyCode priceNumeral)}'
USD19.99
```

Fetching and displaying a picture of the huggable shark using MIME::Base64 or Image::Term256Color as above is left as an exercise to the reader.