API操作 (Flutter Roadmap Working with APIs)

## はじめに
Flutterを網羅的に学習するにあたって[Roadmap](https://roadmap.sh/flutter)を使って学習を進めることにしました。

この記事では、Flutter初学者やこれからFlutterを学習し始める方に向けて、**APIの操作**についてまとめています。

RoadmapはFlutterだけでなく、他の言語やスキルのロードマップも提供されており、何から学習して良いか分からないと悩んでいる方にとって有用なサイトになっています。
ぜひ[Roadmap](https://roadmap.sh/)を利用して学習してみてください。

## Roadmapとは

簡潔に言えば、Roadmap.shは学習者にとっての**ガイドブック**であり、**学習の方向性を提供する学習ロードマップサイト**です。

初心者から上級者まで、ステップバイステップでスキルを習得するための情報が提供されています。

学習の進め方が分かりやすく示されているだけでなく、個々の項目に参考資料やリソースへのリンクも提供されているので、学習者は目標を設定し、自分自身のペースで学習を進めることができます。

## Working with APIs
FlutterロードマップWorking with APIsでは以下の4つのサイトが紹介されています。興味のある方はぜひお読みください。

- Using Google APIs: https://dart.dev/guides/google-apis
- How to work with APIs in Flutter?: https://www.youtube.com/watch?v=uVo7HDWDUEQ
- JSON and serialization: https://docs.flutter.dev/data-and-backend/serialization/json
- Using JSON: https://dart.dev/guides/json

## APIとは
**API**とは「**Application Programming Interface**」の略称です。

**インターフェース**とは、異なる２つのモノをつなぐという意味を持ちます。例えば、USBも「パソコン」と「周辺機器」をつなぐものですので、**インターフェース**の一つです。

つまり、APIとは、「アプリケーション、ソフトウェア」と「プログラム」をつなぐもの、という意味になります。

### APIの使用用途
**API**を使用することによって、ソフトウェアやアプリケーションなどの一部を**外部に向けて公開する**ことができるようになります。

例えば、異なるサービス間で**認証機能を共有**したり、片方のサービスからデータを取り込んで、**取り込んだデータをもう片方のサービスで利用する**など使い方は多岐に渡ります。

では、**APIを公開する**とは、どういうことでしょうか？
簡単に言うと、ソフトウェアに**外部とやりとりするための窓口**を作り、**外部との連携ができる状態**にするということです。

何をして欲しいのかをAPIに要求(**リクエスト**)して、ルールに基づいて要求が書かれていれば、APIを提供するサービスやソフトウェアが処理をして、その結果を返して(**レスポンス**)くれます。

当たり前ですが、APIは決められた仕様以外のことはできません、APIの提供者が**公開する段階でサービスの利用範囲を決めている**ので、決められたこと以外はできないようになっています。

もっと詳しく学びたい方は、わかりやすい記事があったので、以下のリンクからぜひ読んでみてください。

https://data.wingarc.com/what-is-api-16084

https://www.sbbit.jp/article/cont1/62752

## JSONとは
**JSON（JavaScript Object Notation）は、軽量なデータ交換フォーマットで、人間にも機械にも読みやすい形式の記述方法**のことです。**JSONは属性-値ペアの集合**で表現し、多くのプログラミング言語で扱えるため、**データのやり取りに**広く使われています。

JSON形式は以下のように`キー`と`値`からデータが表現されます。

```json
{
  "A さん": {
    "国語": "84 点",
    "数学": "79 点",
  },
  "B さん": {
    "国語": "89 点",
    "数学": "83 点",
  }
}
```

JSONは次のデータ型に対応しています。

- **文字列**: 文字列はダブルクォーテーション(`“`)、バックスラッシュ(`\`)以外の文字であればなんでも使用できます。
- **数値**: 数値はダブルクォーテーションで囲みません。ダブルクォーテーションで囲むと文字列扱いになるので注意が必要です。
- **null**: nullは全て小文字で指定します。
- **bool値**: bool値（true or false）の指定も可能です。
- **オブジェクト**: オブジェクトを使いたい場合は`{}`を使います。オブジェクトの中にオブジェクトを入れることもできます。これを「**ネストする**」と言います。
- **配列**: 配列を使いたい場合は`[]`を使います。配列内の要素はカンマで区切ることで複数入力できます。

## HTTPメソッド

### GETメソッド
**GETメソッドはサーバーからデータを取得する**ために使用されます。読み取り専用のメソッドなので、このメソッドを呼ぶことで、データが変更される恐れはありません。

### POSTメソッド
**POSTメソッドは新しいデータを作成する**のに使用されます。POSTメソッドを実行することによって、データをサーバーに送信し、新しいリソースを作成することができます。新しく作成されるとID(新しいリソースのURI[^1])が割り当てられて、自動的に既存のAPIに関連付けられます。

### PUTメソッド
**PUTメソッドは、既存のリソースを更新する**ために使用されます。特定のリソースを更新する場合、更新しようとしている新しいリソースを含むリクエストを該当のURIに送信して、更新をかけます。

つまり、**PUTではリソース全体を送信**します。例えば、元あるデータが以下だとします。

```json
{
    "title": "sample title",
    "content": "foobarbaz hoge fuga",
    "author": "sample tarou"
}
```
上記のリソースがサーバーにあったとして、以下のデータをPUTメソッドで送信すると

```json
{
    "title": "new title"
}
```
以下のように`content`と`author`は削除されます。リソース全体を送信して、全体が更新されるということです。
```json
{
    "title": "new title"
}
```

### PATCHメソッド
**PATCHメソッドは、既存のリソースを更新する**ために使用されます。PUTメソッドは全体を更新するのに対し、**PATCHメソッドは部分更新**をします。例えば、記事のタイトルだけを更新したり、記事の本文だけを更新したりなどです。

### DELETEメソッド
**DELETEメソッドは、URIで指定された特定のリソースを削除する**ために使用されます。



## Flutter API実装
アプリケーション開発で**API通信**は避けては通れない分野になっています。ここではFlutterでAPIを通信を行う際、どういった手順で導入していくのかを解説していきます。

今回は以下のサンプルアプリを作成していきます。
![スクリーンショット 0005-10-15 1.15.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368449/1ecb7991-873c-e96d-cf11-1e20df366e36.png)


まずFlutterでAPI通信を行うには、`http`パッケージという外部パッケージを活用します。
API通信をサポートするパッケージは他にもたくさんあるので、興味のある方はぜひ調べてみてください。

https://pub.dev/packages/http

### パッケージの導入

1. パッケージを追加します。
```dart
flutter pub add http
```
２. 使用するファイル内で`import`します。
```dart
import 'package:http/http.dart' as http;
```
### JSONPlaceholder
[**JSONPlaceholder**](https://jsonplaceholder.typicode.com/
)は、テスト用途のAPIサービスで、開発者がAPIの動作をテストし、APIリクエストを練習するためのサイトとなっています。今回は投稿リソースを利用したいと思います。

https://jsonplaceholder.typicode.com/posts

### サンプルコード　

```dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<dynamic> _posts = [];

  @override
  void initState() {
    super.initState();
    fetchPosts();
  }

  Future<void> fetchPosts() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
    if (response.statusCode == 200) {
      final List<dynamic> data = jsonDecode(response.body);
      setState(() {
        _posts = data;
      });
    } else {
      throw Exception('Failed to load data');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('API Example'),
        ),
        body: ListView.builder(
          itemCount: _posts.length,
          itemBuilder: (context, index) {
            final post = _posts[index];
            return ListTile(
              title: Text(post['title']),
              subtitle: Text(post['body']),
            );
          },
        ),
      ),
    );
  }
}
```

### サンプルコード解説
```dart: fetchPostsメソッド
  Future<void> fetchPosts() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts')); 
    if (response.statusCode == 200) {
      final List<dynamic> data = jsonDecode(response.body);
      setState(() {
        _posts = data;
      });
    } else {
      throw Exception('Failed to load data');
    }
  }
```
1. 非同期関数で、外部のAPIからデータを取得します。 ここでは`http.get`メソッドを使用して指定したURLからデータを取得します。
2. HTTP応答ステータスコードが200（成功）であるかを確認し、成功した場合はデータをデコード[^2]して `_posts`リストに設定します。エラーが発生した場合は例外をスローします。

```dart: ListView.builder ウィジェット:
 body: ListView.builder(
   itemCount: _posts.length,
   itemBuilder: (context, index) {
     final post = _posts[index];
     return ListTile(
       title: Text(post['title']),
       subtitle: Text(post['body']),
     );
   },
 ),
```

リストビューの`itemCount`プロパティでは、表示するリストアイテムの数を`_posts`リストの数に設定します。

`itemBuilder`プロパティは、リストアイテムを動的に構築するために使用されます。`_posts`リスト内の各要素にアクセスし、`ListTile`ウィジェットとして返します。

`ListTile`ウィジェットプロパティは、リスト内の各項目を表示します。`title`プロパティには`title`フィールドのデータが、`subtitle`プロパティには`body`フィールドのデータが表示されます。

![スクリーンショット 0005-10-15 1.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368449/bc3ecdfb-aeda-c179-6187-7f895d77d2e3.png)



## 参考資料

https://qiita.com/haru-qiita/items/b17665feed6556bd4e67

https://zenn.dev/heyhey1028/books/flutter-basics/viewer/api_calls

https://blog.kikagaku.co.jp/json

https://products.sint.co.jp/topsic/blog/json

https://medium.com/@9cv9official/what-are-get-post-put-patch-delete-a-walkthrough-with-javascripts-fetch-api-17be31755d28

https://progtext.net/tech/http-methods/

[^1]: **URI**
URL(ホームページの住所に相当する情報)とURN（ホームページなどの「名前」に相当する情報）の総称です。つまりURLとURNはURIの一部ということになります。[こちら](https://wa3.i-3-i.info/word1815.html)にわかりやすい記事があったので、是非読んでみてください。 

[^2]: **エンコード / デコード**
エンコードとはデータを特定の形式や規則に従って変換することです。
デコードとはエンコードされたデータを元の形式に戻すことを言います。
[こちら](https://wa3.i-3-i.info/word136.html)にわかりやすい記事があったので、是非読んでみてください。