author Ahmad Muhardian

Tutorial Membuat RESTful API dengan Slim Framework


Pernahkah kamu berpikir:

Bagaimana aplikasi (mobile) Facebook dengan Web Facebook memiliki data yang sama.

Apa yang kita lihat melalui aplikasi mobile akan ada juga di webnya.

Bagaimana cara mereka berbagi data?

Jawabannya: dengan RESTful API atau Webservice.

Tugas RESTful API adalah menyediakan data—biasanya dalam bentuk JSON—untuk dikonsumsi oleh aplikasi lainnya (client).

Ilustrasi RESTful API

Kita bisa membuat RESTful API dengan beberapa bahasa seperti Python, Java, PHP, Javascript, Ruby, dll.

Pada tutorial ini, kita akan menggunakan Slim Framework.

Catatan: Tutorial ini untuk Slim Versi 3

Slim Framework adalah salah satu mikro framework PHP untuk membuat aplikasi web.

Slim biasanya digunakan untuk membuat aplikasi-aplikasi kecil dan lebih banyak dipakai untuk membuat RESTful API atau web service.

Apa bedanya mikro framework dengan framework yang lain?

Mikro framework memiliki fitur yang lebih sedikit dibandingkan dengan framework yang lain seperti CodeIgniter, Laravel, Yii, dan sebagainya.

Bisa dibilang, fiturnya kurang lengkap.

Namun bukan berarti tidak berguna.

Justru yang sia-sia itu, punya banyak fitur tapi tidak digunakan.

Bagaimana Konsep Kerja Slim Framework?

Ketika kita belajar CI, Yii, dan Laravel, kita akan sering mendengar konsep MVC.

MVC adalah sebuah design pattern yang mengelompokkan beberapa class ke dalam tiga kategori, yaitu: **M****odel View, dan Controller.

Namun, Slim tidak menggunakan konsep MVC, meskipun kita bisa membuatnya menjadi MVC.

Slim memiliki konsep yang sangat sederhana…

Slim hanya menerima HTTP Request, lalu menjalankan sebuah fungsi dan mengembalikan sebuah HTTP response.

Itu saja?

Ya benar, itu saja.

Untuk lebih jelasnya, mari kita langsung praktek…

Menyiapkan Project Slim Framework

Untuk membuat project Slim Framework, kita membutuhkan composer.

Pastikan composer sudah terinstal di komputermu.

Silakan Baca: Cara Install dan Menggunakan Composer.

Setelah itu, ketik perintah berikut untuk membuat project slim:

composer create-project slim/slim-skeleton:3.1.8 aplikasi-slim -vvv

Keterangan:

  • slim-api adalah nama direktori project yang akan dibuat.
  • argumen -vvv berfungsi untuk menampilkan detail proses instalasi.

Tunggulah instalasi sampai selesai.

Instalasi Selesai

Setelah itu, buka direktori slim-api dengan teks editor.

Saya membukanya menggunakan teks editor VS Code.

Project Slim di teks editor VSCode

Sekarang mari kita coba jalankan aplikasinya.

Buka terminal di VS Code (tekan tombol Ctrl+`), lalu ketik perintah composer start untuk menjalankan server PHP.

Menjalankan server PHP

Setelah itu, coba buka http://localhost:8080, maka akan tampil seperti ini:

Membuka Aplikasi Slim

Selamat! 🎉

Slim Framework sudah siap. Selanjutnya tinggal coding saja nih.

Tapi tunggu dulu.

Sebelum melakukan itu, kita siapkan dulu database-nya.

Menyiapkan Database Aplikasi

Pada aplikasi ini, kita akan menggunakan database MySQL dengan koneksi PDO.

Silakan buat database baru dan dua buah tabel bernama books dan api_users.

Buka Phpmyadmin, lalu buat database bernama tokobuku.

Membuat database baru

Setelah itu buat tabel books dengan 5 field:

  1. book_id untuk menyimpan id buku;
  2. title untuk menyimpan judul buku;
  3. author untuk menyimpan nama pengarang;
  4. sinopsis untuk menyimpan teks sinopsis;
  5. cover untuk menyimpan nama file sampul buku.

Membuat tabel books

Kode SQL:

CREATE TABLE books (
    book_id INT NOT NULL AUTO_INCREMENT, 
    title VARCHAR(255) NOT NULL, 
    author VARCHAR(255) NOT NULL, 
    sinopsis TEXT NOT NULL, 
    cover VARCHAR(255) NOT NULL DEFAULT 'default_cover.png', 
    PRIMARY KEY (`book_id`)
) ENGINE = InnoDB;

Selanjutnya buat tabel api_users dengan field:

  1. email untuk menyimpan email user. Email bersifat unik, jadi 1 API key hanya dimiliki oleh satu user.
  2. api_key untuk menyimpan API key user. API Key bersifat unik.
  3. hit untuk menyimpan berapa kali api diakses oleh user. Biasanya digunakan untuk membatasi akses.

Membuat tabel api_users

Kode SQL:

CREATE TABLE api_users (
    email VARCHAR(255) NOT NULL, 
    api_key VARCHAR(255) NOT NULL, 
    hit INT NOT NULL, 
    PRIMARY KEY (`email`), 
    UNIQUE (`api_key`)
) ENGINE = InnoDB;

Setelah itu, isi dengan beberapa data:

Menambahkan User API

Kode SQL:

INSERT INTO `api_users` (`email`, `api_key`, `hit`) VALUES ('[email protected]', '123', '0');
INSERT INTO `books` (`book_id`, `title`, `author`, `sinopsis`, `cover`) VALUES (NULL, 'Belajar Pemrograman PHP dari Nol Hingga Mahir', 'Muhar Dian', 'Buku ini membahas tentang tutorial step by step pemrograman php dari awal hingga membuat aplikasi web.', 'default_cover.png');

Sekarang baru kita bisa mulai coding

Membuat Koneksi Database

Hal pertama yang harus kita buat adalah koneksi ke database.

Koneksi database yang akan kita gunakan adalah PDO.

Mari kita buka file src/settings.php lalu tambahkan konfigurasi untuk database seperti ini:

// Database Settings
'db' => [
    'host' => 'localhost',
    'user' => 'root',
    'pass' => 'kopi',
    'dbname' => 'tokobuku',
    'driver' => 'mysql'
]

Silakan sesuaikan username dan password sesuai dengan konfigurasi MySQL di komputermu.

Konfigurasi Database Slim Framework

Selanjutnya buka file src/dependencies.php lalu tambahkan kode berikut:

// database
$container['db'] = function ($c){
    $settings = $c->get('settings')['db'];
    $server = $settings['driver'].":host=".$settings['host'].";dbname=".$settings['dbname'];
    $conn = new PDO($server, $settings["user"], $settings["pass"]);  
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    return $conn;
};

Kode di atas merupakan pendaftaran komponen db (objek PDO) ke dalam aplikasi. Nanti kita bisa akses di dalam aplikasi Slim dengan $this->db.

Membuat koneksi database

Membuat koneksi selesai.

Selanjutnya, kita akan membuat rute aplikasi.

Membuat Rute Aplikasi

Rute aplikasi adalah rute URL yang akan kita sediakan untuk diakses oleh aplikasi client.

Adapun rute yang akan kita buat adalah sebagai berikut.

RuteMetodeParameterKeterangan
/books/GETkeyUntuk mengambil semua buku
/books/1GETkeyUntuk menampilkan 1 buku berdasarkan id
/books/searchGETkey, keywordUntuk mencari buku
/booksPOSTkeyUntuk menambahkan buku baru
/books/1PUTkeyUntuk mengedit data buku
/books/1DELETEkeyUntuk menghapus buku

Setiap rute membutuhkan parameter key. Parameter ini bisa kita berikan melalui query string.

Contoh:

/books/search?key=123&keyword=bukuphp

Nanti kita akan membuat fungsi untuk validasi API Key di bagian akhir.

Sekarang buka file src/routes.php untuk membuat rute aplikasi.

Rute GET /books/

Rute ini diakses dengan metode GET. Pada rute ini kita akan mengirimkan semua data buku yang ada pada tabel books.

Silakan tambahkan kode seperti ini pada file src/routes.php.

$app->get("/books/", function (Request $request, Response $response){
    $sql = "SELECT * FROM books";
    $stmt = $this->db->prepare($sql);
    $stmt->execute();
    $result = $stmt->fetchAll();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

Maka akan menjadi seperti ini:

Membuat rute aplikasi

Setelah itu, tambahkan juga untuk rute-rute yang lainnya.

Rute GET /books/1

Rute ini untuk mengambil 1 data buku berdasarkan ID-nya.

$app->get("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $sql = "SELECT * FROM books WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    $stmt->execute([":id" => $id]);
    $result = $stmt->fetch();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

Rute GET /books/search

Rute ini untuk pencarian buku berdasarkan judul, sinopsis, dan nama pengarang.

$app->get("/books/search/", function (Request $request, Response $response, $args){
    $keyword = $request->getQueryParam("keyword");
    $sql = "SELECT * FROM books WHERE title LIKE '%$keyword%' OR sinopsis LIKE '%$keyword%' OR author LIKE '%$keyword%'";
    $stmt = $this->db->prepare($sql);
    $stmt->execute();
    $result = $stmt->fetchAll();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

Rute POST /books

Rute ini diakses dengan metode POST. Rute ini untuk menambah data buku baru.

$app->post("/books/", function (Request $request, Response $response){

    $new_book = $request->getParsedBody();

    $sql = "INSERT INTO books (title, author, sinopsis) VALUE (:title, :author, :sinopsis)";
    $stmt = $this->db->prepare($sql);

    $data = [
        ":title" => $new_book["title"],
        ":author" => $new_book["author"],
        ":sinopsis" => $new_book["sinopsis"]
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});

Rute PUT /books/1

Rute ini diakses dengan metode PUT. Rute ini bertugas untuk mengubah data buku berdasarkan ID.

$app->put("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $new_book = $request->getParsedBody();
    $sql = "UPDATE books SET title=:title, author=:author, sinopsis=:sinopsis WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    
    $data = [
        ":id" => $id,
        ":title" => $new_book["title"],
        ":author" => $new_book["author"],
        ":sinopsis" => $new_book["sinopsis"]
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});

Rute DELETE /books/1

Rute ini diakses dengan metode DELETE. Rute ini untuk menghapus data buku:

$app->delete("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $sql = "DELETE FROM books WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    
    $data = [
        ":id" => $id
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});

Selesai.

Semua rute sudah kita buat.

Sehingga kode lengkap untuk file src/routes.php akan menjadi seperti ini:

<?php

use Slim\Http\Request;
use Slim\Http\Response;

// Routes
$app->get('/[{name}]', function (Request $request, Response $response, array $args) {
    // Sample log message
    $this->logger->info("Slim-Skeleton '/' route");

    // Render index view
    return $this->renderer->render($response, 'index.phtml', $args);
});

$app->get("/books/", function (Request $request, Response $response){
    $sql = "SELECT * FROM books";
    $stmt = $this->db->prepare($sql);
    $stmt->execute();
    $result = $stmt->fetchAll();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

$app->get("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $sql = "SELECT * FROM books WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    $stmt->execute([":id" => $id]);
    $result = $stmt->fetch();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

$app->get("/books/search/", function (Request $request, Response $response, $args){
    $keyword = $request->getQueryParam("keyword");
    $sql = "SELECT * FROM books WHERE title LIKE '%$keyword%' OR sinopsis LIKE '%$keyword%' OR author LIKE '%$keyword%'";
    $stmt = $this->db->prepare($sql);
    $stmt->execute([":id" => $id]);
    $result = $stmt->fetchAll();
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

$app->post("/books/", function (Request $request, Response $response){

    $new_book = $request->getParsedBody();

    $sql = "INSERT INTO books (title, author, sinopsis) VALUE (:title, :author, :sinopsis)";
    $stmt = $this->db->prepare($sql);

    $data = [
        ":title" => $new_book["title"],
        ":author" => $new_book["author"],
        ":sinopsis" => $new_book["sinopsis"]
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});


$app->put("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $new_book = $request->getParsedBody();
    $sql = "UPDATE books SET title=:title, author=:author, sinopsis=:sinopsis WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    
    $data = [
        ":id" => $id,
        ":title" => $new_book["title"],
        ":author" => $new_book["author"],
        ":sinopsis" => $new_book["sinopsis"]
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});


$app->delete("/books/{id}", function (Request $request, Response $response, $args){
    $id = $args["id"];
    $sql = "DELETE FROM books WHERE book_id=:id";
    $stmt = $this->db->prepare($sql);
    
    $data = [
        ":id" => $id
    ];

    if($stmt->execute($data))
       return $response->withJson(["status" => "success", "data" => "1"], 200);
    
    return $response->withJson(["status" => "failed", "data" => "0"], 200);
});

Middleware untuk Validasi API Key

Middleware adalah fungsi yang akan dieksekusi di setiap ada request yang masuk ke aplikasi.

Konsep Middleware

Middleware biasanya kita gunakan untuk validasi API Key, membuat login, mencatat logs, dan lain-lain.

Silakan buka file src/middleware.php lalu tambahkan kode berikut.

<?php
// Application middleware

// e.g: $app->add(new \Slim\Csrf\Guard);

// middleware untuk validasi api key
$app->add(function ($request, $response, $next) {
    
    $key = $request->getQueryParam("key");

    if(!isset($key)){
        return $response->withJson(["status" => "API Key required"], 401);
    }
    
    $sql = "SELECT * FROM api_users WHERE api_key=:api_key";
    $stmt = $this->db->prepare($sql);
    $stmt->execute([":api_key" => $key]);
    
    if($stmt->rowCount() > 0){
        $result = $stmt->fetch();
        if($key == $result["api_key"]){
        
            // update hit
            $sql = "UPDATE api_users SET hit=hit+1 WHERE api_key=:api_key";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([":api_key" => $key]);
            
            return $response = $next($request, $response);
        }
    }

    return $response->withJson(["status" => "Unauthorized"], 401);

});

Secara garis besar, maksud dari kode di atas adalah:

  1. Cek parameter key;
  2. Ambil data di database berdasarkan parameter key;
  3. bandingkan parameter key dengan api_key yang tersimpan di database;
  4. kalau benar, tampilkan data yang diminta dan tambah hit.

Menambahkan Middleware

Setelah itu coba buka http://localhost:8080, maka kita akan diminta API Key.

Mengakses API tanpa API Key

Sekarang coba tambahkan parameter ?key=123.

Mengakses API dengan API Key

Berhasil!

Selanjutnya, coba bila API key-nya salah.

Mengakses API dengan API Key yang salah

Catatan Tambahan:

Menuliskan middleware seperti contoh di atas, akan membuat semua rute membutuhkan API Key. Nah, apabila kita ingin menggunakan pengecekan API key pada rute tertentu, maka kita harus menambahkan middleware pada rute tersebut.

Contoh:

// membuat middleware
$cekAPIKey = function($request, $response, $next){
     // ini middleware untuk cek API key
};

// menambahkan middleware ke route
$app->get('/books', function ($request, $response) {
       // ...
})->add(cekAPIKey);

Atau bisa juga ditambahkan ke route group.

Contoh:

$app->group('/api', function () use ($app) {
     $app->get('/books', function ($request, $response) {
       // ...
     });
    $app->get('/books/{id}', function ($request, $response) {
        // ...
     });
})->add(cekAPIKey);

Uji Coba Aplikasi dengan Postman

Postman adalah aplikasi yang sering digunakan untuk menguji coba RESTful API. Dengan Postman, kita bisa memodifikasi metode untuk mengakses API.

Pertama kita coba mengakses rute /books/ dengan metode GET:

Mengambil semua data buku

Selanjutnya, coba mengakses rute /books/1 dengan metode GET:

Mengambil satu data buku

Berikutnya, coba untuk melakukan pencarian dengan metode GET:

Mengambil semua data buku

Sekarang, kita coba menambahkan sebuah data.

Menambahkan data buku

Pada demo di atas, kita mengirimkan data berupa JSON ke webservice. Sebenarnya, kita juga bisa mengirim dalam format yang lain seperti form-data.

Slim akan otomatis mengubahnya menjadi array asosiatif, karena kita menggunakan fungsi $this->getParsedBody() untuk mengambilnya.

Hasilnya:

Hasil Menambahkan data buku

Oya, untuk cover, kita tidak masukkan. Karena caranya berbeda. Kita harus melakukan upload dulu baru menyimpan nama gambar cover-nya.

Silakan baca: Cara Upload File ke Webservice dengan Slim Framework

Berikutnya kita coba untuk edit data:

Mengedit data buku

Hasilnya:

Mengedit data buku

Terakhir kita coba untuk menghapus data:

Menghapus data buku

Akhir Kata…

Sekian dulu tutorial ini.

Aplikasi yang kita buat, baru menggunakan 2 tabel saja. Bagaimana kalau ada banyak tabel?

Kodenya tentu akan semakin kompleks.

Kalau kita menuliskan semuanya di dalam src/routes.php, maka akan memakan banyak baris dan akan sulit terbaca.

Di sinilah waktu yang tepat untuk menggunakan design pattern semacam MVC. Supaya kodenya mudah dikelola.

Berikutnya, silakan sempurnakan untuk upload file dan pembuatan API Key.

Gunakan fungsi untuk meng-generate token atau API Key.

Selamat bereksperimen…