Runtime
Compiled Go binary
Ship one executable and keep configuration outside the binary.
Configuration
JSON driven endpoints
Change tables, tokens, limits and modes, then restart the service.
Documentation
Generated from schema
Expose live endpoint documentation at /_documentation.
Why ApiQL.net?
There are a lot advantages why you should use ApiQL.net.
Advantages:
- Simple and intuitive sintax
- Minimum system requirements
- Flexible to use multiple tables on same API call (using views)
- Converts automatically all tables and views into endpoints
- Reliable and secure
- Runtime-generated documentation for your API project at
/_documentation
- Easy configure call methods (enable/disable)
- 100% controll which tables and views can have endpoints
- Unset specific or sensitive fields
System Requirements
ApiQL v2 runs as a single compiled Go binary and uses an external JSON configuration file.
- Linux server capable of running the provided Go binary
- MySQL version 5.6 or greater OR MariaDB version 10.1 or greater
- Apache or Nginx reverse proxy is recommended for public HTTP/HTTPS traffic
- The Go API service serves its own generated documentation at
/_documentation; PHP is only used for this public ApiQL.net website
ApiQL-SDK client
The ApiQL.net library includes dynamic PHP-sdk client, so if your client is based on PHP, you'll be able to use service on easiest way ever!
If you decide to use this sdk library, you can download here.
Linux installation
The installer downloads the latest public binary, creates /etc/apiql/config.json, creates the apiql-go systemd service and can optionally configure Apache reverse proxy.
1. Connect to your server
ssh user@your-server-ip
2. Run the installer
curl -fsSL https://apiql.net/documentation/downloads/install.sh | sudo bash
The installer creates the config file, but if it still contains placeholder database values the service will not start automatically.
3. Edit configuration
sudo nano /etc/apiql/config.json
Set your database connection, token settings and endpoint mode. Use allowed_actions for controlled endpoints, or allow_all for trusted internal APIs. In nano press Ctrl+O, Enter, then Ctrl+X to save and exit.
4. Start the service
sudo systemctl enable --now apiql-go
5. Check service status and logs
sudo systemctl status apiql-go --no-pager
sudo journalctl -u apiql-go -f
6. Test the API
curl "http://127.0.0.1:8097/your_table?token=your_token"
7. Open the generated API documentation
The Go service generates live documentation from your allowed_actions config, or from all database tables when allow_all is enabled. This route does not require a token.
http://127.0.0.1:8097/_documentation
Apache and HTTPS option
To install and configure Apache reverse proxy with Let's Encrypt in the same command, pass your domain and email:
curl -fsSL https://apiql.net/documentation/downloads/install.sh | sudo env APIQL_DOMAIN=api.example.com APIQL_EMAIL=you@example.com bash
After the domain proxy is ready, open your generated API documentation here:
https://api.example.com/_documentation
After every config change
ApiQL reads config.json on startup. If you edit /etc/apiql/config.json, restart the service:
sudo nano /etc/apiql/config.json
sudo systemctl restart apiql-go
sudo systemctl status apiql-go --no-pager
Configuration
Configuration for ApiQL v2 is stored in an external config.json file. You can change endpoints, database credentials, limits, debug settings and token behavior without rebuilding the Go binary. After every config change run sudo systemctl restart apiql-go, then reload /_documentation to see the updated endpoint documentation.
For trusted internal API systems you can install with all tables and all actions enabled:
curl -fsSL https://apiql.net/documentation/downloads/install.sh | sudo env APIQL_ALLOW_ALL=true APIQL_DB_HOST=localhost APIQL_DB_NAME=dbname APIQL_DB_USER=dbuser APIQL_DB_PASS=dbpass APIQL_TOKEN=change_me bash
Example configuration:
{
"listen": "127.0.0.1:8097",
"app_name": "Your project name here",
"app_desc": "Your project description...",
"base_url": "https://yourdomainhere.com/",
"token": "auth_token_here",
"tokens_table": null,
"max_limit_per_page": 100,
"default_per_page": 10,
"allow_all": false,
"allowed_actions": {
"tablename": ["list", "add", "edit", "delete"],
"tablename2": ["list", "add", "edit", "delete"]
},
"disabled_columns": ["email", "password"],
"debug": false,
"defaultStatusMessages": {
"200": "OK",
"400": "Bad request!",
"401": "Autherntication failed!",
"403": "Not allowed!",
"404": "End-point not found!",
"405": "Bad request",
"500": "Internal server error!"
},
"hostname": "localhost",
"port": 3306,
"username": "dbusername",
"password": "dbpassword",
"database": "dbname",
"dbdriver": "mysqli"
}
Allow all mode
For private/internal API systems, allow_all can expose every table and view in the configured database without defining each endpoint manually.
{
"allow_all": true
}
When allow_all is true, ApiQL ignores allowed_actions, disabled_tables and disabled_columns. All tables/views become endpoints, all columns are returned, and list, add, edit and delete are enabled. After changing this value, restart the service with sudo systemctl restart apiql-go.
Token configuration
Use a fixed token from JSON by keeping tokens_table as null:
{
"token": "auth_token_here",
"tokens_table": null
}
Or move tokens to MySQL by setting tokens_table. ApiQL will create the table automatically and authenticate only tokens with status = "valid". The table includes token status, creation datetime and last-used datetime.
{
"token": "auth_token_here",
"tokens_table": "_tokens"
}
/author
https://v2.apiql.net/author
Use `/author` endpoint to list records, get specific record by id, add, edit or delete record.
Also you can check POST vars for `/author` endpoint.
/author
List - get all items
Request-type: HTTP / GET
content-type: multipart/form-data
Simple usage for listing author
This type of usage will select all fields and will keep default pagination and sorting options.
https://v2.apiql.net/author?token=auth_token_here
curl -X GET 'https://v2.apiql.net/author?token=auth_token_here' -H 'content-type: multipart/form-data;'
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$response = $ApiQL->get_author();
if ($response) {
print_r($response);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$url = 'https://v2.apiql.net/author?token=auth_token_here';
$jsonResponse = file_get_contents($url);
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.getJSON('https://v2.apiql.net/author?token=auth_token_here',function(r){
console.log(r.author);
}).fail(function(error){
console.log(error.responseJSON);
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("GET", "https://v2.apiql.net/author?token=auth_token_here");
xhr.send();
Selecting specific fields from author
This type of usage will select only fields that you'll include as URL parameters (Query String).
All available fields for author are: author.id, author.author, author.city, author.country_code, author.email, author.linkedin_link, author.github_link, author.facebook_link
Ex. &field[endpoint.field]=alias_here&field[endpoint.field2]
*Alias is optional
https://v2.apiql.net/author?token=auth_token_here&field[author.id]=author_id&field[author.author]=author_author&field[author.city]=author_city&field[author.country_code]=author_country_code&field[author.email]=author_email&field[author.linkedin_link]=author_linkedin_link&field[author.github_link]=author_github_link&field[author.facebook_link]=author_facebook_link
curl -X GET 'https://v2.apiql.net/author?token=auth_token_here&field[author.id]=author_id&field[author.author]=author_author&field[author.city]=author_city&field[author.country_code]=author_country_code&field[author.email]=author_email&field[author.linkedin_link]=author_linkedin_link&field[author.github_link]=author_github_link&field[author.facebook_link]=author_facebook_link' -H 'content-type: multipart/form-data;'
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$response = $ApiQL->get_author('all', [
'field' => [
'author.some_field' => 'some_field_alias',
'author.some_field2' => 'some_field2_alias',
'author.some_field3' => 'some_field3_alias'
],
'page' => 1,
'limit' => 10,
//'sort' => ['author.some_field' => 'DESC', 'author.some_field2' => 'ASC'],
//'search' => ['author.some_field' => 'some_keyword', 'author.some_field2' => 'some_keyword2'],
//'filter' => ['author.some_field' => 'some_value', 'author.some_field2' => 'some_value2'],
]);
if ($response) {
print_r($response);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$fields = ['field' => []];
$fields['field']['author.id'] = 'author_id';
$fields['field']['author.author'] = 'author_author';
$fields['field']['author.city'] = 'author_city';
$fields['field']['author.country_code'] = 'author_country_code';
$fields['field']['author.email'] = 'author_email';
$fields['field']['author.linkedin_link'] = 'author_linkedin_link';
$fields['field']['author.github_link'] = 'author_github_link';
$fields['field']['author.facebook_link'] = 'author_facebook_link';
$fieldsQueryString = http_build_query($fields);
$url = 'https://v2.apiql.net/author?token=auth_token_here&' . $fieldsQueryString;
$jsonResponse = file_get_contents($url);
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.getJSON('https://v2.apiql.net/author',
{
"token" : "auth_token_here",
"field" : {
"author.id" : "author_id",
"author.author" : "author_author",
"author.city" : "author_city",
"author.country_code" : "author_country_code",
"author.email" : "author_email",
"author.linkedin_link" : "author_linkedin_link",
"author.github_link" : "author_github_link",
"author.facebook_link" : "author_facebook_link"
}
}
,function(r){
console.log(r.author);
}).fail(function(error){
console.log(error.responseJSON);
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("GET", "https://v2.apiql.net/author?token=auth_token_here&field[author.id]=author_id&field[author.author]=author_author&field[author.city]=author_city&field[author.country_code]=author_country_code&field[author.email]=author_email&field[author.linkedin_link]=author_linkedin_link&field[author.github_link]=author_github_link&field[author.facebook_link]=author_facebook_link");
xhr.send();
Same as selecting fields you can do more advanced operations in your API query.
Here is the list:
-
Limit
You can limit data (number of records per page) by simply adding `limit` parameter as query string:
&limit=50
-
Sort
You can sort items in result-set by one or more fields.
This is how you can use this option:
&sort[author.example_field_name1]=desc&sort[author.example_field_name2]=asc
-
Search
You can search by single or multiple values. Search will also check in the part of the word for selected field.
Search parameters can be appear like:
&search[author.author]=keyword&search[author.other_field_name]=keyword2
-
Filter
Very similar like search, filter can also filter response data by single or multiple fields. Compared with search, filter will check only exact data value.
Filter parameters can be appear like:
&filter[author.id]=some_value&filter[author.other_field_name]=some_value
-
Merge
This will execute JOIN in the background. This option is needed when you need to collect data from more then one tables. If you also like to fetch data from additional tables, you should define in fields in same way &field[new_table.field]=alias
This is how merge parameters should looks like:
&merge[new_table_name.id]=this_end_point_field_name
This will execute SQL JOIN like:
INNER JOIN `new_table_name` ON `new_table_name`.`id` = `author`.`this_end_point_field_name`
* Alternatively you can create MySQL view and thread exactly as table with needed tables and search criteria. This way ApiQL will show as separate endpoint. ;)
-
Add subitem in response
Use `add` param if you like to add record from other table. This will run separate sql and will include needed record instead key from the selected alias. This is how add parameters should looks like:
&add[needed_table.foreign_key]=alias_from_selected_field
* Please note: For this option alias of selected field needs to have value from the primary key of the needed table.
/author/{id}
get single record by id
Request-type: HTTP / GET
content-type: multipart/form-data
Get single record from author
This type of usage will select all fields for the single record. You can select only specific fields like on list call.
https://v2.apiql.net/author/{id}?token=auth_token_here
curl -X GET 'https://v2.apiql.net/author/{id}?token=auth_token_here' -H 'content-type: multipart/form-data;'
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$item = $ApiQL->get_author('{id}');
if ($item) {
print_r($item);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$url = 'https://v2.apiql.net/author/{id}?token=auth_token_here';
$jsonResponse = file_get_contents($url);
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.getJSON('https://v2.apiql.net/author/{id}?token=auth_token_here',function(r){
console.log(r.author);
}).fail(function(error){
console.log(error.responseJSON);
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("GET", "https://v2.apiql.net/author/{id}?token=auth_token_here");
xhr.send();
/author
add record
Request-type: HTTP / POST
content-type: multipart/form-data
Add record to author
Not enabled in configuration
This type of usage will insert record.
You can see all possible fields for this call on POST vars section
https://v2.apiql.net/author?token=auth_token_here
curl -X GET 'https://v2.apiql.net/author?token=auth_token_here' -H 'content-type: multipart/form-data' -F author=value -F city=value -F country_code=value -F email=value -F linkedin_link=value -F github_link=value -F facebook_link=value ;
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$insert_id = $ApiQL->add_author([
'author' => 'value_here',
'city' => 'value_here',
'country_code' => 'value_here',
'email' => 'value_here',
'linkedin_link' => 'value_here',
'github_link' => 'value_here',
'facebook_link' => 'value_here',
]);
if ($insert_id) {
print_r($insert_id);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$url = 'https://v2.apiql.net/author?token=auth_token_here';
$postVars=[];
$postVars['author'] = 'value_here'
$postVars['city'] = 'value_here'
$postVars['country_code'] = 'value_here'
$postVars['email'] = 'value_here'
$postVars['linkedin_link'] = 'value_here'
$postVars['github_link'] = 'value_here'
$postVars['facebook_link'] = 'value_here'
$opts = ['http' =>
[
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query($postVars)
]
];
$jsonResponse = file_get_contents($url, false, $context);
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.post('https://v2.apiql.net/author?token=auth_token_here',
{
"author" : "value_here",
"city" : "value_here",
"country_code" : "value_here",
"email" : "value_here",
"linkedin_link" : "value_here",
"github_link" : "value_here",
"facebook_link" : "value_here"
}
,function(r){
console.log(r.author);
}).fail(function(error){
console.log(error.responseJSON);
});
var data = new FormData();
data.append("author", "value_here");
data.append("city", "value_here");
data.append("country_code", "value_here");
data.append("email", "value_here");
data.append("linkedin_link", "value_here");
data.append("github_link", "value_here");
data.append("facebook_link", "value_here");
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("POST", "https://v2.apiql.net/author?token=auth_token_here");
xhr.send(data);
/author/{id}
edit record
Request-type: HTTP / POST
content-type: multipart/form-data
Edit record from author
Not enabled in configuration
This type of usage will update record on author.
You can see all possible fields for this call on POST vars section
https://v2.apiql.net/author/{id}?token=auth_token_here
curl -X GET 'https://v2.apiql.net/author/{id}?token=auth_token_here' -H 'content-type: multipart/form-data' -F author=value -F city=value -F country_code=value -F email=value -F linkedin_link=value -F github_link=value -F facebook_link=value ;
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$edit_response_id = $ApiQL->edit_author('{id}', [
'author' => 'new_value_here',
'city' => 'new_value_here',
'country_code' => 'new_value_here',
'email' => 'new_value_here',
'linkedin_link' => 'new_value_here',
'github_link' => 'new_value_here',
'facebook_link' => 'new_value_here',
]);
if ($edit_response_id) {
print_r($edit_response_id);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$url = 'https://v2.apiql.net/author/{id}?token=auth_token_here';
$postVars=[];
$postVars['author'] = 'value_here'
$postVars['city'] = 'value_here'
$postVars['country_code'] = 'value_here'
$postVars['email'] = 'value_here'
$postVars['linkedin_link'] = 'value_here'
$postVars['github_link'] = 'value_here'
$postVars['facebook_link'] = 'value_here'
$opts = ['http' =>
[
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query($postVars)
]
];
$jsonResponse = file_get_contents($url, false, $context);
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.post('https://v2.apiql.net/author/{id}?token=auth_token_here',
{
"author" : "value_here",
"city" : "value_here",
"country_code" : "value_here",
"email" : "value_here",
"linkedin_link" : "value_here",
"github_link" : "value_here",
"facebook_link" : "value_here"
}
,function(r){
console.log(r.author);
}).fail(function(error){
console.log(error.responseJSON);
});
var data = new FormData();
data.append("author", "value_here");
data.append("city", "value_here");
data.append("country_code", "value_here");
data.append("email", "value_here");
data.append("linkedin_link", "value_here");
data.append("github_link", "value_here");
data.append("facebook_link", "value_here");
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("POST", "https://v2.apiql.net/author/{id}?token=auth_token_here");
xhr.send(data);
/author/{id}
delete single record by id
Request-type: HTTP / DELETE
content-type: multipart/form-data
Delete single record from author
Not enabled in configuration
This type of usage will delete single record.
https://v2.apiql.net/author/{id}?token=auth_token_here
curl -X DELETE 'https://v2.apiql.net/author/{id}?token=auth_token_here' -H 'content-type: multipart/form-data;'
$ApiQL = new ApiQL('https://v2.apiql.net/', 'auth_token_here');
$delete_author = $ApiQL->delete_author('{id}');
if ($delete_author) {
print_r($delete_author);
} else {
print_r($ApiQL->error()->status_code);
print_r($ApiQL->error()->status_msg);
}
$url = 'https://v2.apiql.net/author/{id}?token=auth_token_here';
$jsonResponse = file_get_contents($url, false, stream_context_create(['http' => ['method' => 'DELETE']]));
$response = json_decode($jsonResponse, true);
print_r($response['author']);
exit();
$.ajax({
url: 'https://v2.apiql.net/author/{id}?token=auth_token_here',
type: 'DELETE',
success: function(r) {
console.log(r.author);
}
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var response_data=JSON.parse(this.responseText).author;
console.log(response_data);
}
});
xhr.open("DELETE", "https://v2.apiql.net/author/{id}?token=auth_token_here");
xhr.send();
POST vars for /author:
| Parameter |
Type |
Required |
Default |
* Note |
| author
| string |
Yes |
|
Max. length: 200 |
| city
| string |
No |
|
Max. length: 100 |
| country_code
| string |
Yes |
|
Max. length: 2 |
| email
| string |
Yes |
|
Max. length: 150 |
| linkedin_link
| string |
No |
|
Max. length: 250 |
| github_link
| string |
No |
|
Max. length: 200 |
| facebook_link
| string |
No |
|
Max. length: 250 |
/information
https://v2.apiql.net/information
Use `/information` endpoint to list records, get specific record by id, add, edit or delete record.
Also you can check POST vars for `/information` endpoint.
Request-type: HTTP / GET
content-type: multipart/form-data
Simple usage for listing information
This type of usage will select all fields and will keep default pagination and sorting options.
https://v2.apiql.net/information?token=auth_token_here
Selecting specific fields from information
This type of usage will select only fields that you'll include as URL parameters (Query String).
All available fields for information are: information.id, information.property, information.value
Ex. &field[endpoint.field]=alias_here&field[endpoint.field2]
*Alias is optional
https://v2.apiql.net/information?token=auth_token_here&field[information.id]=information_id&field[information.property]=information_property&field[information.value]=information_value
Same as selecting fields you can do more advanced operations in your API query.
Here is the list:
-
Limit
You can limit data (number of records per page) by simply adding `limit` parameter as query string:
&limit=50
-
Sort
You can sort items in result-set by one or more fields.
This is how you can use this option:
&sort[information.example_field_name1]=desc&sort[information.example_field_name2]=asc
-
Search
You can search by single or multiple values. Search will also check in the part of the word for selected field.
Search parameters can be appear like:
&search[information.property]=keyword&search[information.other_field_name]=keyword2
-
Filter
Very similar like search, filter can also filter response data by single or multiple fields. Compared with search, filter will check only exact data value.
Filter parameters can be appear like:
&filter[information.id]=some_value&filter[information.other_field_name]=some_value
-
Merge
This will execute JOIN in the background. This option is needed when you need to collect data from more then one tables. If you also like to fetch data from additional tables, you should define in fields in same way &field[new_table.field]=alias
This is how merge parameters should looks like:
&merge[new_table_name.id]=this_end_point_field_name
This will execute SQL JOIN like:
INNER JOIN `new_table_name` ON `new_table_name`.`id` = `information`.`this_end_point_field_name`
* Alternatively you can create MySQL view and thread exactly as table with needed tables and search criteria. This way ApiQL will show as separate endpoint. ;)
-
Add subitem in response
Use `add` param if you like to add record from other table. This will run separate sql and will include needed record instead key from the selected alias. This is how add parameters should looks like:
&add[needed_table.foreign_key]=alias_from_selected_field
* Please note: For this option alias of selected field needs to have value from the primary key of the needed table.
Request-type: HTTP / GET
content-type: multipart/form-data
Get single record from information
This type of usage will select all fields for the single record. You can select only specific fields like on list call.
https://v2.apiql.net/information/{id}?token=auth_token_here
Request-type: HTTP / POST
content-type: multipart/form-data
Add record to information
Not enabled in configuration
This type of usage will insert record.
You can see all possible fields for this call on POST vars section
https://v2.apiql.net/information?token=auth_token_here
Request-type: HTTP / POST
content-type: multipart/form-data
Edit record from information
Not enabled in configuration
This type of usage will update record on information.
You can see all possible fields for this call on POST vars section
https://v2.apiql.net/information/{id}?token=auth_token_here
Request-type: HTTP / DELETE
content-type: multipart/form-data
Delete single record from information
Not enabled in configuration
This type of usage will delete single record.
https://v2.apiql.net/information/{id}?token=auth_token_here
POST vars for /information:
| Parameter |
Type |
Required |
Default |
* Note |
| property
| string |
Yes |
|
Max. length: 100 |
| value
| string (big) |
No |
|
/ |