Professional Documents
Culture Documents
Materi API
Materi API
Materi API
PART I
What Is Swagger?
Swagger is an open-source framework for designing and describing
APIs. Swagger’s journey started in 2010 when it was developed by Reverb
Technologies (formerly called Wordnik) to solve the need for keeping the API
design and documentation in sync.
Fast forward 6 years and Swagger has become the de-facto standard
for designing and describing RESTful APIs used by millions of developers and
organizations for developing their APIs, be it internal or client facing.
swagger: '2.0'
info:
version: 1.0.0
title: Simple example API
description: An API to illustrate Swagger
paths:
/list:
get:
description: Returns a list of stuff
responses:
200:
description: Successful response
The syntax above will make sense once we finish this walkthrough.
Why Swagger?
There are a lot of factors that contributed to Swagger’s meteoric adoption for
building RESTful APIs. We have listed the most important ones as to why use
Swagger for designing APIs. Design and document APIs simultaneously, thus
keeping the blueprint and documentation in sync.
Artist name
Artist genre
Number of albums published under the label
Artist username
The API will let consumers obtain the list of artists stored in the database and
add a new artist to the database.
All Swagger defined APIs start with the swagger: 2.0 declaration. The info
object is then specified with additional, required metadata like the version and
title. The description is optional.
The base URL is what any application or end user will call in order to
consume the API. In this case, it is https://example.io/v1, which is defined by
the schemes, host and basePath objects respectively.
We can also add a basic authentication which only lets authorized users
consume the API. For more advanced security, see here.
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths: {}
Path Items
The path items are the endpoints of your API under which you can specify
HTTP verbs for manipulating the resources in the desired manner. These
endpoints are relative to the base URL (which in our case is
http://example.io/v1). In this example, we have listed the /artists endpoint
under which is the GET method.
swagger: '2.0'
info:
version: 0.0.0
title: Simple API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
# ----- Added lines -----------------------------------
-----
paths:
/artists:
get:
description: Returns a list of artists
# ---- /Added lines ----------------------------------
Responses
The GET method, under the artists endpoint, lets the consumer of the
API obtain the details of a list of artists from the http://example.io/v1 database.
Every response would need at least one HTTP status code to describe
the kind of responses a consumer is likely to expect. The description gives
details on what the responses of the API would be. In our sample code, we
have specified 200, which is a successful client request, while 400 is an
unsuccessful request. You can find more information about HTTP status
codes here. A successful response will return the artist name, genre,
username and albums recorded. An unsuccessful request is described under
the 400 HTTP code, with a corresponding error message detailing why the
response is invalid.
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
# ----- Added lines -----------------------------
-----------
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ---- /Added lines -----------------------------
See here for more information on responses.
Parameters
RESTful parameters specify the variable part of the resource a user works
with. If you want to get some advanced information on parameters, read here.
Query parameters
Query parameters are the most common type of parameters. They appear at
the end of a URL following a question mark. Query parameters are optional
and non unique, so they can be specified multiple times in the URL. It is a
non-hierarchical component of the URL.
In our example, it would make sense to let the client limit the information
required instead of returning the entire list of artists from the database, which
would lead to unnecessary load on the server. The client could describe the
page number (offset) and the amount of information on the page (limit), for
example:
GET http://example.com/v1/artists?limit=20&offset=3
These variables are defined under the parameters object in the Swagger
definition. Thus, the definition would now look as follows –
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
# ----- Added lines -----------------------------
-----------
parameters:
- name: limit
in: query
type: integer
description: Limits the number of items on a
page
- name: offset
in: query
type: integer
description: Specifies the page number of the
artists to be displayed
# ---- /Added lines -----------------------------
-----------
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
Body parameters
Body parameters are stated in the body of the request. A key principle of such
a parameter is that resources are sent in the message body. Body parameters
are great for POST and PUT requests. It is not advisable to use these
parameters in GET and DELETE actions.
For this API, let’s add the ability for a user to post an artist to our database.
This would be under the /artists resource. Note the inclusion of a parameter
which is specified in the body of the request from the in:body description.
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
parameters:
- name: limit
in: query
type: integer
description: Limits the number of items on a
page
- name: offset
in: query
type: integer
description: Specifies the page number of the
artists to be displayed
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ----- Added lines -------------------------------
---------
post:
description: Lets a user post a new artist
parameters:
- name: artist
in: body
description: creates a new artist in our
database
schema:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
responses:
200:
description: Successfully created a new artist
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ---- /Added lines -------------------------------
Path parameters
The path parameters can be used to isolate a specific component of
the data that the client is working with, for example,
http://example.io/v1/{userrole}. Path parameters are part of the hierarchical
component of the URL, and are thus stacked sequentially.
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
parameters:
- name: limit
in: query
type: integer
description: Limits the number of items on a
page
- name: offset
in: query
type: integer
description: Specifies the page number of the
artists to be displayed
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
post:
description: Lets a user post a new artist
parameters:
- name: artist
in: body
description: creates a new artist in our
database
schema:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
responses:
200:
description: Successfully created a new artist
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ----- Added lines ---------------------------------
-------
/artists/{username}:
get:
description: Obtain information about an artist
from his or her unique username
parameters:
- name: username
in: path
type: string
required: true
responses:
200:
description: Successfully returned an artist
schema:
type: object
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ---- /Added lines ------------------------------
If you followed through till here, then congratulation! You have just
designed a simple API for a record label!
Reusable components
What we have just described are just 2 endpoints and 3 actions. This
was 83 lines of API definition, and the spec will only get longer as the API
gets bigger. One of the things you may notice in the spec we have so far is
that we have the same Artist schema (artist name, genre, username and
albums published) that gets repeated in various 200 and 400 responses.
Bigger APIs would involve rewriting and reusing a lot of the same specs, so it
would be a tedious task writing a more complex API.
Definitions
Responses
Parameters
Path Items can also be reused with the help of applications like SwaggerHub,
which store your reusable components to be referenced across multiple APIs.
Definitions
The global definitions section can contain various data models consumed and
returned by the API. Here is how we can use definitions to store the schema
for an HTTP 200 OK response:
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
parameters:
- name: limit
in: query
type: integer
description: Limits the number of items on a
page
- name: offset
in: query
type: integer
description: Specifies the page number of the
artists to be displayed
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
# ----- Added line ----------------------
------------------
$ref: '#/definitions/Artist'
# ---- /Added line ----------------------
------------------
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
post:
description: Lets a user post a new artist
parameters:
- name: artist
in: body
description: creates a new artist in our
database
schema:
# ----- Added line ------------------------
----------------
$ref: '#/definitions/Artist'
# ---- /Added line ------------------------
----------------
responses:
200:
description: Successfully created a new artist
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
/artists/{username}:
get:
description: Obtain information about an artist
from his or her unique username
parameters:
- name: username
in: path
type: string
required: true
responses:
200:
description: Successfully returned an artist
schema:
type: object
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
400:
description: Invalid request
schema:
type: object
properties:
message:
type: string
# ----- Added lines -----------------------------------
definitions:
Artist:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
# ---- /Added lines -----------------------------------
The spec is not only shorter, but anytime a new endpoint with the same
schema is needed, the designer does not need to spend time writing the piece.
See here for more information on Definitions.
In the API definitiion below, we have, under the reusable parameters, the
PageLimit and PageNumber parameters, which define the query parameters
we used under the /artists endpoint. They are referenced under the /artists
endpoint.
We also have a 400Error reusable response defined, which specifies the 400
response we used under all the endpoints, which are then appropriately
referenced.
swagger: '2.0'
info:
version: 1.0.0
title: Simple Artist API
description: A simple API to understand the Swagger
Specification in greater detail
schemes:
- https
host: example.io
basePath: /v1
securityDefinitions:
UserSecurity:
type: basic
security:
- UserSecurity: []
paths:
/artists:
get:
description: Returns a list of artists
parameters:
- $ref: '#/parameters/PageLimit'
- $ref: '#/parameters/PageNumber'
responses:
200:
description: Successfully returned a list of
artists
schema:
type: array
items:
$ref: '#/definitions/Artist'
400:
# ----- Added line --------------------------
--------------
$ref: '#/responses/400Error'
# ---- /Added line --------------------------
--------------
post:
description: Lets a user post a new artist
parameters:
- name: artist
in: body
description: creates a new artist in our
database
schema:
$ref: '#/definitions/Artist'
responses:
200:
description: Successfully created a new artist
400:
# ----- Added line --------------------------
--------------
$ref: '#/responses/400Error'
# ---- /Added line --------------------------
--------------
/artists/{username}:
get:
description: Obtain information about an artist
from his or her unique username
parameters:
- name: username
in: path
type: string
required: true
responses:
200:
description: Successfully returned an artist
schema:
type: object
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
400:
# ----- Added line --------------------------
--------------
$ref: '#/responses/400Error'
# ---- /Added line --------------------------
--------------
definitions:
Artist:
type: object
required:
- username
properties:
artist_name:
type: string
artist_genre:
type: string
albums_recorded:
type: integer
username:
type: string
# ----- Added lines -----------------------------------
-----
parameters:
PageLimit:
name: limit
in: query
type: integer
description: Limits the number of items on a page
PageNumber:
name: offset
in: query
type: integer
description: Specifies the page number of the artists
to be displayed
responses:
400Error:
description: Invalid request
schema:
type: object
properties:
message:
type: string
Summary
We have successfully designed a RESTful API which exposes the artists
present in the database of a record label! We have only covered the basics of
Swagger, as the specification can be anything you want it to be (mostly). If
you want to become a Swagger expert, you can refer to the official
specification and to the syntax guide, or try out our certification courses!
PART II
If you have a positive feedback about our work, please let us know. If
there's a section in this tutorial that is confusing or hard to
understand, we consider it as a problem. Please let us know as well.
REST API can be used by any application that can connect to the
internet. If data from an application can be created, read, updated or
deleted using another application, it usually means a REST API is used.
Also, please note that this PHP REST API is not yet in its nal form. We
still have some work to do with .htaccess for better URLs and more.
But one thing is for sure, this source code is good enough and works for
our JavaScript tutorials.
The code below shows the database credentials and a method to get a
database connection using PDO. If you're not yet familiar with PDO,
please learn from our PHP OOP CRUD Tutorial
[https://www.codeofaninja.com/2014/06/php-object-oriented-crud-
example-oop.html] rst.
<?php
class Database{
// specify your own database credentials
private $host = "localhost";
private $db_name = "api_db";
private $username = "root";
private $password = "";
public $conn;
// get the database connection
public function getConnection(){
$this->conn = null;
try{
$this->conn = new PDO("mysql:host=" . $this->
$this->conn->exec("set names utf8");
}catch(PDOException $exception){
echo "Connection error: " . $exception->getMe
}
return $this->conn;
}
}
?>
The code below shows a class named Product with several of its
properties. It also shows a constructor method that will accept the
database connection.
<?php
class Product{
// database connection and table name
private $conn;
private $table_name = "products";
// object properties
public $id;
public $name;
public $description;
public $price;
public $category_id;
public $category_name;
public $created;
// constructor with $db as database connection
public function __construct($db){
$this->conn = $db;
}
}
?>
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
// database connection will be here
If there are records found, we loop through it using the while loop, add
each record to the $products_arr array, set a 200 OK response code
and show it to the user in JSON format.
// query products
$stmt = $product->read();
$num = $stmt->rowCount();
// check if more than 0 record found
if($num>0){
// products array
$products_arr=array();
$products_arr["records"]=array();
// retrieve our table contents
// fetch() is faster than fetchAll()
// http://stackoverflow.com/questions/2770630/pdofetc
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
// extract row
// this will make $row['name'] to
// just $name only
extract($row);
$product_item=array(
"id" => $id,
"name" => $name,
"description" => html_entity_decode($descript
"price" => $price,
"category_id" => $category_id,
"category_name" => $category_name
);
array_push($products_arr["records"], $product_ite
}
// set response code - 200 OK
http_response_code(200);
// show products data in json format
echo json_encode($products_arr);
}
// no products found will be here
We used the read() method in the previous section but it does not
exist yet in the Product class. We need to add this read() method.
The code below shows the query to get records from the database.
To make sure you added it correctly, place the code before the last
closing curly brace.
// read products
function read(){
// select all query
$query = "SELECT
c.name as category_name, p.id, p.name, p.
FROM
" . $this->table_name . " p
LEFT JOIN
categories c
ON p.category_id = c.id
ORDER BY
p.created DESC";
// prepare query statement
$stmt = $this->conn->prepare($query);
// execute query
$stmt->execute();
return $stmt;
}
If the $num variable has a value of zero or negative, it means there are
no records returned from the database. We need to tell the user about
this.
On the code below, we set the response code to 404 - Not found
and a message that says No products found.
else{
// set response code - 404 Not found
http_response_code(404);
// tell the user no products found
echo json_encode(
array("message" => "No products found.")
);
}
4.7 Output
You need to use POSTMAN [https://www.getpostman.com/] to test our
API. Download your version of POSTMAN here
[https://www.getpostman.com/apps] .
http://localhost/api/product/read.php [http://localhost/a
[https://i1.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/read-products-200.jpg?ssl=1]
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Acces
// get database connection
include_once '../config/database.php';
// instantiate product object
include_once '../objects/product.php';
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);
// get posted data
$data = json_decode(file_get_contents("php://input"));
// make sure data is not empty
if(
!empty($data->name) &&
!empty($data->price) &&
!empty($data->description) &&
!empty($data->category_id)
){
// set product property values
$product->name = $data->name;
$product->price = $data->price;
$product->description = $data->description;
$product->category_id = $data->category_id;
$product->created = date('Y-m-d H:i:s');
// create the product
if($product->create()){
// set response code - 201 created
http_response_code(201);
// tell the user
echo json_encode(array("message" => "Product was
}
// if unable to create the product, tell the user
else{
// set response code - 503 service unavailable
http_response_code(503);
// tell the user
echo json_encode(array("message" => "Unable to cr
}
}
// tell the user data is incomplete
else{
// set response code - 400 bad request
http_response_code(400);
// tell the user
echo json_encode(array("message" => "Unable to create
}
?>
The previous section will not work without the following code inside
the Product (objects/product.php) class.
// create product
function create(){
// query to insert record
$query = "INSERT INTO
" . $this->table_name . "
SET
name=:name, price=:price, description=:de
// prepare query
$stmt = $this->conn->prepare($query);
// sanitize
$this->name=htmlspecialchars(strip_tags($this->name))
$this->price=htmlspecialchars(strip_tags($this->price
$this->description=htmlspecialchars(strip_tags($this-
$this->category_id=htmlspecialchars(strip_tags($this-
$this->created=htmlspecialchars(strip_tags($this->cre
// bind values
$stmt->bindParam(":name", $this->name);
$stmt->bindParam(":price", $this->price);
$stmt->bindParam(":description", $this->description);
$stmt->bindParam(":category_id", $this->category_id);
$stmt->bindParam(":created", $this->created);
// execute query
if($stmt->execute()){
return true;
}
return false;
}
5.3 Output
Click "raw".
{
"name" : "Amazing Pillow 2.0",
"price" : "199",
"description" : "The best pillow for amazing programm
"category_id" : 2,
"created" : "2018-06-01 00:35:07"
}
[https://i1.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/create-product-201.jpg?ssl=1]
If the system is unable to create the product, it should look like this:
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/create-product-503.jpg?ssl=1]
If the sent data is incomplete, for example, it is missing the price data,
the output should look like this:
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Credentials: true");
header('Content-Type: application/json');
// include database and object files
include_once '../config/database.php';
include_once '../objects/product.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare product object
$product = new Product($db);
// set ID property of record to read
$product->id = isset($_GET['id']) ? $_GET['id'] : die();
// read the details of product to be edited
$product->readOne();
if($product->name!=null){
// create array
$product_arr = array(
"id" => $product->id,
"name" => $product->name,
"description" => $product->description,
"price" => $product->price,
"category_id" => $product->category_id,
"category_name" => $product->category_name
);
// set response code - 200 OK
http_response_code(200);
// make it json format
echo json_encode($product_arr);
}
else{
// set response code - 404 Not found
http_response_code(404);
// tell the user product does not exist
echo json_encode(array("message" => "Product does not
}
?>
The previous section will not work without the following code inside
the Product class.
6.3 Output
First, we will test for a product that exists. Open POSTMAN. Enter the
following as the request URL. Click the blue "Send" button.
http://localhost/api/product/read_one.php?id=60 [http://l
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/read-one-200.jpg?ssl=1]
Next, we will test for a product that does not exist. Enter the following
as the request URL. Click the blue "Send" button.
http://localhost/api/product/read_one.php?id=999 [http://
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/read-one-404.jpg?ssl=1]
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Acces
// include database and object files
include_once '../config/database.php';
include_once '../objects/product.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare product object
$product = new Product($db);
// get id of product to be edited
$data = json_decode(file_get_contents("php://input"));
// set ID property of product to be edited
$product->id = $data->id;
// set product property values
$product->name = $data->name;
$product->price = $data->price;
$product->description = $data->description;
$product->category_id = $data->category_id;
// update the product
if($product->update()){
// set response code - 200 ok
http_response_code(200);
// tell the user
echo json_encode(array("message" => "Product was upda
}
// if unable to update the product, tell the user
else{
// set response code - 503 service unavailable
http_response_code(503);
// tell the user
echo json_encode(array("message" => "Unable to update
}
?>
The previous section will not work without the following code inside
the Product class.
7.3 Output
Open POSTMAN. Enter the following as the request URL.
http://localhost/api/product/update.php [http://localhost
Click "raw".
{
"id" : "106",
"name" : "Amazing Pillow 3.0",
"price" : "255",
"description" : "The best pillow for amazing programm
"category_id" : 2,
"created" : "2018-08-01 00:35:07"
}
If you specify an ID that does not exist in the database, it might still say
that the product was updated. It does not update anything on the
database but the query was executed successfully without any syntax
errors.
To prevent this, you need an extra validation where you check if an ID
exists in the database. This feature is not yet part of our tutorial.
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/update-product-200.jpg?ssl=1]
If the system fails to update the product, the output will look like this:
[https://i1.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/update-product-503.jpg?ssl=1]
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Acces
// include database and object file
include_once '../config/database.php';
include_once '../objects/product.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare product object
$product = new Product($db);
// get product id
$data = json_decode(file_get_contents("php://input"));
// set product id to be deleted
$product->id = $data->id;
// delete the product
if($product->delete()){
// set response code - 200 ok
http_response_code(200);
// tell the user
echo json_encode(array("message" => "Product was dele
}
// if unable to delete the product
else{
// set response code - 503 service unavailable
http_response_code(503);
// tell the user
echo json_encode(array("message" => "Unable to delete
}
?>
The previous section will not work without the following code inside
the Product class.
8.3 Output
Open POSTMAN. Enter the following as the request URL.
http://localhost/api/product/delete.php [http://localhost
Click "raw".
{
"id" : "106"
}
If the system fails to delete the product, the output will look like this:
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/delete-503-1.jpg?ssl=1]
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
// include database and object files
include_once '../config/core.php';
include_once '../config/database.php';
include_once '../objects/product.php';
// instantiate database and product object
$database = new Database();
$db = $database->getConnection();
// initialize object
$product = new Product($db);
// get keywords
$keywords=isset($_GET["s"]) ? $_GET["s"] : "";
// query products
$stmt = $product->search($keywords);
$num = $stmt->rowCount();
// check if more than 0 record found
if($num>0){
// products array
$products_arr=array();
$products_arr["records"]=array();
// retrieve our table contents
// fetch() is faster than fetchAll()
// http://stackoverflow.com/questions/2770630/pdofetc
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
// extract row
// this will make $row['name'] to
// just $name only
extract($row);
$product_item=array(
"id" => $id,
"name" => $name,
"description" => html_entity_decode($descript
"price" => $price,
"category_id" => $category_id,
"category_name" => $category_name
);
array_push($products_arr["records"], $product_ite
}
// set response code - 200 OK
http_response_code(200);
// show products data
echo json_encode($products_arr);
}
else{
// set response code - 404 Not found
http_response_code(404);
// tell the user no products found
echo json_encode(
array("message" => "No products found.")
);
}
?>
// search products
function search($keywords){
// select all query
$query = "SELECT
c.name as category_name, p.id, p.name, p.
FROM
" . $this->table_name . " p
LEFT JOIN
categories c
ON p.category_id = c.id
WHERE
p.name LIKE ? OR p.description LIKE ? OR
ORDER BY
p.created DESC";
// prepare query statement
$stmt = $this->conn->prepare($query);
// sanitize
$keywords=htmlspecialchars(strip_tags($keywords));
$keywords = "%{$keywords}%";
// bind
$stmt->bindParam(1, $keywords);
$stmt->bindParam(2, $keywords);
$stmt->bindParam(3, $keywords);
// execute query
$stmt->execute();
return $stmt;
}
9.3 Output
http://localhost/api/product/search.php?s=shirt [http://l
If there are no products found, the output will look like this:
[https://i2.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/search-404.jpg?ssl=1]
This le holds our core con guration like the home URL and pagination
variables.
<?php
// show error reporting
ini_set('display_errors', 1);
error_reporting(E_ALL);
// home page url
$home_url="http://localhost/api/ [http://localhost/api/]
// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;
// set number of records per page
$records_per_page = 5;
// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records
?>
Still in the product class ( product.php le), add the following method.
The total rows are needed to build the pagination array. It is included in
the 'paging' computation.
<?php
class Utilities{
public function getPaging($page, $total_rows, $record
// paging array
$paging_arr=array();
// button for first page
$paging_arr["first"] = $page>1 ? "{$page_url}page
// count all products in the database to calculat
$total_pages = ceil($total_rows / $records_per_pa
// range of links to show
$range = 2;
// display links to 'range of pages' around 'curr
$initial_num = $page - $range;
$condition_limit_num = ($page + $range) + 1;
$paging_arr['pages']=array();
$page_count=0;
for($x=$initial_num; $x<$condition_limit_num; $x+
// be sure '$x is greater than 0' AND 'less t
if(($x > 0) && ($x <= $total_pages)){
$paging_arr['pages'][$page_count]["page"]
$paging_arr['pages'][$page_count]["url"]=
$paging_arr['pages'][$page_count]["curren
$page_count++;
}
}
// button for last page
$paging_arr["last"] = $page<$total_pages ? "{$pag
// json format
return $paging_arr;
}
}
?>
10.6 Output
Open POSTMAN. Enter the following as the request URL.
http://localhost/api/product/read_paging.php [http://loca
[https://i0.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/read-paging-200.jpg?ssl=1]
If there are no products found, the output will look like this:
[https://i1.wp.com/www.codeofaninja.com/wp-
content/uploads/2017/02/read-paging-404.jpg?ssl=1]
<?php
class Category{
// database connection and table name
private $conn;
private $table_name = "categories";
// object properties
public $id;
public $name;
public $description;
public $created;
public function __construct($db){
$this->conn = $db;
}
// used by select drop-down list
public function readAll(){
//select all data
$query = "SELECT
id, name, description
FROM
" . $this->table_name . "
ORDER BY
name";
$stmt = $this->conn->prepare( $query );
$stmt->execute();
return $stmt;
}
}
?>
<?php
// required header
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
// include database and object files
include_once '../config/database.php';
include_once '../objects/category.php';
// instantiate database and category object
$database = new Database();
$db = $database->getConnection();
// initialize object
$category = new Category($db);
// query categorys
$stmt = $category->read();
$num = $stmt->rowCount();
// check if more than 0 record found
if($num>0){
// products array
$categories_arr=array();
$categories_arr["records"]=array();
// retrieve our table contents
// fetch() is faster than fetchAll()
// http://stackoverflow.com/questions/2770630/pdofetc
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
// extract row
// this will make $row['name'] to
// just $name only
extract($row);
$category_item=array(
"id" => $id,
"name" => $name,
"description" => html_entity_decode($descript
);
array_push($categories_arr["records"], $category_
}
// set response code - 200 OK
http_response_code(200);
// show categories data in json format
echo json_encode($categories_arr);
}
else{
// set response code - 404 Not found
http_response_code(404);
// tell the user no categories found
echo json_encode(
array("message" => "No categories found.")
);
}
?>
The previous section's code will not work without the following code
inside the category.php le.
1. Creating and publishing an API via the Publisher Portal of WSO2 API-
M.
2. Subscribing to the API via the Developer Portal of WSO2 API-M and
generating keys.
Follow the instructions below to create and publish an API via the
Publisher Portal of WSO2 API-M.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/api-
publisher-home.png]
2. Create an API.
Let's use a mock REST service to create a REST API from scratch.
{"hello": "world"}
3. Select REST API from the home screen and then click Start From
Scratch.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/desi
gn-new-rest-api.png]
Context /hello
Version 1.0.0
Endpoint http://run.mocky.io/v2/5185415ba171ea3a00704eed
Note
This will publish your first API on the Developer Portal as well as
deploy it on the API Gateway. You now have an OAuth2.0 secured
REST API that is ready to be consumed.
[]
Step 2 - Subscribe to the API
Follow the instructions below to subscribe to the API and generate the
keys via the Developer Portal of WSO2 API-M.
https://localhost:9443/devportal [https://localhost:9443/devportal]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/dev-
portal-landing-page.png]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/api-
overview.png]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
subscriptions-menu.png]
b. Click SUBSCRIPTION & KEY GENERATION WIZARD in the above
screen.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
key-gen-wizard.png]
Enter the application name, and click Next without changing any
of the other default values.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
key-gen-wizard-1.png]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
key-gen-wizard-2.png]
This step allows you to specify the permissions (scopes) for the
token. Click Next without changing any of the default values.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
key-gen-wizard-4.png]
g. Click the copy icon, as shown below, to copy the generated test
access token to the clipboard.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
key-gen-wizard-5.png]
h. Click Finish.
Voila!!! You can now test the HelloWorld API with the OAuth2.0 token that
you just generated.
[]
Follow the instructions below to invoke the previously created API with the
generated keys.
2. Paste the access token that you previously copied in the Access
Token field.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/test-
api.png]
3. If this is the first time you are using the API test console from your
browser, open a new tab and navigate to the https://localhost:8243/
[https://localhost:8243/] URL.
This will prompt your browser to accept the certificate used by the
API Gateway. This is required because by default the API Gateway
uses a self-signed certificate that is not trusted by web browsers.
Note
This certificate that is used by the API Gateway is replaced when deploying the
system in production.
4. Click on the GET resource of the API to expand the resource and Click
Try It Out.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/expa
nded-get-resource.png]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/try-
api.png]
You should see the {"hello" : "world"} response from the API.
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/try-
it-success.png]
Let's look at how you can use WSO2 API Controller (apictl) which is the
command-line tool to move APIs, API Products, and Applications across
WSO2 API-M environments and to perform CI/CD operations. (For more
information, see Getting Started with WSO2 API Controller (apictl)
[https://apim.docs.wso2.com/en/4.0.0/install-and-setup/setup/api-
controller/getting-started-with-wso2-api-controller/])
1. Download the apictl version 4.0.0 (or the latest of the 4.x family)
based your operating system from https://wso2.com/api-
management/tooling/ [https://wso2.com/api-management/tooling/]
from under the Dev-Ops Tooling section.
Warn
If you have previously used an apictl old version, backup and remove the
/home/<user>/.wso2apictl directory and reconfigure the environments using the
commands as explained below.
./apictl --help
4. Point the apictl to the instance of WSO2 API-M in which you want to
deploy APIs.
It is assumed that WSO2 API-M is run locally (localhost) using the default ports.
Note
Use the following command to view the various options related to initializing a
project.
b. Open the api.yaml file. (Alternatively, You can use a text editor
to open this file as well.)
lifeCycleStatus: PUBLISHED
production_endpoints:
url: https://petstore.swagger.io/v2
sandbox_endpoints:
url: https://petstore.swagger.io/v2
Note
Make sure that there are no spaces in-between the context value in the
api.yaml file.
If you want to push this API to the Publisher Portal only, the status
should be CREATED .
Step 2 - Publish the API
Info
If you are working with a specific environment for the first time, you will be
prompted to enter your account credentials on WSO2 API-M. You can use the
default admin credentials as admin/admin .
You should now see your API deployed successfully on WSO2 API-M.
2. Browse the Publisher and the Developer Portals to view the API
details.
https://localhost:9443/publisher
[https://localhost:9443/publisher]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
qsg-publisher.png]
https://localhost:9443/devportal
[https://localhost:9443/devportal]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/
qsg-devportal.png]
You will get an access token that can be used to invoke your API. (For
more information on generating keys using apictl, see Get keys for an
API/API Product [https://apim.docs.wso2.com/en/4.0.0/install-and-
setup/setup/api-controller/ci-cd-with-wso2-api-management/#g-get-
keys-for-an-apiapi-product].)
Execute the below cURL command to invoke the resource GET /pet
of your API. (Make sure to enter the access token that you recieved
earlier as the Bearer in thee request)
{"id":5,"category":
{"id":5,"name":"jimmy"},"name":"jimmy","photoUrls":
[],"tags":[{"id":5,"name":"jimmy"}],"status":"pending"}
Note
Alternatively, you can consume the API as explained in the following sections
using the WSO2 API-M Developer Portal.
Subscribe to the API via the Developer Portal and generate keys [#subscribe]
[https://apim.docs.wso2.com/en/4.0.0/assets/img/get_started/qsg-
petstore-response.jpg]
Scenario 1 - Create REST API from an
OpenAPI Definition
This tutorial is part of a series that guides you through all the capabilities
of API Manager. This involves creating a REST API from an OpenAPI
Definition. For more details on the scenario and general prerequisites,
please see the scenario overview page
[https://apim.docs.wso2.com/en/4.0.0/tutorials/scenarios/scenario-
overview].
User story
Coltrain is one of the railway companies that is partnered up with GOGO
Train to provide better service to their customers. Coltrain already has
some internally managed APIs deployed in-house and these are managed
by their internal development team. One of the APIs is a train schedule
retrieval API, which is intended for the public community to get the
Coltrain schedules. Currently, this API is exposed to the public and the
Coltrain development team faces challenges in maintaining and handling
the high load for the API.
By exposing this API through WSO2 API Manager, Coltrain expects to get
the full benefits of an API Management solution such as API lifecycle
management, security, rate limiting, etc. and decouple the maintenance
overhead from the internal teams.
Let's first test the backend. You could do following curl command and you
will get a response with schedule items.
curl "http://localhost:8082/train-operations/v1/schedules"
This indicates that the backend is working fine. Next, let’s expose this API
through WSO2 API Manager.
4. Add the context (say /coltrain) and create the API. Keep the endpoint
as it is. You could select the checkbox in front of the endpoint to
check the status of the backend URL.
Since this API is intended to be accessed by public users, the Coltrain API
providers want to remove any authentication for this resource. This way
anyone can access the service without any credentials.
Step 3: Remove authentication to the resource
3. Under the Operation Governance section, turn off the radio button for
Security.
Before Publishing the API, Coltrain developers want to test this API. WSO2
API Manager provides a Test console to test the API while it is in the
development stage.
Before starting to test the API, you need to deploy the API in the Gateway.
For that, go to Deploy → Deployment section and select Deploy . This will
deploy the API in the Gateway but the API will not be visible to the outside
world.
Expand the /schedules resource and invoke the API. You won’t need to
click Generate Key since we have removed the security for this resource.
You will have to generate one if you want to test the /schedules/{id}
resource.
Now testing is done from the publisher end.
What's next
Try out the next scenario in the series, Access Control
[https://apim.docs.wso2.com/en/4.0.0/tutorials/scenarios/scenario2-
access-control].
Expose a SOAP service as a REST
API
This tutorial will explain the steps to design, publish and invoke a SOAP
service as a RESTful API using Pass Through
2. Select Pass Through option and thereafter, select one of the following
options:
Note
When uploading a WSDL archive, all the dependent wsdls/xsds that are referred in
the parent WSDL file should reside inside the WSDL archive itself. If not, the
validation will fail at the point of API creation.
3. Click NEXT button to proceed to the next phase and Provide the
information in the table below and click CREATE button.
Name PhoneVerification
Context /phoneverify
Version 1.0
Endpoint http://ws.cdyne.com/phoneverify/phoneverify.asmx
4. The created API appears in the publisher as follows.
Note
If you wish to add scopes to the resources that were created, navigate to
Resources and expand the resources. Thereafter, creating new scopes and specify
them under operation scope. If you specify a scope, you need to use the same
scope when generating access tokens for the subscribed application to invoke the
API. For more information on working with the scopes, see OAuthscopes
[https://apim.docs.wso2.com/en/4.0.0/design/api-security/oauth2/oauth2-
scopes/fine-grained-access-control-with-oauth-scopes/]
Note
Note that when creating this API, the default option of Rate limiting level , was
selected to API Level. For more information on setting advanced throttling
policies, see Enforce Throttling and Resource Access Policies
[https://apim.docs.wso2.com/en/4.0.0/design/rate-limiting/setting-throttling-
limits/].
. Navigate to Life Cycle and Click Publish button. You have now
published SOAP API at the Developer portal.
The generated JSON Web Token (JWT) appears in the popup. Make
sure to copy it.
5. Navigate to TryOut tab and paste the token at Access token input
field.
. Expand the POST method and click Try it out . Enter the following,
and click Execute to invoke the API.
SOAP
<soap:Envelope
Request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CheckPhoneNumber
xmlns="http://ws.cdyne.com/PhoneVerify/query">
<PhoneNumber>18006785432</PhoneNumber>
<LicenseKey>18006785432</LicenseKey>
</CheckPhoneNumber>
</soap:Body>
</soap:Envelope>
SOAP
Action http://ws.cdyne.com/PhoneVerify/query/CheckPhoneNumber
7. Note the API response that appears on the console.
Note
You can also invoke this API using a third-party tool such as SOAP UI. For more
information on how to invoke an API using a SOAP client, see Invoke an API using
a SOAP Client [https://apim.docs.wso2.com/en/4.0.0/consume/invoke-
apis/invoke-apis-using-tools/invoke-an-api-using-a-soap-client/] .