Tutorial sebelumnya saya pernah membahas Slim framework untuk membuat sebuah RESTFul API dengan authorize Json Web Token (JWT). Slim framework merupakan sebuah mricoframework yang berguna untuk membuat web service. Berbeda dengan framework lainnya seperti codeigniter, laravel atau lainnya. Slim framework memiliki fitur yang lebih sedikit akan tetapi tidak mengurangi dalam fungsi dan penggunaanya.
Pernahkah teman-teman menggunakan layanan api google atau api RajaOngkir yang menggunakan api key untuk request datanya? Api key sendiri berguna untuk mengamankan pertukaran data antar server dan aplikasi client. Tutorial kali ini kita akan belajar membuat sebuah RESTFul Api yang memerlukan api key untuk request data menggunakan Slim framework.
RESTFul Api Slim Framework dengan Api key
Pertama-tama kita buat database mysql, dalam kasus ini kita menggunakan contoh database toko dengan dua tabel user dan produk. tabel user berguna untuk menyimpan data user dan api key. Hanya api key yang sudah terdaftar dalam tabel user yang dibolehkan request data.
CREATE DATABASE toko;
USE toko;
CREATE TABLE `user` (
`IdUser` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`Username` varchar(255),
`NamaLengkap` varchar(255),
`Email` varchar(255),
`NoHp` varchar(255),
`Password` varchar(255),
`ApiKey` varchar(255)
);
CREATE TABLE `produk` (
`IdProduk` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`KodeProduk` varchar(25),
`NamaProduk` varchar(255),
`HargaJual` int(11),
`Stok` int(11)
);
Insert data kedalam tabel.
INSERT INTO `produk` VALUES ('1', '8999999273361', 'WALLS CONELLO HZL MOCHACINO', '25000', '100');
INSERT INTO `produk` VALUES ('2', '8999999273378', 'CONERLLO CHOCOLUV 135', '25000', '100');
INSERT INTO `produk` VALUES ('3', '8999999275556', 'WALLS NINJA JELLY', '25000', '100');
INSERT INTO `produk` VALUES ('4', '8999999275563', 'NINJA EAGLE SURP', '25000', '100');
INSERT INTO `produk` VALUES ('5', '8999999275570', 'RAINBOW PEAK', '25000', '100');
INSERT INTO `produk` VALUES ('6', '8999999275587', 'PADDLE POP CHOKO KICK', '25000', '100');
INSERT INTO `produk` VALUES ('7', '8999999278458', 'CONELO DISC VAN', '25000', '100');
INSERT INTO `user` VALUES ('1', 'user', 'User', 'user@user.com', '0822222222222', 'user1234', 'TVXYQc8WVp1jJHLpVRLXvs4tuoXHVSg0');
Buat projek slim framework baru dengan composer melalui terminal.
composer create-project slim/slim-skeleton:3.1.8 SlimRestApi
Setelah selesai install projek slim baru, buka folder SlimRestApi, kemudian ketikkan perintah berikut ini untuk menjalankan server PHP.
php -S localhost:8080 -t public public/index.php
Ketikkan http://localhost:8080/ di browser maka akan tampil seperti dibawah ini.
Buka file settings.php yang berada di directory slimrestapi/src/settings.php untuk menambahkan pengaturan database.
// setting database toko
'db' => [
'host' => 'localhost',
'user' => 'root',
'pass' => '',
'dbname' => 'toko',
'driver' => 'mysql'
]
Tambahkan juga kode berikut ini di dalam file dependencies.php yang berada di directory slimrestapi/src/dependencies.php.
// database toko
$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;
};
Setelah selesai pengaturan database, selanjutnya kita akan membuat rute aplikasi. Rute aplikasi ini merupakan url yang akan di akses oleh aplikasi client. Terdapat beberapa rute yang akan kita buat. buka file routes.php yang berada di directory src/routes.php.
Rute produk(get)
Rute ini digunakan untuk mengambil semua data produk dengan method Get.
//ambil semua data produk
$app->get("/produk", function (Request $request, Response $response, array $args){
$sql = "SELECT * FROM produk";
$stmt = $this->db->prepare($sql);
$stmt->execute();
$mainCount=$stmt->rowCount();
$result = $stmt->fetchAll();
if($mainCount==0) {
return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200);
}
return $response->withJson(["status" => "success", "data" => $result], 200);
});
Rute produk/1(get)
Rute ini digunakan untuk mengambil data produk berdasarkan id produk dengan method Get.
//ambil data produk berdasarkan id produk
$app->get("/produk/{IdProduk}", function (Request $request, Response $response, array $args){
$IdProduk = trim(strip_tags($args["IdProduk"]));
$sql = "SELECT * FROM produk WHERE IdProduk=:IdProduk";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("IdProduk", $IdProduk);
$stmt->execute();
$mainCount=$stmt->rowCount();
$result = $stmt->fetchObject();
if($mainCount==0) {
return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200);
}
return $response->withJson(["status" => "success", "data" => $result], 200);
});
Rute produk(post)
Rute ini digunakan untuk menambahkan data produk baru dengan method Post.
//menyimpan data produk
$app->post('/produk', function (Request $request, Response $response, array $args) {
$input = $request->getParsedBody();
$KodeProduk=trim(strip_tags($input['KodeProduk']));
$NamaProduk=trim(strip_tags($input['NamaProduk']));
$HargaJual=trim(strip_tags($input['HargaJual']));
$Stok=trim(strip_tags($input['Stok']));
$sql = "INSERT INTO produk(KodeProduk, NamaProduk, HargaJual, Stok)
VALUES(:KodeProduk, :NamaProduk, :HargaJual, :Stok)";
$sth = $this->db->prepare($sql);
$sth->bindParam("KodeProduk", $KodeProduk);
$sth->bindParam("NamaProduk", $NamaProduk);
$sth->bindParam("HargaJual", $HargaJual);
$sth->bindParam("Stok", $Stok);
$StatusInsert=$sth->execute();
if($StatusInsert){
return $this->response->withJson(['status' => 'success','data'=>'success insert produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error insert produk.'],200);
}
});
Rute produk/1(put)
Rute ini digunakan untuk mengubah data produk berdasarkan id produk dengan method put.
//update data produk berdasarkan id produk
$app->put('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
$input = $request->getParsedBody();
$IdProduk = trim(strip_tags($args["IdProduk"]));
$KodeProduk=trim(strip_tags($input['KodeProduk']));
$NamaProduk=trim(strip_tags($input['NamaProduk']));
$HargaJual=trim(strip_tags($input['HargaJual']));
$Stok=trim(strip_tags($input['Stok']));
$sql = "UPDATE produk SET KodeProduk=:KodeProduk, NamaProduk=:NamaProduk, HargaJual=:HargaJual, Stok=:Stok WHERE IdProduk=:IdProduk";
$sth = $this->db->prepare($sql);
$sth->bindParam("IdProduk", $IdProduk);
$sth->bindParam("KodeProduk", $KodeProduk);
$sth->bindParam("NamaProduk", $NamaProduk);
$sth->bindParam("HargaJual", $HargaJual);
$sth->bindParam("Stok", $Stok);
$StatusUpdate=$sth->execute();
if($StatusUpdate){
return $this->response->withJson(['status' => 'success','data'=>'success update produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error update produk.'],200);
}
});
Rute produk/1(delete)
Rute ini digunakan untuk menghapus data produk berdasarkan id produk dengan method delete.
//delete data produk berdasarkan id produk
$app->delete('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
$IdProduk = trim(strip_tags($args["IdProduk"]));
$sql = "DELETE FROM produk WHERE IdProduk=:IdProduk";
$sth = $this->db->prepare($sql);
$sth->bindParam("IdProduk", $IdProduk);
$StatusDelete=$sth->execute();
if($StatusDelete){
return $this->response->withJson(['status' => 'success','data'=>'success delete produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error delete produk.'],200);
}
});
Kode lengkap untuk routes.php seperti dibawah ini.
<?php
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
return function (App $app) {
//ambil semua data produk
$app->get("/produk", function (Request $request, Response $response, array $args){
$sql = "SELECT * FROM produk";
$stmt = $this->db->prepare($sql);
$stmt->execute();
$mainCount=$stmt->rowCount();
$result = $stmt->fetchAll();
if($mainCount==0) {
return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200);
}
return $response->withJson(["status" => "success", "data" => $result], 200);
});
//ambil data produk berdasarkan id produk
$app->get("/produk/{IdProduk}", function (Request $request, Response $response, array $args){
$IdProduk = trim(strip_tags($args["IdProduk"]));
$sql = "SELECT * FROM produk WHERE IdProduk=:IdProduk";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("IdProduk", $IdProduk);
$stmt->execute();
$mainCount=$stmt->rowCount();
$result = $stmt->fetchObject();
if($mainCount==0) {
return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200);
}
return $response->withJson(["status" => "success", "data" => $result], 200);
});
//menyimpan data produk
$app->post('/produk', function (Request $request, Response $response, array $args) {
$input = $request->getParsedBody();
$KodeProduk=trim(strip_tags($input['KodeProduk']));
$NamaProduk=trim(strip_tags($input['NamaProduk']));
$HargaJual=trim(strip_tags($input['HargaJual']));
$Stok=trim(strip_tags($input['Stok']));
$sql = "INSERT INTO produk(KodeProduk, NamaProduk, HargaJual, Stok)
VALUES(:KodeProduk, :NamaProduk, :HargaJual, :Stok)";
$sth = $this->db->prepare($sql);
$sth->bindParam("KodeProduk", $KodeProduk);
$sth->bindParam("NamaProduk", $NamaProduk);
$sth->bindParam("HargaJual", $HargaJual);
$sth->bindParam("Stok", $Stok);
$StatusInsert=$sth->execute();
if($StatusInsert){
return $this->response->withJson(['status' => 'success','data'=>'success insert produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error insert produk.'],200);
}
});
//update data produk berdasarkan id produk
$app->put('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
$input = $request->getParsedBody();
$IdProduk = trim(strip_tags($args["IdProduk"]));
$KodeProduk=trim(strip_tags($input['KodeProduk']));
$NamaProduk=trim(strip_tags($input['NamaProduk']));
$HargaJual=trim(strip_tags($input['HargaJual']));
$Stok=trim(strip_tags($input['Stok']));
$sql = "UPDATE produk SET KodeProduk=:KodeProduk, NamaProduk=:NamaProduk, HargaJual=:HargaJual, Stok=:Stok WHERE IdProduk=:IdProduk";
$sth = $this->db->prepare($sql);
$sth->bindParam("IdProduk", $IdProduk);
$sth->bindParam("KodeProduk", $KodeProduk);
$sth->bindParam("NamaProduk", $NamaProduk);
$sth->bindParam("HargaJual", $HargaJual);
$sth->bindParam("Stok", $Stok);
$StatusUpdate=$sth->execute();
if($StatusUpdate){
return $this->response->withJson(['status' => 'success','data'=>'success update produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error update produk.'],200);
}
});
//delete data produk berdasarkan id produk
$app->delete('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
$IdProduk = trim(strip_tags($args["IdProduk"]));
$sql = "DELETE FROM produk WHERE IdProduk=:IdProduk";
$sth = $this->db->prepare($sql);
$sth->bindParam("IdProduk", $IdProduk);
$StatusDelete=$sth->execute();
if($StatusDelete){
return $this->response->withJson(['status' => 'success','data'=>'success delete produk.'],200);
} else {
return $this->response->withJson(['status' => 'error','data'=>'error delete produk.'],200);
}
});
};
Setiap rute diatas bisa di akses tanpa api key karena kita belum menambahkan validasi api key di middleware. Untuk menambahkan validasi kita buka middleware.php yang berada di src/middleware.php. Dengan menambahkan validasi api key di dalam middleware.php, semua rute yang akan di akses harus disertai headers apikey.
<?php
use Slim\App;
return function (App $app) {
// e.g: $app->add(new \Slim\Csrf\Guard);
//validasi api key
$app->add(function ($request, $response, $next) {
$error = null;
if($request->getHeader('ApiKey')){
$key = $request->getHeader('ApiKey')[0];
try{
$sql = "SELECT * FROM user where ApiKey = :ApiKey";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("ApiKey", $key);
$stmt->execute();
$result = $stmt->fetchObject();
if(!$result){
//Api key tidak ada
$error = 'Unidentified key';
}
}catch(PDOException $e){
$error = array('error'=>$e->getMessage());
}
}else{
$error = 'Missing key';
}
if($error){
return $response->withJson(array('error'=>$error),401);
}
return $next($request, $response);
});
};
Uji coba menggunakan Postman
untuk menguji coba rest api kita menggunakan aplikasi postman.
rute untuk mengambil semua data produk menggunakan method get dengan headers api key seperti dibawah ini. Api key harus terdaftar dalam tabel user agar dapat menampilkan data produk.
Jika api key tidak ada, maka akan menampilkan error 401 seperti gambar dibawah ini.
Jika api key yang di gunakan tidak terdaftar dalam tabel maka akan menampilkan error 401 seperti dibawah ini.
Rute untuk menampilkan data produk berdasarkan id dengan method get dan headers apikey.
Rute untuk menyimpan data produk baru dengan method post dan headers apikey.
Rute untuk mengubah data produk berdasarkan id produk dengan method put dan headers apikey.
Terakhir rute untuk menghapus data produk berdasarkan id produk dengan method delete.
Teman-teman juga dapat menyesuaikan rute mana yang akan kita validasi dengan api key. Kita dapat mengubah kode validasi api key di dalam routes.php. Pertama-tama hapus validasi api key yang berada di middleware.php tadi kemudian tambahkan validasi api key nya di routes.php. Contoh penggunaan:
//validasi api key
$cekAPIKey = function($request, $response, $next){
$error = null;
if($request->getHeader('ApiKey')){
$key = $request->getHeader('ApiKey')[0];
try{
$sql = "SELECT * FROM user where ApiKey = :ApiKey";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("ApiKey", $key);
$stmt->execute();
$result = $stmt->fetchObject();
if(!$result){
//Api key tidak ada
$error = 'Unidentified key';
}
}catch(PDOException $e){
$error = array('error'=>$e->getMessage());
}
}else{
$error = 'Missing key';
}
if($error){
return $response->withJson(array('error'=>$error),401);
}
return $next($request, $response);
};
//ambil semua data produk
$app->get("/produk", function (Request $request, Response $response, array $args){
$sql = "SELECT * FROM produk";
$stmt = $this->db->prepare($sql);
$stmt->execute();
$mainCount=$stmt->rowCount();
$result = $stmt->fetchAll();
if($mainCount==0) {
return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200);
}
return $response->withJson(["status" => "success", "data" => $result], 200);
})->add($cekAPIKey);