REST API

PHPMaker supports REST API that enables you to perform CRUD (Create, Read, Update and Delete) for the tables in the generated web application. You can issue the request to the API (e.g. via JavaScript code), get the response as JSON (JavaScript Object Notation), interpret and present the result as you like it. The default path for the API request is "api" under the generated application (i.e. <mysite>/api).

Note In this article we assume the API is at <mysite>/api so the URL of the API is /api/, if you change the API to a different folder, you need to change the URL in the following examples accordingly.

 

API Actions

The REST API is implemented on the basis of the list, view, add, edit and delete pages supported for each table selected for generation. The basic supported API actions are add (Create), list / view (Read), edit (Update), delete (Delete), login (Authenticate User, if Security is enabled) and file (Get file content).

The standard exchange of information with the API is shown in the following examples (using the "cars" table of the demo database for demonstration):

Example 1 - Get a record by key (view action)

JavaScript (Assuming "input-id" is an input for key value)

$("#input-id").change(function() {
    var object = "<Table>",
        key = encodeURIComponent($(this).val());
    $.get("/api/view/" + object + "/" + key, function(res) { // Get response from View page API
        if (res && res.success) {
            var row = res[object];
            alert(JSON.stringify(row)); // Show output JSON
        } else {
            alert(res.failureMessage);
        }
    });
});

HTTP Request
GET /api/view/cars/1
Accept: application/json


HTTP Response
200 OK
{
    "success": true,
    "version": "18.0.0",
    "cars": {
        "ID": 1,
        "Trademark": 1,
        ...
    }
}

Example 2 - Create a record (add action)

JavaScript (Assuming "btn-id" is the submit button for the add form)

$("#btn-id").click(function() {
    var object = "<Table>",
        data = $(this.form).serialize();
    $.post("/api/add/" + object, data, function(res) { // Get response from Add page API
        if (res && res.success) {
            var row = res[object];
            alert(JSON.stringify(row)); // Show output JSON
        } else {
            alert(res.failureMessage);
        }
    });
});
HTTP Request
POST /api/add/cars
Trademark=1&...
Accept: application/json

If your data is JSON, the HTTP request should be:

POST /api/add/cars
{ "Trademark": 1, ... }
Content-Type: application/json
Accept: application/json

Note If your data is JSON, make sure you have set the content type to application/json and make sure you use FULL JSON syntax so it can be parsed properly by json_decode.

HTTP Response (success)
200 OK
{
    "success": true,
    "version": "18.0.0",
    "cars": {
        "ID": 16,
        "Trademark": 1,
        ...
    }
}
HTTP Response (failure)
200 OK
{
    "success": false,
    "version": "18.0.0",
    "failureMessage": "<failed reason>"
}

The following actions are supported:

Note If you enable Use Swagger UI for API (see Tools -> Advanced Settings), you can test these actions with Swagger UI at http://mysite/basepath/swagger/.
Action Parameters Response

GET /api/list/{table}

Get list of records, e.g.

/api/list/cars

start=<StartRecordNumber> (Optional, start record number, default = 1)
recperpage=<RecordsPerPage> (Optional, record per page, default = Records per page setting, requires activating the Selectable page sizes setting)
order=<Field> (Optional, sort by the specified field, requires activating the Sort setting in List page)
ordertype=ASC|DESC (Optional, sort ascending (ASC) or descending (DESC), requires activating the Sort setting in List page)
<Field>=<FieldValue> (Optional, search field by value, requires activating the Advanced/Extended Search setting in List page)
Successful response
{ "success": true,"version": "18.0.0",
 "cars": [ { "ID": 1, "Trademark": 1, ... },
        { "ID": 2, "Trademark": 1, ... } ] }


Failed response
{ "success": false,"version": "18.0.0",
 "failureMessage": "<failed reason>" }


GET /api/view/{table}/{key}

Get single record by key, e.g.

/api/view/cars/1


Successful response
{ "success": true, "version": "18.0.0",
 "cars": { "ID": 1, "Trademark": 1, ... } }


Failed response
See failed response for list

POST /api/add/{table}

Insert a new record, e.g.

/api/add/cars
Trademark=1&...

<Field>=<FieldValue> (field to be inserted)
Successful response
{ "success": true, "version": "18.0.0",
 "cars": { "ID": 16, "Trademark": 1, ... } }


Failed response
See failed response for list

POST /api/register

Register a new user in the user table, e.g.

/api/register
Username=newuser&...

<Field>=<FieldValue> (field to be inserted)
Successful response
{ "success": true, "version": "18.0.0",
 "employees": { "EmployeeID": 10, "Username": "newuser", ... } }


Failed response
See failed response for list

POST /api/edit/{table}/{key}

Update an existing record by key, eg.

/api/edit/cars/1
Trademark=2&...

<Field>=<FieldValue> (field to be updated)
Successful response
{ "success": true, "version": "18.0.0",
 "cars": { "Trademark": 2, ... } }


Failed response
See failed response for list

GET /api/delete/{table}/{key}

Delete an existing record by key, e.g.

/api/delete/cars/1

POST /api/delete/{table}

Delete multiple records by keys, e.g.

/api/delete/cars
key_m[]=1&key_m[]=2&...

key_m[]=<FieldValue>

Note If composite key, <FieldValue> is comma separated values.


Successful response
{ "success": true, "version": "18.0.0",
 "cars": { "ID": 1, "Trademark": 1, ... } }


Failed response
See failed response for list

POST /api/login

Authenticate an user, see Authenticate user with JWT, e.g.

/api/login
username=admin&password=master

GET /api/login

Accepted if the advanced setting Allow login by URL is enabled, e.g.

/api/login?username=admin&password=master

username=<UserName> (user name value)
password=<Password> (password value)
securitycode=<SecurityCode> (Google Authenticator security code, required if Two factor authentication is enabled.)

Successful response
{ "JWT": "<JsonWebToken>" }

Note When testing in Swagger UI, after loggiing in, you need to click the Authorize button and enter "Bearer <JsonWebToken>" to authorize yourself before testing other actions.


Failed response
401 Unauthorized

GET /api/file/{table}/{field}/{key}

Get file info by primary, e.g.

/api/file/employees/Photo/1

GET /api/file/{table}/{path}

Get file data by encrypted file path, e.g.

/api/file/employees/xxx

Note If composite key, when testing in Swagger UI, enter {key} as comma separated values.

Successful response
For blob field (e.g. Picture field in cars table), binary content of file field with the correct content type.
For string field (e.g. Photo field in employees table), JSON response of file locations:
{ "Photo": { "EmpID1.jpg": "http://<my_site>/upload/EmpID1.jpg" }, "version": "18.0.0" }
If file path is encrypted:
{ "Photo": { "EmpID1.jpg": "http://<my_site>/api/file/employees/<encrypted_file_path>" }, "version": "18.0.0" }
then you can use the second file action to get the file content.


Failed response
Empty response

POST /api/upload

Upload files, e.g.

/api/upload

  Successful response
JSON response of file token and file info:
{ "success": true, "version": "18.0.0", "token": "<FileToken>", "files": [ ...file info...] }

Failed response
{ "success": false, "version": "18.0.0", "files": [] }

GET /api/permissions/{userLevelID}

Get permissions, e.g.

/api/permissions/1

POST /api/permissions/{userLevelID}

Update permissions, e.g.

/api/permissions/1
{ "cars": 264, ... }

 

{ "<Table1>": <Permission1>, "<Table2>": <Permission2>, ... }
Get permissions
JSON response:
{ "userlevel": 1, "permissions": { "cars": 8 , ... } }

Update permission

Successful response
{ "success": true, "userlevel": 1, "permissions": { "cars": 264 } }

Failed response
{ "success": false, "userlevel": 1 }

 

Authenticate User with JWT (JSON Web Token)

REST API is stateless. Each API request is independent of each other and no session data is stored in the server to record the current state of the API requests. To access protected resources from the API, you need to authenticate the user first by getting a JSON Web Token (JWT) using the login action, then pass the JWT as an authentication header in subsequent API requests.

Notes

  1. If you do not provide the JWT when accessing protected resources, you will get a 401 Unauthorized response.
  2. Each JWT has a limited timespan, so you may need to re-authenticate the user again once the token has expired.

You can customize the following JWT parameters via Tools -> Advanced Settings.

Settings Description Value
API JWT signing secret key The secret key used to sign the JWT. Non-optional, make sure you use different values for different projects and keep it in a secret place
API JWT signing algorithm The algorithm used to sign the JWT. Default is HS512. For more possible values refer to JWT web site below.
API JWT authorization header The name of the header storing the JWT. Default is X-Authorization
API access time after login (seconds) Time you can access the protected resources after login Default is 10. If you want immediate access, you can change it to 0.
API expire time after login (seconds) The JWT expiry time Default is 600 (10 minutes). You will need to authenticate again once the JWT expires.

To understand more about JWT, please visit:
https://jwt.io/introduction/

The following JavaScript shows how to get a protected resource from the API by JWT. Authenticate the user first by clicking the login button. Then click the Get order record button to get the order record which is a protected resource.

$(function() {
    var store = store || {};
    // Store JWT
    store.setJWT = function(data) {
        this.JWT = data;
    }
    // Login
    $("#frm-login").submit(function(e) {
        e.preventDefault();
        var data = $("#frm-login").serialize();
        $.post("/api/login", data, function(data) {
            $("button").removeAttr("disabled");
            store.setJWT(data.JWT);
        }).fail(function(xhr, status, error) {
            alert("login failed. status: " + status + ", error: " + error);
        });
    });
    // Get protected resource
    var getProtectedResource = function(e) {
        e.preventDefault();
        $.ajax({
            url: "/api/view/orders/10248",
            type: "GET",
            success: function(data, status, xhr) {
                var out = (data && typeof data === 'object') ? JSON.stringify(data) : data;
                alert(out);
            },
            error: function(xhr, ajaxOptions, thrownError) {
                alert("status: " + xhr.status + ", error: " + thrownError);
            },
            beforeSend: function(request) { // Set JWT header
                request.setRequestHeader('X-Authorization', 'Bearer ' + store.JWT);
            }
        });
    }
    $("#btn-with-token").click(getProtectedResource);
});
<form id="frm-login" method="post">
    <input type="text" name="username" value="admin">
    <input type="text" name="password" value="master">
    <input type="submit" value="Login">
</form>
<button id="btn-with-token" type="button" disabled>Get order record</button>

 

Upload Files

To upload files, you can use upload action (see above). You can upload file(s) by HTML 5, e.g.

<input type="file" id="id" name="name" onchange="uploadFiles(this);">

If you want to let the user select multiple files, simply use the multiple attribute on the input element and add [] to the name:

<input type="file" id="id" name="name[]" multiple onchange="uploadFiles(this);">

<script>
// Upload files
function uploadFiles(el) {
    var files = el.files, // Get FileList object
        name = el.name,
        url = "/api/upload",
        xhr = new XMLHttpRequest(),
        fd = new FormData();
    xhr.responseType = "json";
    xhr.open("POST", url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200 && xhr.response) { // Files uploaded
            if (xhr.response.filetoken) // Check file token
                console.log(xhr.response.filetoken);
        }
    };
    for (var i = 0; i < files.length - 1; i++)
        fd.append(name, files[i]); // Append File object to form data
    xhr.send(fd);
}
</script>

Notes

  1. When the user selects file(s), the files property of the input element is a FileList object containing File objects representing the files selected by the user.
  2. If file uploading is successful, you will get successful response (see above) with a file token, then you can use the file token as the field value of your field for add or edit actions.
  3. Uploading multiple files to a BLOB field is NOT supported.

 

 

Create Your Own API Action

Beside the built-in API actions, you can add your own API actions by Api_Action server event (see Server Events and Client Scripts).

Example 1 - Hello world

function Api_Action($app)
{
    $app->get('/hello/{name}', function ($request, $response, $args) {
        $name = $args["name"] ?? null; // Get the input value
        if ($name !== null) {
            $response = $response->write("Hello, " . $name); // Write to response
        } 
        return $response; // Return Psr\Http\Message\ResponseInterface object 
    });
}
The user can now access the URL /api/hello/John to get the response text "Hello John".

Notes

  1. Make sure you return a Psr\Http\Message\ResponseInterface object in your route callback.
  2. See Request and Response object for details about the $request and the $response arguments of the callback function.

 

Example 2 - Get product by name

function Api_Action($app)
{
    $app->get('/getProductByName/{name}', function ($request, $response, $args) {
        $name = $args["name"] ?? null; // Get the input value
        if ($name !== null) {
            $response = $response->withJson(ExecuteRow("SELECT * FROM products WHERE ProductName = '" . AdjustSql($name) . "'")); // Select the record by name and return as JSON
        }
        return $response; // Return Psr\Http\Message\ResponseInterface object
    });
}

The product data can now be accessed using the following JavaScript:

$.get("/api/getProductByName/Chai", function(res) {
    console.log(res); // Show the result in console
});

Note See Returning JSON for more details about returning JSON data from callback function.

 

 ©2002-2022 e.World Technology Ltd. All rights reserved.