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:
Visual Studio Code (VSCode). Kita akan menggunakan VSCode untuk code editor kita. karena VSCode sudah menyediakan plug-in yang dapat mempermudah pengerjaan kita nantinya.
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. Berikut ini tampilan untuk halaman website VSCode.
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, untuk operating system Windows disarankan menggunakan Chocolatey. Bagi yang belum menginstal package manager Chocolatey di perangkat masing-masing, dapat melakukan instalasi sesuai dengan halaman website dari Chocolatey pada panduan instalasinya.
Setelah selesai melakukan instalasi Chocolatey, lakukan instalasi Dart dengan menggunakan perintah berikut ini:
choco install dart-sdk
Untuk update Dart SDK dapat menggunakan perintah berikut ini:
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:
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.
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
.
Setelah memilih new project
, akan muncul pilihan lagi dan pilih Server App
.
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.
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.
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.
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:
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
.
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/', kita dapat mengetikkan url localhost:8080
dengan method request GET
.
Maka kita akan mendapatkan hasil kembalian string 'hallo'. Hasil kembalian ini kita dapatkan dari fungsi _echoHandler
yang digunakan pada route '/echo/'. sehingga route untuk '/echo/' sebenarnya menjalankan fungsi _echoHandler
.
Seperti contoh di atas, pada penulisan route akan seperti ini '/echo/' namun saat pemanggilan, parameter '' 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:
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.
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.
Bagi yang masih bingung untuk instalasinya, cukup ketikkan perintah di bawah ini pada terminal yang mengarah pada project kita.
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:
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.
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.
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.
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.
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.
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.
Untuk source code lengkap untuk tutorial ini, dapat diakses pada repository di bawah ini.
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.
Top comments (0)