Rest API Menggunakan Dart
Tags: dart, beginners, tutorial, programming
Published: 2022-10-01 13:59:38

Artikel ini adalah tutorial pembuatan REST API dengan menggunakan bahasa Dart. Tutorial ini akan terasa cukup panjang, dengan membahas pembuatan REST API dengan menggunakan Dart. Dari menerima http request dari client berupa test dan menerima file upload dari client ke aplikasi REST API server Dart kita.

## Persiapan

Sebelum lebih jauh, kita akan mempersiapkan terlebih dahulu apa saja yang kita perlukan dalam membuat REST API ini. Pada seri kali ini, kita memerlukan beberapa hal berikut ini:

1. Visual Studio Code (VSCode). Kita akan menggunakan VSCode untuk code editor kita. karena VSCode sudah menyediakan plug-in yang dapat mempermudah pengerjaan kita nantinya.

2. Dart SDK. Untuk membuat REST API dengan menggunakan DART, sudah tentu kita memerlukan bahasa Dart itu sendiri terinstall pada perangkat kia.

### Intalasi VS Code
Bagi yang belum menginstall aplikasi VSCode pada perangkat masing-masing, dapat mengunjungi [halaman website VS Code](https://code.visualstudio.com/). Berikut ini tampilan untuk halaman website VSCode.

![Halaman website VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj5cyho7jbhhoox224hu.png)

Pada halaman awal VSCode sudah disediakan link untuk men-download installer, dan VSCode sudah dapat memprediksi operating system yang kita gunakan, sehingga link download installer sudah menyesuaikan operating system yang kita gunakan, di sini saya menggunakan Windows dan pada halaman tersebut sudah disediakan installer VS Code untuk Windows.

Selanjutnya silahkan click link download tersebut, untuk men-download installer-nya. Setelah selesai download, lakukan instalasi seperti biasa.

### Instalasi Dart SDK
Setelah melakukan instalasi VSCode, selanjutnya yang kita perlukan adalah Dart SDK, untuk melakukan instalasi Dart SDK, sebenarnya sudah dijelaskan pada [halaman website dari Dart](https://dart.dev/get-dart), untuk operating system Windows disarankan menggunakan [Chocolatey](https://chocolatey.org/). Bagi yang belum menginstal package manager Chocolatey di perangkat masing-masing, dapat melakukan instalasi sesuai dengan halaman website dari Chocolatey pada panduan [instalasinya](https://chocolatey.org/install).

Setelah selesai melakukan instalasi Chocolatey, lakukan instalasi Dart dengan menggunakan perintah berikut ini:

``` powershell
choco install dart-sdk
```

Untuk update Dart SDK dapat menggunakan perintah berikut ini:

``` powershell
choco upgrade dart-sdk
```

Apabila tidak ada kesalahan maka sekarang Dart SDK sudah berhasil terinstal pada perangkat kita. untuk memeriksanya, kita dapat mengetikkan perintah berikut ini pada command prompt kita:

``` powershell
dart
```

Perintah di atas akan memunculkan informasi terkait cara penggunaan Dart pada terminal. Dengan demikian kita sudah mempersiapkan alat yang kita butuhkan untuk membuat REST API dengan menggunakan Dart.

### Addons VS Code
Penjelasan sebelumnya kenapa pada tutorial ini menggunakan VSCode adalah, karena VSCode menyediakan Addons yang dapat memudahkan pengerjaan kita dalam menggunakan Dart. Pada tutorial kali ini, kita akan menggunakan salah satu addons Dart pada VSCode.

![Addons Dart di VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r751pex4bggf7njd93ah.png)

Tambahkanlah addons tersebut pada aplikasi VSCode masing-masing.

---

## Aplikasi REST API Pertama

Setelah kita selesai melakukan persiapan, kita akan memulai membuat aplikasi REST API pertama kita. Pada VSCode kita, buka command palette dengan menekan `ctrl + shift + p`, Selanjutnya pada kolom palette ketikan `dart`, sehingga akan muncul pilihan `new project`, dan pilih `new project`.

![New Project Dart Dengan VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdtqwcfj7mde6snlqzx7.png)

Setelah memilih `new project`, akan muncul pilihan lagi dan pilih `Server App`.

![Pilihan Project Baru Dengan Dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7knalr61ljjg863539i7.png)

Setelah memilih `Server App`, akan muncul jendela di mana kita akan menempatkan project kita. Kita bebas untuk menempatkan project kita, pilih saja dimana kita akan menempatkannya. Setelah menentukan direktori untuk project kita, selanjutnya VSCode akan meminta nama project baru kita. Kita bebas menentukan nama project baru kita, pada tutorial kali ini, kita akan menggunakan nama default dari plugin, yaitu `dart_application_1`. Setelah itu, plugin akan melakukan generate data project baru kita secara otomatis. Setelah selesai, maka aplikasi REST API pertama kita sudah siap untuk digunakan.

![Project Pertama Dart REST API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3w5fs0yw81a6p5epav67.png)

## Running Aplikasi REST API Pertama Kita
Setelah kita membuat aplikasi pertama kita, kita akan mencoba untuk running aplikasi kita. Kita akan menjalankan aplikasi kita dengan menggunakan terminal command prompt atau powershell di Windows, untuk sistem operasi lain dapat menggunakan terminal pada sistem operasi masing-masing. Pertama kita perlu mengarahkan direktori terminal kita ke direktori project kita, atau cara paling mudah adalah kita membuka terminal melalui VSCode dengan menekan `ctrl + shift + c`, atau kita juga dapat menggunakan terminal bawaan dari VSCode, untuk menggunakannya, tekan `ctrl + j`. Untuk menjalankan aplikasi kita, kita perlu menjalankan file utama pada server Dart kita. Pada project default, file utama ada di `bin/server.dart`. untuk menjalankanya ketikan perintah berikut ini.

``` powershell
dart bin/server.dart
```

Apabila tidak ada masalah, maka pada terminal kita akan mencetak tulisan 'Server listening on port 8080' yang menandakan server kita telah berjalan di port 8080. Untuk memastikan kalau aplikasi kita berjalan dengan baik, kita dapat mencobanya pada browser kita dengan mengakses halaman localhost:8080. Apabila tidak ada kesalahan, maka alamat tersebut akan memunculkan kalimat 'Hello, World!'.

### Review Kode

Kita telah berhasil menjalankan aplikasi REST API pertama kita, selanjutnya kita perlu memahami struktur kode yang baru saja kita jalankan. Pada bundle aplikasi hasil generate dari dart pada langkah awal pada tutorial ini, kita akan banyak bekerja pada direktori `bin` di mana kita akan menempatkan semua file sistem pada direktori ini. Apabila diperhatikan, pada direktori bin terdapat file server.dart, ini merupakan file utama pada aplikasi kita.

``` dart
import 'dart:io';

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';

// Configure routes.
final _router = Router()
  ..get('/', _rootHandler)
  ..get('/echo/<message>', _echoHandler);

Response _rootHandler(Request req) {
  return Response.ok('Hello, World!\n');
}

Response _echoHandler(Request request) {
  final message = request.params['message'];
  return Response.ok('$message\n');
}

void main(List<String> args) async {
  // Use any available host or container IP (usually `0.0.0.0`).
  final ip = InternetAddress.anyIPv4;

  // Configure a pipeline that logs requests.
  final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router);

  // For running in containers, we respect the PORT environment variable.
  final port = int.parse(Platform.environment['PORT'] ?? '8080');
  final server = await serve(handler, ip, port);
  print('Server listening on port ${server.port}');
}
```

Baris kode di atas adalah file server.dart hasil generate dari dart. Pada generated awal, kita akan diberikan dua route sampel untuk bisa kita coba, perhatikan baris kode ini:

``` dart
final _router = Router()
  ..get('/', _rootHandler)
  ..get('/echo/<message>', _echoHandler);
```

Baris kode di atas menunjukkan bahwa dart membuatkan kita route sampel yaitu `'/'` dan `'/echo/<message>'`. Untuk mencobanya, kita gunakan aplikasi Postman, pastikan terlebih dahulu dart server kita berjalan dengan mengetikkan perintah di terminal `dart bin/server.dart`.

#### Menjalankan Route '/'

Untuk mencoba route '/', kita dapat mengetikkan url `localhost:8080` dengan method request `GET`.
 
![Akses Route](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wouxuftti9sw9am5atjy.png)

Maka kita akan mendapatkan hasil kembalian berupa string 'Hallo World'. Hasil kembalian ini kita dapatkan dari fungsi `_rootHandler` yang di gunakan pada route '/'. sehingga route untuk '/' sebenarnya menjalankan fungsi `_rootHandler`.

#### Menjalankan Route '/echo/\<message\>'

Untuk mencoba route '/echo/<message>', kita dapat mengetikkan url `localhost:8080` dengan method request `GET`.

![Akses Route](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i6qi517mf5s1wb0kir6x.png)

Maka kita akan mendapatkan hasil kembalian string 'hallo'. Hasil kembalian ini kita dapatkan dari fungsi `_echoHandler` yang digunakan pada route '/echo/<message>'. sehingga route untuk '/echo/<message>' sebenarnya menjalankan fungsi `_echoHandler`.

Seperti contoh di atas, pada penulisan route akan seperti ini '/echo/<message>' namun saat pemanggilan, parameter '<message>' kita ganti dengan string 'hallo'. Dalam REST API dengan menggunakan Dart, untuk melewatkan parameter dapat menggunakan notasi `<nama>` yang disebut dengan notasi diamond.

#### Kesimpulan

Konsep dasarnya adalah kita membuat sebuah fungsi dengan parameter object `Request`, untuk handle sebuah route. untuk menambahkan route lagi, kita hanya perlu membuat fungsi lagi untuk menanganinya, kemudian kita daftarkan pada `_router`. Kita akan mencobanya setelah ini.

---

## Method Request

Sebelumnya kita telah mencoba membuat REST API kita dengan menggunakan metode GET, selanjutnya kita kan akan mencoba membuat metode request POST dan bagaimana menangkap parameternya, kali ini kita akan mencoba membuat route `/greeting`. Pertama kita perlu membuat sebuah fungsi untuk menangani route `/greeting` ini.
kita akan membuat sebuah fungsi seperti berikut ini:

``` dart
Future<Response> _getPostJsonHandler(Request request) async {
  final content = await request.readAsString();
  var queryParams = Uri(query: content).queryParameters;
  return Response.ok(
    json.encode(queryParams),
    headers: {'Content-type': 'application/json'},
  );
}
```

Fungsi di atas adalah fungsi untuk menampilkan POST body. Selanjutnya, kita perlu membuat route baru pada `_router` dan mendaftarkan fungsi `_getPostJsonHandler`. pada fungsi `_router` kita rubah menjadi seperti berikut ini:

```
// Configure routes.
final _router = Router()
  ..get('/', _rootHandler)
  ..get('/echo/<message>', _echoHandler)
  ..post('/getpostdata', _getPostJsonHandler);
```

Setelah menambahkan route, maka kita dapat mencoba testing route baru kita. Untuk mencobanya, **kita perlu mematikan server kita terlebih dahulu dan memulainya kembali**. Berikut apabila kita coba menggunakan aplikasi Postman.

![Contoh Response Dengan Metode POST](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fjs9zcrb74l8xfhg0z4x.png)

Pada gambar di atas memperlihatkan bahwa hasil response berupa JSON. Langkah selanjutnya, kita dapat mengkreasikan dengan menimpan data tersebut ke database atau menyesuaikan kebutuhan kita nantinya.

---

## Media Upload

Setelah kita berhasil membuat plikasi REST API dengan menerima parameter berupa string baik dengan GET, POST, atau dengan metode request yang lain, selanjutnya kita akan mencoba untuk menerima parameter berupa file. Untuk melakukan ini, kita memerlukan  dependensi dari Dart yaitu [mime](https://pub.dev/packages/mime).

Bagi yang masih bingung untuk instalasinya, cukup ketikkan perintah di bawah ini pada terminal yang mengarah pada project kita.

``` powershell
dart pub add mime
```

### Fungsi Controller

Setelah selesai install dependensi `mime`, maka kita sudah memiliki semua yang dibutuhkan untuk menerima file. Seperti biasa, hal pertama yang perlu kita lakukan adalah dengan membuat fungsi untuk menangani route nantinya. Pada tutorial ini kita akan membuat fungsi untuk menangani upload seperti berikut ini:

``` dart
Future<Response> _upload(Request request) async {
  final contentType = request.headers['content-type'];
  if (contentType == null) {
    return Response(400, body: 'content-type tidak ditemukan');
  }

  final mediaType = MediaType.parse(contentType);
  if (mediaType.mimeType != 'multipart/form-data') {
    return Response(400, body: 'content-type tidak valid');
  }

  final boundary = mediaType.parameters['boundary'];
  if (boundary == null) {
    return Response(400, body: 'boundary tidak ditemukan');
  }

  final payload = request.read();
  final parts = MimeMultipartTransformer(boundary).bind(payload).where((part) {
    return part.headers['content-type'] == 'image/png';
  });

  final partsIterator = StreamIterator(parts);

  while (await partsIterator.moveNext()) {
    final part = partsIterator.current;

    final file = File('./uploads/testing.png'); // direktori file upload
    if (await file.exists()) {
      await file.delete();
    }
    final chunksIterator = StreamIterator(part);
    while (await chunksIterator.moveNext()) {
      final chunk = chunksIterator.current;
      await file.writeAsBytes(chunk, mode: FileMode.append);
    }

    return Response.ok('Upload Berhasil');
  }

  return Response.ok(payload);
}
```

Setelah membuat fungsi untuk menangani file upload seperti di atas, selanjutnya kita buat route untuk mengakses fungsi tersebut, pada variabel `_router` kita ubah menjadi seperti berikut ini:

```
final _router = Router()
  ..get('/', _rootHandler)
  ..get('/echo/<message>', _echoHandler)
  ..post('/getpostdata', _getPostJsonHandler)
  ..post('/upload', _upload);
```

Kita menambahkan router `/upload` pada variabel `_router` dengan mengakses fungsi `_upload`.

Setelah kita mempersiapkan fungsi dan juga route-nya, selanjutnya kita persiapkan direktori untuk menampung file yang akan kita upload, pada tutorial ini kita akan menempatkan semua file upload pada filter uploads pada direktori root. Kita akan membuat folder Uploads pada direktori root kita.

Setelah semua siap, selanjutnya kita dapat mulai mencoba untuk menjalankan sistem upload kita. Seperti biasa kita perlu me-restart server kita, kita matikan server kita, dan kita jalankan lagi. Setelah berhasil me-restart, kita jalankan route upload yang baru saja kita buat. Kali ini, kita akan mencoba menjalankannya dengan menggunakan aplikasi Postman.

![Contoh Request Upload Gambar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9cepo5z0z7dudbrwpxdm.png)

Gambar di atas adalah apabila kita mencoba akses route upload kita dengan menggunakan Postman. Untuk membuktikan apakah file sudah benar-benar berpindah, kita dapat memeriksa folder uploads pada direktori root kita. Sampai di sini, file tutorial dapat diambil pada repositori di bawah ini.

{% embed https://gitlab.com/caturdev/demo-rest-api-mengguanakan-dart.git %}

---

## Desain Module

Sesi terakhir pada tutorial kita kali ini adalah untuk membuat aplikasi kita setidaknya mudah untuk di-maintenance. Module aplikasi REST API yang terakhir kita buat, kita akan mencoba memisahkan antara startup file dengan module-module yang lain.

Pertama kita akan memisahkan fungsi `_uplaod` dan `_getPostJsonHandler` ke dalam file baru pada folder yang sama, yaitu folder dengan nama file baru `user.dart`. Selanjutnya, pada file server.dart kita juga akan menghapus fungsi `_rootHandler` dan `_echoHandler` beserta route dari fungsi-fungsi tersebut.

Pada file modul user.dart, fungsi `_uplaod` dan `_getPostJsonHandler` akan kita sesuaikan supaya dapat di terima pada startup file (server.dart) kita perlu membuat fungsi yang mengembalikan objek router.

``` dart
class UserApi {
  Router get router {
    final router = Router();



    return router;
  }
}
```

Pada file user.dart kita akan buat dulu class UserApi, nama kelas ini bebas, dapat disesuaikan sesuai keinginan. pada class UserApi kita membuat sebuah getter yang mengembalikan object `Router`. Selanjutnya kita akan memindahkan fungsi `_uplaod` dan `_getPostJsonHandler` sebelumnya pada blok getter ini. Perhatikan block code berikut ini.

``` dart
class UserApi {
  Router get router {
    final router = Router();

    router.post('/', (Request request) async {
      return Response.ok(
        headers: {'Content-type': 'application/json'},
        json.encode({
          'code': 200,
          'data': {'name': 'Catur Wicaksono', 'address': 'Jakarta'},
        }),
      );
    });

    router.post('/login', (Request request) async {
      final content = await request.readAsString();
      var queryParams = Uri(query: content).queryParameters;
      return Response.ok(
        headers: {'Content-type': 'application/json'},
        json.encode({
          'code': 200,
          'data': queryParams,
        }),
      );
    });

    router.post('/upload/picture', (Request request) async {
      final contentType = request.headers['content-type'];
      if (contentType == null) {
        return Response(400, body: 'content-type tidak ditemukan');
      }

      final mediaType = MediaType.parse(contentType);
      if (mediaType.mimeType != 'multipart/form-data') {
        return Response(400, body: 'content-type tidak valid');
      }

      final boundary = mediaType.parameters['boundary'];
      if (boundary == null) {
        return Response(400, body: 'boundary tidak ditemukan');
      }

      final payload = request.read();
      final parts =
          MimeMultipartTransformer(boundary).bind(payload).where((part) {
        return part.headers['content-type'] == 'image/png';
      });

      final partsIterator = StreamIterator(parts);

      while (await partsIterator.moveNext()) {
        final part = partsIterator.current;

        final file = File('./uploads/testing.png'); // direktori file upload
        if (await file.exists()) {
          await file.delete();
        }
        final chunksIterator = StreamIterator(part);
        while (await chunksIterator.moveNext()) {
          final chunk = chunksIterator.current;
          await file.writeAsBytes(chunk, mode: FileMode.append);
        }

        return Response.ok('Upload Berhasil');
      }

      return Response.ok(payload);
    });

    return router;
  }
}

```

Pada baris kode di atas, kita mengimplementasikan fungsi `_uplaod` dan `_getPostJsonHandler` sebelumnya, namun dengan cara yang agak berbeda. Perhatikan, nama kedua fungsi menjadi anonymous function yang langsung ditambahkan setelah inisialisasi router. fungsi `_uplaod` kita tempatkan pada route `/upload/picture`, dan `_getPostJsonHandler` pada route `/login`. Untuk referensi kita akan menambahkan satu route lagi dengam metode `get`, dengan baris kode seperti di bawah ini.

``` dart
router.post('/', (Request request) async {
  return Response.ok(
    headers: {'Content-type': 'application/json'},
    json.encode({
      'code': 200,
      'data': {'name': 'Catur Wicaksono', 'address': 'Jakarta'},
    }),
  );
});
```

Setelah selesai membuat module user, selanjutnya kita gabungkan module user ke startup file kita, server.dart. Pada file server.dart, kita tambahkan module pada variable router sebelumnya seperti berikut ini.

```
final _router = Router()..mount('/user', UserApi().router);
```

Untuk menambahkan module, kita gunakan fungsi mount seperti di atas, parameter pertama adalah user, dan parameter kedua adalah object module yang akan ditambahkan.

Dengan demikian cara mengakses route agak sedikit berbeda dari sebelumnya, misal pada module user terdapat route `/login`, karena module ini ada pada poute `/user` pada startup file, maka cara mengaksesnya dengan menggunakan router pada startup file terlebih dahulu dan dilanjutkan dengan route module, menjadi `/user/login`. Kita akan mencobanya dengan menggunakan Postman.

![Akses Route Baru Dengan Postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ei1vdbsbj87ecq8zaran.png)

---

Untuk source code lengkap untuk tutorial ini, dapat diakses pada repository di bawah ini.

{% embed https://gitlab.com/caturdev/demo-rest-api-mengguanakan-dart.git %}

---

## Epilog

Kita cukupkan materi tutorial ini sampai di sini. Sistem yang kita bangun pada tutorial ini masih sangat dasar, sehingga masih perlu banyak pengembangan lagi. Terima kasih telah mengikuti tutorial yang cukup panjang ini.