1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScript/JSエンジニアがApexに入門して感じた「違い」と「勘所」

Posted at

普段はTypeScriptやJavaScript(以下TS/JS)を中心にWeb開発を行っているエンジニアが、Salesforceのバックエンド言語である Apex を学習した際の記録です。
TS/JSの知識をベースに、「ここは似ている」「ここは全然違う」というポイントをまとめました。同じようなバックグラウンドを持つ方の参考になれば幸いです。

1. 言語の雰囲気:ほぼJava、でもTS勢も読める

Apexの構文は Javaに非常に似ています
TSを使っている人であれば、クラスベースのOOPや静的型付けには慣れていると思うので、コード自体はスラスラ読めると思います。

// Apexのクラス定義
public class HelloWorld {
    public static void sayHello() {
        String msg = 'Hello, Apex!';
        System.debug(msg);
    }
}

JS/TSとの主な違い(基本構文)

  • 変数の宣言: let / const ではなく、型を明示します(型推論は基本ありません)。
  • 定数: final 修飾子を使います。
  • セミコロン: 必須です。ASI(自動挿入)はありません。
  • 文字列: シングルクォート ' が基本です。
// TypeScript
const count: number = 10;
// Apex
Integer count = 10;
final String GREETING = 'Hello';

2. コレクション操作:List, Set, Map

JSの Array, Set, Map に相当するものが Apex にもあります。

List (配列相当)

JSの配列とほぼ同じですが、宣言時に「何が入るか」をジェネリクスで指定するのが一般的です。

// Apex
List<String> names = new List<String>{'Alice', 'Bob'};
names.add('Charlie');

// アクセスは [] も使えるが、get() もある
System.debug(names[0]); 

JSの mapfilter のような高階関数は、Apexには長らく存在しませんでしたが、最近のアップデートで Comparator や一部のメソッドが充実してきています。しかし、基本的には for-eachループ で処理することが多いです。

// Apexでのループ
for (String name : names) {
    System.debug(name);
}

Map (オブジェクト/Map相当)

JSではオブジェクト { key: val } をMapとして使うことが多いですが、Apexでは明確に Map クラスを使います。

Map<String, Integer> scores = new Map<String, Integer>();
scores.put('Alice', 100);
Integer aliceScore = scores.get('Alice');

3. 非同期処理:Promiseはない

ここがJS勢にとって一番のカルチャーショックかもしれません。Apexには Promiseasync/await はありません。

Apexは基本的には 同期的に 実行されます。
APIコールアウトや重い処理を非同期で行いたい場合は、以下の仕組みを使います。

  1. @future メソッド: 非同期で実行されるメソッド。
  2. Queueable Apex: ジョブキューのような仕組み。
  3. Batch Apex: 大量のレコードを処理するためのバッチ処理。

「この処理が終わったら次を実行する」というチェーン(.then())を書きたい場合、Queueableの中から次のQueueableを呼ぶ(Chaining)などの工夫が必要です。

4. ガバナ制限 (Governor Limits)

Apexはマルチテナント環境(サーバーリソースを多数の企業で共有)で動くため、リソース消費に対する厳格な制限があります。
これが「ガバナ制限」です。JSのランタイム(ブラウザやNode.js)ではメモリリークくらいしか気にしませんが、Apexでは以下のような制限と戦う必要があります。

  • SOQLの発行回数: 1トランザクションにつき100回まで
  • DML(DB更新)の発行回数: 150回まで
  • CPU時間: 10,000ミリ秒(10秒)まで

そのため、ループの中でクエリ(SOQL)や更新(DML)を行うのは御法度です。

❌ ダメな例(アンチパターン)

List<Account> accounts = [SELECT Id FROM Account];
for (Account acc : accounts) {
    // ループ内でクエリを投げている -> 制限オーバーでエラーになる可能性大
    Integer contactCount = [SELECT Count() FROM Contact WHERE AccountId = :acc.Id];
}

✅ 良い例(バルク化)

「必要なデータはループの外でまとめて取る」のが鉄則です。これを 「バルク化 (Bulkification)」 と呼びます。

// IDセットを作成
Set<Id> accIds = new Set<Id>();
for (Account acc : accounts) {
    accIds.add(acc.Id);
}

// まとめて取得(1回のクエリ)
List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accIds];

5. デバッグとテスト

console.log ではなく System.debug

System.debug('Hello World');

出力はブラウザのコンソールではなく、Salesforce上の「デバッグログ」で確認します。

テストコードは必須

JSの世界ではテストを書くかどうかはプロジェクト次第ですが、Salesforceでは 本番環境にデプロイするために、75%以上のテストカバレッジが必須 です。
そのため、開発とテストライティングはセットです。

@isTest
private class MyTest {
    @isTest static void testBehavior() {
        // テストデータの作成
        Account acc = new Account(Name='Test');
        insert acc;
        
        // 処理の実行とアサーション
        Test.startTest();
        // ...method call...
        Test.stopTest();
        
        System.assertEquals('expected', 'actual');
    }
}

まとめ

TS/JS経験者にとって、Apexは「構文は懐かしいJava風、でも実行モデル(ガバナ制限・非同期)が全く違う」言語だと感じました。
特に 「ループ内でクエリを投げない(バルク化)」 という意識は、N+1問題を避ける意味でWeb開発全般に通じる良い習慣になりそうです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?