Interacting With Api

From DreamFactory Wiki (Staging)
Jump to navigation Jump to search

Learn to interact with DreamFactory APIs using HTTP clients for CRUD operations, filtering, joins, and stored procedures

Interacting with the API

The following topic contains examples to help you become familiar with the many ways you can interact with a database-backed API. For these examples we're using the Insomnia HTTP client, however you can use any similar client or even cURL to achieve the same results.

Retrieving all records

Let's begin by retrieving all of a particular table's records just as was done within the API Docs example. Open your client and in the address bar set the URL to /api/v2/{service_name}/{table_name}, replacing {service_name} with the name of your API and {table_name} with the name of a table found within the database (and to which your API key's associated role has access). For the remainder of this example we use mysql as the service name, and in this particular example the table we're querying is called employees so the URL looks like this:

http://localhost/api/v2/_table/employees

Also, because we're retrieving records the method is set to GET.

Next, you must set the header which defines the API key. This header should be named X-DreamFactory-Api-Key. You might have to hunt around for a moment within your HTTP client to figure out where this is placed, but we promise it is definitely there. In the case of Insomnia select the Header tab found directly below the address bar:

Setting DreamFactory API Key Header

With the URL and header in place, request the URL and you should see the table records returned in JSON format:

JSON response of GET Request for All Records

The equivalent SQL query would look like this:

SELECT * FROM employees;

Limiting results

The previous example returns all records found in the employees table. But what if you only wanted to return five or ten records? You can use the limit parameter to do so. Modify your URL to look like this:

http://localhost/api/v2/_table/employees?limit=10

The equivalent SQL query would look like this:

SELECT * FROM employees LIMIT 10;

Offsetting results

The above example limits your results found in the employees table to 10, but what if you want to select records 11 - 20? You would use the offset parameter like this:

http://localhost/api/v2/_table/employees?limit=10&offset=10

The equivalent SQL query would look like this:

SELECT * FROM employees LIMIT 10 OFFSET 10;

Ordering results

You can order results by any column using the order parameter. For instance to order the employees tab by the emp_no field, modify your URL to look like this:

http://localhost/api/v2/_table/employees?order=emp_no

The equivalent SQL query looks like this:

SELECT * FROM employees ORDER BY emp_no;

To order in descending fashion, just append desc to the order string:

http://localhost/api/v2/_table/employees?order=emp_no%20desc
ℹ️ Note: Note The space separating emp_no and desc has been HTML encoded. Most programming languages offer HTML encoding capabilities either natively or through a third-party library so there's no need for you to do this manually within your applications. The equivalent SQL query looks like this: :::

note Note In the real world the location names would likely be managed in a separate table and then a join table would relate locations and supplies together. We're just trying to keep things simple for the purposes of this demonstration. :::

The table schemas look like this:

CREATE TABLE `supplies` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

CREATE TABLE `locations` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `supply_id` int(10) unsigned NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `supply_id` (`supply_id`),
  CONSTRAINT `locations_ibfk_1` FOREIGN KEY (`supply_id`) REFERENCES `supplies` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

Remember from the last example that DreamFactory creates convenient join aliases which can be used in conjunction with the related parameter. In this case, that alias is locations_by_supply_id. To create the relationship alongside the new supplies record, use that alias to nest the location name within the payload, as demonstrated here:

    {
        "resource": [
            {
                "name": "Broom",
                "locations_by_supply_id": [
                    {
                        "name": "Broom Closet"
                    }
                ]
            }
        ]
    }

With the payload sorted out, all that remains is to make a request to the supplies table endpoint:

/api/v2/mysql/_table/supplies

If the nested insert is successful, a 200 status code is returned along with the primary key ID of the newly inserted supplies record:

    {
        "resource": [
            {
                "id": 15
            }
        ]
    }

Updating records

Updating database records is a straightforward matter in DreamFactory. However to do so you first need to determine which type of REST update you wany to perform. Two options are supported:

  • PUT: The PUT request replaces an existing resource in its entirety. This means you need to pass along all of the resource attributes regardless of whether the attribute value is actually being modified.
  • PATCH: The PATCH request updates only part of the existing resource, meaning you only need to supply the resource primary key and the attributes you'd like to update. This is typically a much more convenient update approach than PUT, although both have their uses.

Let's work through update examples involving each method.

Updating records with PUT

When updating records with PUT you must send along all of the record attributes within the request payload:

    {
        "resource": [
            {
                "emp_no": 500015,
                "birth_date": "1900-12-15",
                "first_name": "Johnny",
                "last_name": "Football",
                "gender": "m",
                "hire_date": "2007-01-01"
            }
        ]
    }

With the payload in place, send a PUT request to the employees table endpoint:

/api/v2/mysql/_table/employees

If successful, DreamFactory returns a 200 status code and a response body containing the primary key of the updated record:

    {
        "resource": [
            {
                "emp_no": 500015
            }
        ]
    }

The equivalent SQL query looks like this:

    UPDATE supplies SET first_name = 'Johnny', last_name = 'Football',
    birthdate = '1900-12-15', gender = 'm', hire_date = '2007-01-01' WHERE emp_no = 500015;

Updating records with PATCH

To update one or more (but not all) attributes associated with a particular record found in the supplies table, send a PATCH request to the supplies table endpoint, accompanied by the primary key:

/api/v2/mysql/_table/supplies/8

Suppose the supplies table includes attributes such as name, description, and purchase_date, but we only want to modify the name value. The JSON request body would look like this:

    {
      "name": "Silver Stapler"
    }

If successful, DreamFactory returns a 200 status code and a response body containing the primary key of the updated record:

    {
      "id": 8
    }

The equivalent SQL query looks like this:

UPDATE supplies SET name = 'Silver Stapler' WHERE id = 8;

Deleting records

To delete a record, send a DELETE request to the table endpoint associated with the record you'd like to delete. For instance, to delete a record from the employees table, reference this URL:

/api/v2/mysql/_table/employees/500016

If deletion is successful, DreamFactory returns a 200 status code with a response body containing the deleted record's primary key:

    {
        "resource": [
            {
                "emp_no": 500016
            }
        ]
    }

The equivalent SQL query looks like this:

DELETE FROM employees WHERE emp_no = 500016;

Working with stored procedures

Stored procedure support via the REST API is just for discovery and calling what you have already created on your database, it is not for managing the stored procedures themselves. They can be accessed on each database service by the _proc resource.

As with most database features, there are a lot of common things about stored procedures across the various database vendors, with some notable exceptions. DreamFactory's blended API defines the difference between stored procedures and how they are used in the API as follows.

Procedures can use input parameters ('IN') and output parameters ('OUT'), as well as parameters that serve both as input and output ('INOUT'). They can, except in the Oracle case, also return data directly.

warning Database Vendor Exceptions * SQLite does not support procedures (or indeed functions). * PostgreSQL calls procedures and functions the same thing (a function) in PostgreSQL. DreamFactory calls them procedures if they have OUT or INOUT parameters or don't have a designated return type, otherwise they are referred to as functions. * SQL Server treats OUT parameters like INOUT parameters, and therefore require some value to be passed in. :::

Listing available stored procedures

The following call lists the available stored procedures, based on role access allowed:

GET http(s)://<dfServer>/api/v2/<serviceName>/_proc

Getting stored procedure details

We can use the ids url parameter and pass a comma delimited list of resource names to retrieve details about each of the stored procedures. For example if you have a stored procedure named getCustomerByLastName a GET call to http(s)://<dfServer>/api/v2/<serviceName>?ids=getCustomerByLastName returns the following:

{
  "resource": [
    {
      "alias": null,
      "name": "getCustomerByLastName",
      "label": "GetCustomerByLastName",
      "description": null,
      "native": [],
      "return_type": null,
      "return_schema": [],
      "params": [
        {
          "name": "LastName",
          "position": 1,
          "param_type": "IN",
          "type": "string",
          "db_type": "nvarchar",
          "length": 25,
          "precision": null,
          "scale": null,
          "default": null
        }
      ],
      "access": 31
    }
  ]
}

Calling a stored procedure

Using GET

When passing no payload is required, any IN or INOUT parameters can be sent by passing the values in the order required inside parentheses:

/api/v2/<serviceName>/_proc/myproc(val1, val2, val3)

Or as URL parameters by parameter name:

/api/v2/<serviceName>/_proc/myproc?param1=val1&param2=val2&param3=val3

In the below example, there is a stored procedure getUsernameByDepartment which takes two input parameters, a department code, and a userID. Making the following call:

/api/v2/<serviceName>/_proc/getUserNameByDepartment(AB,1234)

In the above example, AB is the department code and 1234 is the userID, which returns:

{
  "userID": "1234",
  "username": "Tomo"
}

Using POST

If a payload is required, i.e. passing values that are not url compliant, or passing schema formatting data, include the parameters directly in order. The same call as above can be made with a POST request with the following in the body:

{
  "params": ["AB", 1234]
}

Formatting results

For procedures that do not have INOUT or OUT parameters, the results can be returned as is, or formatted using the returns URL parameter if the value is a single scalar value, or the schema payload attribute for result sets.

If INOUT or OUT parameters are involved, any procedure response is wrapped using the configured (a URL parameter wrapper) or default wrapper name (typically "resource"), and then added to the output parameter listing. The output parameter values are formatted based on the procedure configuration data types.

Note that without formatting, all data is returned as strings, unless the driver (i.e. mysqlnd) supports otherwise. If the stored procedure returns multiple data sets, typically via multiple "SELECT" statements, then an array of datasets (i.e. array of records) is returned, otherwise a single array of records is returned.

schema - When a result set of records is returned from the call, the server uses any name-value pairs, consisting of "<field_name>": "<desired_type>", to format the data to the desired type before returning.

wrapper - Just like the URL parameter, the wrapper designation can be passed in the posted data.

Request with formatting configuration:

{
  "schema": {
    "id": "integer",
    "complete": "boolean"
  },
  "wrapper": "data"
}

Response without formatting:

{
  "resource": [
    {
      "id": "3",
      "name": "Write an app that calls the stored procedure.",
      "complete": 1
    },
    {
      "id": "4",
      "name": "Test the application.",
      "complete": 0
    }
  ],
  "inc": 6,
  "count": 2,
  "total": 5
}

Response with formatting applied:

{
  "data": [
    {
      "id": 3,
      "name": "Write an app that calls the stored procedure.",
      "complete": true
    },
    {
      "id": 4,
      "name": "Test the application.",
      "complete": false
    }
  ],
  "inc": 6,
  "count": 2,
  "total": 5
}

Using symmetric keys to decrypt data in a stored procedure (SQL Server)

SQL Server has the ability to perform column level encryption using symmetric keys which can be particularly useful for storing sensitive information such as passwords. A good example of how to do so can be found here

Typically, you would then decrypt this column (assuming the user has access to the certificate) with a statement in your sql server workbench such as:

OPEN SYMMETRIC KEY SymKey
DECRYPTION BY CERTIFICATE <CertificateName>;

SELECT <encryptedColumn> AS 'Encrypted data',
CONVERT(varchar, DecryptByKey(<encryptedColumn>)) AS 'decryptedColumn'
FROM SalesLT.Address;

Now, we cannot call our table endpoint (e.g /api/v2/<serviceName>/_table/<tableWithEncryptedField>) and add this logic with DreamFactory, however we could put the same logic in a stored procedure, and have DreamFactory call that to return our decrypted result. As long as the SQLServer user has permissions to the certificate used for encryption, they are able to decrypt the field. You could then use roles to make sure only certain users have access to this stored procedure.

The stored procedure looks something like this:

CREATE PROCEDURE dbo.<procedureName>
AS
BEGIN
  SET NOCOUNT on;
  OPEN SYMMETRIC KEY SymKey
  DECRYPTION BY CERTIFICATE <CertificateName>;

  SELECT <oneField>, CONVERT(nvarchar, DecryptByKey(<encryptedField>)) AS 'decrypted'
  FROM <table>;
END
return;

It can be called by DreamFactory with /_proc/<procedureName>.

See also