Salesforce + JavaScript開発者をご機嫌にする JSforce 2.0 α版を速攻プレビュー

## ついに出る! JSforce 2.0

JSforce 2.0 のαバージョンがリリースされています。(2.0.0-alpha.1)

2.0は、1.xでの JavaScript (ES5)ベースのコードからTypeScriptでの総書き換えになります。一時期はJSforceのAuthorの好みというか主義からFlowtypeでの書き換えを行っていましたが、昨今のTypeScriptの型表現力の向上と採用数の増加を受けてTypeScriptに途中で書き換えられました。そのため、4,5年くらい前から計画されていた2.0ではありますが、やっとのαリリースになります。

JSforceがTypeScriptベースで書き直されたことの恩恵は、まずはライブラリ内のコードの品質を高める効果が第一となるので、同ライブラリを利用するだけのユーザから見ればあまり感じないかもしれません。しかし昨今ではTypeScriptを用いてアプリケーションを開発するユーザも多いでしょう。その場合、TypeScriptの型定義ファイルが同梱されることによって、より安全にAPIを利用した開発が可能になっています。

α版なのでインストールの際には明示的にバージョン指定が必要です。

```
$ npm install -g jsforce@2.0.0-alpha.1
```

なおα版につき現在いくつかのAPIモジュール(stream, bulk など)はまだ利用できません。

## 2.0の注目機能をピックアップ

### TypeScriptで項目名を自動補完！スキーマ型機能

JSforce2.0の最大の特徴はTypeScriptで書き直されたことで型定義がネイティブに提供されることです。
もちろん今までも[DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jsforce)で有志によって公開された型定義ファイルを入手することで型を使った開発はできましたが、一部APIインターフェースと型定義が異なるところがあったり、ざっくりとしか定義されていなかったりと、少し不満もあったのではないかと思います。またSalesforceオブジェクト/項目などのスキーマについては未対応であるなど、Apexでのスキーマ情報を活かした開発に慣れている人にとっては物足りない面もありました。

2.0ではTypeScriptの型推論能力を活用し、APIのインターフェースのみならず、クエリやCRUD操作においてSalesforceオブジェクトのスキーマ情報を利用したレコードの型チェック/推論ができるようになりました。これにより、Visual Studio Codeなどを利用してコーディングを行う際、`SObject#find()`などのクエリで絞り込む項目名や取得する項目名を自動補完したり、クエリ実行後に取得されたレコードに含まれる項目の型をTypeScriptで保証できるようになります。

項目自動補完がどのように行われるのかはこちらのスクリーンキャストをご覧いただければ分かるかと思います。

![jsforce-2-autocomplete-demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6495/55841fc3-4481-329e-5a72-1c12b225d2c1.gif)

上記動画の完成コードは以下のようになります。

```ts
import jsforce, { Connection, StandardSchema } from 'jsforce';

(async () => {
  // Specify schema of connecting organization
  const conn = new Connection<StandardSchema>();
  await conn.login(process.env.SF_USERNAME, process.env.SF_PASSWORD);

  // fetch records using find()
  const recs = await conn.sobject('Opportunity').find({
    CloseDate: { $lte: jsforce.Date.YESTERDAY },
    IsClosed: true
  });

  // output retrieved records info
  for (const rec of recs) {
    console.log(rec.Amount, rec.Name, rec.LastActivityDate);
  }
})();
```

Connectionインスタンスの作成時のコンストラクタ呼び出しの型変数として`StandardSchema`というスキーマ用の型を指定しています。こちらは標準オブジェクト/項目のみを利用する場合のビルトインのスキーマ型定義です。

もし自分の開発組織でカスタムオブジェクト/項目を追加している場合は、カスタムのスキーマ型定義を生成して利用することができます。組織に接続してスキーマ型定義ファイルをダンプするためのコマンド `jsforce-gen-schema` が新たに用意されています。

```
$ jsforce-gen-schema --help
Usage: jsforce-gen-schema [options]

Options:
  -u, --username [username]      Salesforce username
  -p, --password [password]      Salesforce password (and security token, if available)
  -c, --connection [connection]  Connection name stored in connection registry
  -l, --loginUrl [loginUrl]      Salesforce login url
  -n, --schemaName [schemaName]  Name of schema type (default: "MySchema")
  -o, --outputFile <outputFile>  Generated schema file path (default: "./schema.d.ts")
  --sandbox                      Login to Salesforce sandbox
  --no-cache                     Do not generate cache file for described result in tmp directory
  --clearCache                   Clear all existing described cache files
  -V, --version                  output the version number
  -h, --help                     output usage information

$ jsforce-gen-schema -u username@example.org -p pass123 -n MySchema -o ./myschema.d.ts
Logged in as : username@example.org
describing global
describing AcceptedEventRelation
describing Account
describing AccountBrand
describing AccountBrandShare
describing AccountChangeEvent
describing AccountCleanInfo
...
Dumped to: ./myschema.d.ts
```

利用時には生成された`myschema.d.ts`ファイルをimportして、スキーマ型をコンストラクタの型変数に指定します。

```ts
import jsforce, { Connection } from 'jsforce';
import { MySchema } from './myschema';

...

const conn = new Connection<MySchema>();

```

### ログインの必要なし！Salesforce CLIの接続を利用できる接続レジストリ機能

今まで正式にはドキュメント化されていませんでしたが、JSforceには接続レジストリという機能がありました。これはJSforce CLIで接続した接続済のコネクションをJavaScriptコード内から名前指定で利用できるという機能です。取得した`Connection`インスタンスはすでに接続済みなので、ユーザ名/パスワードやOAuthなどでログイン処理を書く必要がありません。

```ts
import jsforce from 'jsforce';

(async() => {
  const username = 'username@example.com';
  const conn = await jsforce.registry.getConnection(username);
  const accounts = await conn.sobject('Account').find().limit(10);
  console.log(accounts);
})();
```

今回、この接続情報のレジストリに、Salesforce CLI(sfdx)が保管する組織の接続情報を利用できるようになりました。`Registry#getConnection()`にはユーザ名だけでなくエイリアスも指定可能です。

```ts:index.js
import jsforce from 'jsforce';

(async() => {
  const usernameOrAlias = 'username@example.com';
  // const usernameOrAlias = 'my-scratch-org';
  const conn = await jsforce.registry.getConnection(usernameOrAlias);
  const accounts = await conn.sobject('Account').find().limit(10);
  console.log(accounts);
})();
```

なおsfdxで保持されている組織の接続情報をレジストリとして用いる場合は、現在実行時に環境変数`JSFORCE_CONNECTION_REGISTRY`に`sfdx`と指定しておく必要があります。

```
$ export JSFORCE_CONNECTION_REGISTRY=sfdx
$ node index.js
```

なお、JavaScriptプログラムからだけではなく、JSforceのREPLでもsfdxレジストリを利用することができます。Scratch組織に接続してゴニョゴニョしたいときにとても便利です。

```
$ export JSFORCE_CONNECTION_REGISTRY=sfdx
$ jsforce -c MyScratchOrg
Logged in as : test-abcdefghijkl@example.com
> query('SELECT count() FROM Account')
{ totalSize: 78, done: true, records: [] }
```

### Promiseスタイルの呼び出しがデフォルトに。Callbackスタイルのコードは書き換え必要（かも？）

JavaScript（およびNode.js）での非同期関数呼び出しの慣習として、コールバック関数を非同期関数の末尾引数として渡し、非同期の返値（およびエラー）をそのコールバック関数の引数で受け取る形式が主流でした。しかしながら、ES6(ES2015)以降にPromiseがJavaScriptでも標準化されたことで、PromiseでのAPIコールが2.0ではデフォルトとなっています（なお1.xでもPromise形式でのAPIコールはサポートされています）。

Promiseは従来の`Promise#then()`によるチェーン呼び出しで非同期処理を連結しますが、ES2017より導入された`async`/`await`を利用するのがよりモダンな書き方でしょう。

```js
async function main() {
  const conn = new jsforce.Connection();
  const { id: userId } = await conn.login(
    'username@example.org',
    'password123'
  );
  const recs = await conn
    .sobject('Account')
    .find()
    .limit(10);
  const urecs = recs.map(r => ({
    Id: r.Id,
    OwnerId: userId
  }));
  const rets = await conn.sobject('Account').update(urecs);
  for (const ret of rets) {
    if (ret.success) {
      console.log(ret.id);
    } else {
      console.error(ret.errors.map(e => e.message).join('\n'));
    }
  }
}
```

現在の2.0αでは、コールバック形式の呼び出しはサポートされていません。正式リリースの際にコールバック形式が残されるかどうかは現時点では不明です。

### Semantic Versioningに正式対応(予定)

今までJSforceのversionは、実はSemantic Versioningにはなっていませんでした。例えば1.3から1.4になる際に、`Metadata.create()` のAPIインターフェースは変わっているのですが、メジャーバージョンを上げていません。2.0リリース以降はインターフェースの変更についてはメジャーバージョン(2.0 => 3.0)で対応することになります。


## まとめ

以上、まだalphaバージョンですが、coreの実装は固まりつつあるので、一度触ってみてフィードバックいただければと思います（特にTypeScriptユーザにぜひ）。

なお、2020年1月の[Japan Dreamin'](https://www.japandreamin.com/) にてJSforce 2.0が初お目見え？するらしいです。Authorから直接JSforceの内容をセッション形式で聞けるのはグローバル含めて初とのことなので、参加してみてはいかがでしょうか。