emahiLog

Golangと読んだ本の感想がメインです。

webpack + typescript

なぜtypescript?

MSが作ってGoogleが採用したので、フロントエンド界隈でtypescriptがしばらく積極的に使われて来そうな気が予感がしたのと、そろそろフロントエンドのキャッチアップしとかないとなーと感じ始めていたので...

typescriptの導入

typescriptをwebpackでコンパイルできるようにします。 https://webpack.js.org/guides/typescript/ を参考にして導入しました。

yarnでinstall

$ yarn add -D typescript ts-loader inline-source-map

ts-loader
refs: https://github.com/TypeStrong/ts-loader

tsの設定

tsconfig.jsonを作成する

$ touch tsconfig.json
$ ls -la
drwxr-xr-x    - user 14 10  0:56 .idea
drwxr-xr-x    - user 14 10  0:56 dist
drwxr-xr-x    - user 14 10  1:28 node_modules
.rw-r--r--  640 user 14 10  1:28 package.json
drwxr-xr-x    - user 14 10  0:54 src
.rw-r--r--    0 user 14 10  1:29 tsconfig.json
.rw-r--r--  857 user 14 10  0:56 webpack.config.js
.rw-r--r-- 142k user 14 10  1:28 yarn.lock

tsconfig.jsonにtypescriptをes5にコンパイルする設定をカリカリ書いていく。
今回は最初というのもあり、webpackのtypescript導入に記載してあるjsonファイルをそのまま使う。

json内でjsxを使っているが今回は使っていない

{
    "compilerOptions": {
        "outDir": "./dist/",
        "noImplicitAny": true,
        "module": "commonjs",
        "sourceMap": true,
        "target": "es5",
        "jsx": "react",
        "allowJs": true
    }
}

webpack.config.js

以下の用にts-loaderを入れてtypescriptをコンパイルできるようにする

const src = __dirname + "/src";
const dist = __dirname + "/dist"

var webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
    context: src,
    entry: "./ts/index.ts",
    devtool:"inline-source-map",
    module: {
        rules:[
            {
                test: /\.html$/,
                loader: "html-loader"
            },
            {
                test:/\.tsx?$/,
                loader: "ts-loader",
                exclude: /node_modules/
            },
        ]
    },
    resolve: {
        extensions:[".js", ".ts", ".tsx"]
    },
    output: {
        path: dist,
        filename: "bundle.js"
    },
    devServer: {
        contentBase: dist,
        port: 3000
    },
    plugins: [
        new UglifyJSPlugin(),
    ]
};

typescriptファイルの作成

$ touch src/ts/index.ts
console.log("test") //のみを記載

コンパイル

ema-hiro.hatenablog.com

で作ったyarnのscriptの再利用。

$ yarn run build
yarn run v1.1.0
$ yarn run --config webpack
ts-loader: Using typescript@2.5.3 and ~/emahiro/ts_sample/tsconfig.json
Hash: bcac9e140803472acb6d
Version: webpack 3.7.1
Time: 1114ms
     Asset       Size  Chunks             Chunk Names
 bundle.js  498 bytes       0  [emitted]  main
index.html  182 bytes          [emitted]
   [0] ./ts/index.ts 21 bytes {0} [built]
Child html-webpack-plugin for "index.html":
     1 asset
       4 modules
✨  Done in 2.27s.

動作確認する

以前作成したものと同様の設定で webpack-dev-server を起動し localhost:3000 にアクセスする

$ yarn run start // ※ リンクにあるブログで作成済みのコマンド
Project is running at http://localhost:3000/
webpack output is served from /
Content not from webpack is served from ~/emahiro/ts_sample/dist

開発者画面 -> consoleを見て test とlogが記載されていればtypescriptはes5にコンパイルされて正常にブラウザで読み込まれている。

所感

webpack + typescriptは想像以上に導入が簡単。
次は簡単なアプリを作ってみたい。

広告を非表示にする

webpackでhtmlファイルも出力する

ema-hiro.hatenablog.com

昨日の上記エントリへの追記

TODO

  • htmlファイルをdist以下に作成していましたが、frontendのファイルはsrc以下に管理します。
  • webpackでコンパイルしたらdist以下にhtmlファイルも出力させます
  • html-webpack-pluginを使います

install

$ yarn add -D html-webpack-plugin
$ yarn add -D html-loader

webpack.config.js

html-webpack-pluginhtml-loader を加えてコンパイルの設定を更新する

const src = __dirname + "/src";
const dist = __dirname + "/dist"

var webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  context: src,
  entry: "./js/index.js",
  output: {
    path: dist,
    filename: "index.min.js"
  },
  devServer: {
    contentBase: dist,
    port: 3000
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude:/node_modules/,
        loader: "babel-loader",
        query: {
          presets:[
            ["env", {
              "targets": {
                "node": "current"
              }
            }]
          ]
        }
      },
      {
        test: /\.html$/,
        loader: "html-loader"
      }
    ]
  },
  plugins: [
    new UglifyJSPlugin(),
    new HtmlWebpackPlugin({
      template: "./html/index.html"
    })
  ]
};

ディレクトリの変更

- root
  - src
    - js
      - index.js
    - html
      - index.html
  - dist
    - 出力先

index.htmlを作成する

src/html/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>test</title>
  </head>
  <body>
    hello world
  </body>
</html>

コンパイルする

$ yarn run build
yarn run v1.1.0
$ yarn run --config webpack
Hash: 1c4cb048a83c8c452b7b
Version: webpack 3.6.0
Time: 577ms
       Asset       Size  Chunks             Chunk Names
index.min.js  814 bytes       0  [emitted]  main
  index.html  195 bytes          [emitted]
   [0] ./js/index.js 400 bytes {0} [built]
   [1] ./js/person.js 171 bytes {0} [built]
Child html-webpack-plugin for "index.html":
     1 asset
       [0] ../node_modules/html-webpack-plugin/lib/loader.js!./html/index.html 168 bytes {0} [built]
✨  Done in 1.71s.

buildpackage.json で新しくscriptsに追加したカスタムビルドコマンド

生成された index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>test</title>
  </head>
  <body>
    hello world
  <script type="text/javascript" src="index.min.js"></script></body>
</html>

コンパイルした index.min.jsscript タグに入ってちゃんと生成されている。

この状態で webpack-dev-server を動かしてちゃんと動作するかを確認する

おまけ

複数のhtmlファイルをコンパイルしたい

refs: https://github.com/jantimon/html-webpack-plugin#generating-multiple-html-files

SPAならindex.htmlだけ作成してそこにSPAの要素を追加していくのもありであるが、複数のhtmlファイルを出力したい場合がある。 そういう場合は html-webplack-plugin はデフォでは index.html しか出力しないが、別のファイルを新しくpluginで指定してfile名とコンパイル対象のtemplateを指定すれば良い。

// 略
plugins: [
  // index
  new HtmlWebpackPlugin({
    template: "./html/index.html"
  }),
  // その他
  new HtmlWebpackPlugin({
    filename: "admin.html",
    template: "./html/admin.html"
  })
]
$ ls dist/
admin.html  index.html

htmlファイルが複数出力されている

所感

webpack一発でフロントエンドの環境が構築されるっていうのはこういうことかってわかってきた。
今回はベタなhtmlをそのままコンパイルしているがテンプレ(ejsファイルとか)をコンパイルすることも可能

引き続きサンプルコードはこちら

github.com

いまさらだけどyarn + webpackを試す

仕事の中でjsの開発環境を構築する機会があったので、自前でes6をコンパイルする環境をととえてみた備忘録。
jsは動きが早くて色んなツールが出て来るなか、一定出尽くして落ち着いてきそうな感じをここ最近感じていたので、ようやく少しずつキャッチアップしていこうと思いました。

サマリ

  • yarn + webpack導入
  • es6を動かす
  • es6で書いたコードを動かす

どうしてyarnにしてのか

yarnとは?
facebookが開発したnext npm的なポジションのpackageマネージャー

去年10月にローンチして、徐々に使用事例が多くなってきたので自前でjsのモダンな開発環境を作るにあたって採用してみようと思ったから。

yarnのいいところ

https://qiita.com/0829/items/ec5271c06f8ff0633dd3 あたりを参考にした。

npmが生成した package.json を読み込める用に作られているので、npmとの互換性が保たれているので、正直どちらを使ってもいいと思われるが、npmより高速に動作し、依存関係やバージョン等も厳格に管理することができるため、npmでもできるが、yarnを積極的に使わない理由も弱い。

といった感想を持っている。

導入手順

yarn init

$ mkdir MyProject
$ cd MyProject
$ yarn init
yarn init
yarn init v1.1.0
...
success Saved package.json

対話式でプロジェクトの概要をセットアップしていく。
完了したらpackage.jsonができる

babelの設定

babelを使う場合は、 .babelrc が必要なものかと思ってましたが、webpackの場合は webpack.config.jsコンパイルの定義を書けます。

babelと一緒にes2015(es6)の構文対応できるように、 babel-preset-env をインストールします。

$ yarn add -D babel-loader babel-core babel-preset-env

当初 babel-preset-es2015 を入れてましたが、yarn add している最中に warning babel-preset-es2015@6.24.1: 🙌 Thanks for using Babel: we recommend using babel-preset-env now: please read babeljs.io/env to update! というwarnを受けたので、 babel-preset-env を入れました。

refs: https://babeljs.io/env/
github: https://github.com/babel/babel-preset-env

webpack.config.jsの設定

コンパイル元のファイルはentryに書いて、1つに固めたファイルの出力先がoutput。

というわけで出来上がった webpack.config.js はこちら

const src = __dirname + "/src";
const dist = __dirname + "/dist"

var webpack = require('webpack');

module.exports = {
  context: src,
  entry: "./index.js",
  output: {
    path: dist,
    filename: "index.min.js"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude:/node_modules/,
        loader: "babel-loader",
        query: {
          presets:[
            ["env"]
          ]
        }
      }
    ]
  }
};

es6の導入

ディレクトリ構成は以下

MyProject
  src
    index.js
  dist
    index.min.js

このindex.jsにes6の構文で書いて、コンパイル後index.min.jsがちゃんと読み込まれるかを確認する

おまけ

難読化(UglifyJS)

コンパイルしたファイルを難読化する。
webpackには標準でUglifyJSがあるので、これを plugins に追加したが、defaultの難読化プラグインコンパイル時にエラーが発生した

$ yarn run build
yarn run v1.1.0
$ yarn run --config webpack
Hash: 2689b764da6353bc4101
Version: webpack 3.6.0
Time: 341ms
       Asset     Size  Chunks             Chunk Names
index.min.js  3.11 kB       0  [emitted]  main
   [0] ./index.js 371 bytes {0} [built]
   [1] ./person.js 162 bytes {0} [built]

ERROR in index.min.js from UglifyJs
Unexpected token: name (Friend) [index.min.js:79,6]
error Command failed with exit code 2.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

標準のuglify-jsが動かなかったので、uglifyjs-webpack-pluginを使う
refs: https://www.npmjs.com/package/uglifyjs-webpack-plugin

$ yarn add -D uglifyjs-webpack-plugin
const src = __dirname + "/src";
const dist = __dirname + "/dist"

var webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin') //追加

module.exports = {
  context: src,
  entry: "./index.js",
  output: {
    path: dist,
    filename: "index.min.js"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude:/node_modules/,
        loader: "babel-loader",
        query: {
          presets:[
            ["env", {
              "targets": {
                "node": "current"
              }
            }]
          ]
        }
      }
    ]
  },
  plugins: [
    new UglifyJSPlugin() //追加
  ]
};

webpackを動かす

yarn run build
yarn run v1.1.0
$ yarn run --config webpack
Hash: 2689b764da6353bc4101
Version: webpack 3.6.0
Time: 381ms
       Asset       Size  Chunks             Chunk Names
index.min.js  786 bytes       0  [emitted]  main
   [0] ./index.js 371 bytes {0} [built]
   [1] ./person.js 162 bytes {0} [built]
✨  Done in 1.37s.

$ cat dist/index.min.js
!function(e){function t(n){if(r[n])return r[n].exports;var u=r[n]={i:n,l:!1,exports:{}};return e[n].call(u.exports,u,u.exports,t),u.l=!0,u.exports}var r={};t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e:{default:e}}(r(1));class u extends n.default{constructor(e){super(name)}callName(){alert(this.name)}}new u("Taro").callName()},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});class n{constructor(e){this.n=e}}t.default=n}]);⏎

ちゃんと難読化されている。

動かしてみる

webpackにはlocalにサーバーを立てるライブラリ webpack-dev-server があるのでこちらを使う

$ yarn add -D webpack-dev-server
$ yarn run webpack-dev-server //これで動作させることができる。

上記でも問題ないがscriptを作成した

"scritps": {
  "start": "yarn run webpack-dev-server"
  "build":...
}

devserverはデフォルトは8080ポートで動作するが、手元のgoとパッティングするので3000番portに変更。
以下を webpack.config.json に追加する

const src = __dirname + "/src";
const dist = __dirname + "/dist"

var webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  context: src,
  entry: "./index.js",
  output: {
    path: dist,
    filename: "index.min.js"
  },
  devServer: {
    contentBase: dist,
    port: 3000
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude:/node_modules/,
        loader: "babel-loader",
        query: {
          presets:[
            ["env", {
              "targets": {
                "node": "current"
              }
            }]
          ]
        }
      },
      // 以下でhtmlの読み込みも追加する
      {
        test: /\.html$/, 
        loader: "file?name=[path][name].[ext]"
      }
    ]
  },
  plugins: [
    new UglifyJSPlugin()
  ]
};

サーバーを起動させる

yarn run start
yarn run v1.1.0
$ yarn run webpack-dev-server
Project is running at http://localhost:3000/
webpack output is served from /
Content not from webpack is served from ~/emahiro/es6_sample/dist
Hash: 093cadee3f8742a9e298
Version: webpack 3.6.0
Time: 2247ms
       Asset    Size  Chunks             Chunk Names
...
webpack: Compiled successfully.

注目すべきは以下

webpack output is served from /
Content not from webpack is served from ~/emahiro/es6_sample/dist

サーブされているのは / だけど、実際にはdistがwebから見たときのcontentのrootになる

dist
  index.min.js

というディレクトリ構造をしている場合、htmlからjsを呼び出すときは ./index.min.js と直書きする。

サーバーが起動した状態で localhost:3000 にアクセスしたら alert が発火するはず。
発火したら正常にes6がコンパイルされて動作していることになる。

所感

今までjsの潮流が早すぎて、正直足突っ込んだら抜けられなくなりそうだったので、避けてきた分野でしたが、webpackを使うことでjsド素人でも簡単にes6をプロジェクトに取り入れることが出来ました。

色々試せそうですが、次はtypescript入れてコンパイルしてみようと思います。

今回作ってみたリポジトリはこちら

github.com

goでのhttpの書き方あれこれ

goの標準

goでhttpの処理を書きたいときはnet/httpパッケージを使う。
強力なパッケージなので、基本これを使うで、やりたいことの殆どはまかなえてしまうと思う

http.Get(url)

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, err := http.Get("http://www.google.co.jp")
    if err != nil {
        fmt.Printf("err: %v", err)
    }
    defer resp.Body.Close()

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("ioutil err: %v", err)
    }
    fmt.Printf("html: %v", string(b))
}

http.NewRequest(url)

単純にGetをするだけであれば http.Get(url) で事足りると思われるが、その他のMethodにも対応させるために、httpクライアントを明示的に作ることも可能

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    req, err := http.NewRequest("GET", "http://www.google.co.jp", nil)
    if err != nil {
        fmt.Printf("new request err: %v", err)
    }
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("request response err: %v", err)
    }

    defer resp.Body.Close()

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("ioutil err: %v", err)
    }
    fmt.Printf("html: %v", string(b))
}

GAEの標準

GAE/Goでhttpを書きたいときにはちょっと1つ考慮しておかなければいけないことがある。

  • GAE/Goでは、GAEのContextを使わないといけない
  • 通常のリクエストからGAEのContextを作成し、それを使ってhttpのrequest/responseを実装する
package main

import (
    "net/http"

    "io/ioutil"

    "google.golang.org/appengine"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine/urlfetch"
)

func init() {
    http.HandleFunc("/", SampleHandler)
}

func SampleHandler(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r) // GAEのContextを作成する
    httpClient := urlfetch.Client(ctx)
    resp, err := httpClient.Get("http://www.google.co.jp")
    if err != nil {
        log.Debugf(ctx, "html: %v", err)
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Debugf(ctx, "html: %v", err)
    }
    log.Debugf(ctx, "html: %v", string(b))
}

ハマったところ

  • gaeのプロジェクトの最小構成単位はhandlerを受け付けるgoのファイルとapp.yamlがあること。
  • しかし、この2つのファイルは同じ階層においては行けない
- my-project
    - app.yaml
    - src
        - main.go

というディレクトリ構成にする必要がある。

  • gaeではmain関数でなく init() を使う。
  • main() を使っていた最中にずっと起動後 localhost:8080 に繋いでも 404 page not found が返ってきてしまっていた。
  • gaeのhttpのHandlerには http.ListenAndServe() メソッドは使わない。

The Twelve-Factor App を読む

ちょっと前にはてブで上位に上がっていた以下のスライド

speakerdeck.com

の中に出てきていた「The Twelve-Factor App」を読んだ感想を書く。

原典の邦訳はこちら ▶ https://12factor.net/ja/

今更感があるがちょっと最近基礎が揺らいでいるなという感覚もあったのでざっと通読してみた。

  • 12個の原則はどれも昨今のウェブサービスを作るのであれば当たり前とも言える内容だった。
  • 自分がそういう環境でしか仕事をしたことがないからかもしれないが、既存のチームにJOINするにしても、新しく1からサービスを作るにしても意識、というか当たり前にできなければいけないことだと思う。
  • 内容としては昨今のフルマネージドが流行りつつある状況ではもしかしたらvagrantやポートバインディングなどは自分で管理するものではないかもしれない。
  • しかし、背景にこういう法則があって、というContextを理解しておくことは大事だと思う。

結構忘れていることも多く、API開発の名著も読み直そうと思う。

goで一時的なスクリプトを書く方法

Goで開発しているときに

  • 標準パッケージの動作確認したい
  • ライブラリの動作確認したい
  • 簡単なスクリプト(HTTPやgoroutineなど)を書きたい

と思って調べてみました。

Rubyならirbやpryを使ってコンソールで簡単な動作検証を行えたり、変数の内部見ることができますが、Goは静的な言語でコンパイルすることが必要になるので、デフォルトではそういうちょっとした確認のためのスクリプト書いたりみたいなことができません。

Goで一時的なスクリプト確認したい場合はいつも The Go Playgroundを使っていましたが、

  • 補完が効かない
  • 標準パッケージしか使えない

という2点があって、例えばgithub上のライブラリの検証は実際に手元に落としてきて、Goでsampleファイル書くっていう手順をいつも行っててめんどくさいなーって思ってました。

そこでGoでもREPL的なものが無いかを調べてみたところ

ありました

github.com

作者さんが書いた使い方のエントリー motemen.hatenablog.com

実際に入れてみたところ

$ gore
gore version 0.2.6  :help for help
gore> :import encoding/json
gore> b, err := json.Marshal(nil)
[]uint8{
  0x6e, 0x75, 0x6c, 0x6c,
}
nil

おおおお!これはよい。簡単なスクリプトを試すにはすごく便利です。
:import すると手元に落としてきたライブラリもimportできるので、標準以外のメジャーなライブラリのサンプル書くのもいちいちGoのファイル作らなくていいので楽になります。

ppgocode をあわせて入れとくと出力をきれいに整形してくれたり、補完が効くようになります。
可読性も効率もアップできるのでとても便利なデバック用のライブラリだと思います。

GoonのGet()とGetAll()の違いでハマる

Go+GAEの環境でCloudDataStoreからデータを取得するときに

  • Get()
  • GetMulti()
  • GetAll()

の挙動の違いでハマったので備忘録として挙動をまとめておきます。

Datasotreのライブラリ

datastore

https://godoc.org/google.golang.org/appengine/datastore

googleappengineには標準でdatastoreのクライアントとして datastore が用意されています。 僕も使うようになってこの便利さに気づいたのですが、structでRDBでいうところのレコード、datasotreではentityの構造を定義してしまえば、かなり簡単にCRUDの操作ができます。
※ ここでいうentityの構造とはモデルの構造のことです。

goon

https://godoc.org/github.com/mjibson/goon

goonはdatastoreとほぼ同じようなインターフェースを持っていて、自動キャッシュがついているので、キャッシュのことまで考えた場合に使いやすいライブラリです。

データを取得する

goonを使ってDatastoreからentityを取得する場合以下のようにします。

type entity struct {
    Id      int64 `datastore:"-" goon:"id"`     
    Field_1 string
    Field_2 string
}

func GetInstances(r *http.Request){
    g := goon.NewGoon(r)
    entity := &entity{ID:1}
    g.Get(entity) // ID(key) = 1のentityを取得する
}

goon.NewGoon(r) としている箇所はhttp.Requestから新規でappengineのContextを作成しています。NewGoonメソッドの内部はgoonのソースコード読むと

// NewGoon creates a new Goon object from the given request.
func NewGoon(r *http.Request) *Goon {
    return FromContext(appengine.NewContext(r))
}

ちなみに以下のような書き方も可能

g := goon.NewGoon(r)
entity := &entity{ID:1}
g.Goon().Get(entity) // ID(key) = 1のentityを取得する

とあるので、ここでappengineから新しくContext作っているのが一目瞭然ですね。

遭遇したエラー

Datastoreからデータを取得するときに以下のエラーに遭遇しました。
順を追ってどうしてエラーが発生したのか見ていきます。

  1. goon: cannot find a key for struct
  2. goon: Expected dst to be a pointer to a slice or nil, got instead
  3. invalid entity type

1. goon: cannot find a key for struct

こちらはGet()とGetMultiが ID(key) をKeyにしないとDatastoreからデータを取得できない性質を持っているということを知らなかったために発生しました。

RDBのようにprimaryキーだけでなく別のfieldをフックにしてデータを取得したい場合があると思います。
しかし、DataStoreのGet()とGetMulti()はID(DatastoreでいうところのprimaryKey)をKeyに取ることしかできません。

そのため、ID以外のfieldをKeyにしたい場合には上記2つのメソッドではなく、GetAll()メソッドを使ってクエリを直に指定する必要があります。

2. goon: Expected dst to be a pointer to a slice or nil, got instead

これはGetAll()を使うときに発生しました。 dstは GetAll(query *datastore.Query, dst interface{}) とあるようにGetAll()メソッドの第二引数です。

DataStoreからデータを取得する場合には dst に対して指定した参照型の変数にデータが格納されて入ってきます。
これがそのまま、DataStoreから取得したデータの結果になります。
そのため、dstには slice化されたpointer型の変数 もしくは何も指定指定しない nil しかいれることができません。
上記のサンプルでも entity が参照型になっています。

3. invalid entity type

これも2と要点は同じなんですが、Datastoreから取得したEntityの入れ物となる参照型の変数の構造は取得したいDatastoreのkindのfield構成と一致している必要があるそうです。

言われてみれば当たり前のような気もしますが、参照型、かつ構造が同じでないといけません。

Datastoreのルールで詰まったり、構造で詰まったり、そもそもなぜdstは参照型で渡すのか、色々知るいい機会になりました。