8i | 9i | 10g | 11g | 12c | 13c | 18c | 19c | 21c | 23c | Misc | PL/SQL | SQL | RAC | WebLogic | Linux

Home » Articles » Misc » Here

Oracle REST Data Services (ORDS) : Create Basic RESTful Web Services Using PL/SQL

This article gives an overview of using Oracle REST Data Services to developing RESTful web services using the PL/SQL API provided by the ORDS package.

Related articles.

Assumptions and Comments

This article assumes the following.

The examples in this article are based around single operations against a single table. I don't consider these "realistic" tests by any means. From a performance perspective, web services that perform DML should call transactional APIs, which may perform multiple operations in a single call, rather than effectively exposing table APIs, like we are here.

Create a Test Database User

We need a new database user for our testing.

CONN / AS SYSDBA
ALTER SESSION SET CONTAINER=pdb1;

DROP USER testuser1 CASCADE;
CREATE USER testuser1 IDENTIFIED BY testuser1
  DEFAULT TABLESPACE users QUOTA UNLIMITED ON users;
  
GRANT CREATE SESSION, CREATE TABLE, CREATE TYPE TO testuser1;

Create and populate a copy of the EMP table.

CONN testuser1/testuser1@pdb1

CREATE TABLE EMP (
  EMPNO NUMBER(4,0), 
  ENAME VARCHAR2(10 BYTE), 
  JOB VARCHAR2(9 BYTE), 
  MGR NUMBER(4,0), 
  HIREDATE DATE, 
  SAL NUMBER(7,2), 
  COMM NUMBER(7,2), 
  DEPTNO NUMBER(2,0), 
  CONSTRAINT PK_EMP PRIMARY KEY (EMPNO)
  );
  
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7369,'SMITH','CLERK',7902,to_date('17-DEC-80','DD-MON-RR'),800,null,20);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7499,'ALLEN','SALESMAN',7698,to_date('20-FEB-81','DD-MON-RR'),1600,300,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7521,'WARD','SALESMAN',7698,to_date('22-FEB-81','DD-MON-RR'),1250,500,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7566,'JONES','MANAGER',7839,to_date('02-APR-81','DD-MON-RR'),2975,null,20);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7654,'MARTIN','SALESMAN',7698,to_date('28-SEP-81','DD-MON-RR'),1250,1400,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7698,'BLAKE','MANAGER',7839,to_date('01-MAY-81','DD-MON-RR'),2850,null,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7782,'CLARK','MANAGER',7839,to_date('09-JUN-81','DD-MON-RR'),2450,null,10);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7788,'SCOTT','ANALYST',7566,to_date('19-APR-87','DD-MON-RR'),3000,null,20);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7839,'KING','PRESIDENT',null,to_date('17-NOV-81','DD-MON-RR'),5000,null,10);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7844,'TURNER','SALESMAN',7698,to_date('08-SEP-81','DD-MON-RR'),1500,0,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7876,'ADAMS','CLERK',7788,to_date('23-MAY-87','DD-MON-RR'),1100,null,20);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7900,'JAMES','CLERK',7698,to_date('03-DEC-81','DD-MON-RR'),950,null,30);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7902,'FORD','ANALYST',7566,to_date('03-DEC-81','DD-MON-RR'),3000,null,20);
insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7934,'MILLER','CLERK',7782,to_date('23-JAN-82','DD-MON-RR'),1300,null,10);
commit;

Enable ORDS

We are going to assume ORDS is installed and is available from the following base URL.

http://localhost:8080/ords/

We have to enable Oracle REST data services for the test schema. We could use any unique and legal URL mapping pattern for the schema, so it is not necessary to expose the schema name. In this example we used "hr".

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.enable_schema(
    p_enabled             => TRUE,
    p_schema              => 'TESTUSER1',
    p_url_mapping_type    => 'BASE_PATH',
    p_url_mapping_pattern => 'hr',
    p_auto_rest_auth      => FALSE
  );
    
  COMMIT;
END;
/


SELECT parsing_schema,
       pattern
FROM   user_ords_schemas;

PARSING_SCHEMA    PATTERN
----------------- -----------
TESTUSER1         hr

SQL>

Web services from the schema can now be referenced using the following base URL.

http://localhost:8080/ords/hr/

We are now ready to start.

Create GET Web Services (Read/SELECT)

This section describes the creation of REST web services using the GET method, which are typically used to read data. To stay true to REST, you should not perform any changes to data with a GET operation. Think of this as query-only. It breaks down the process to give you some indication of the pieces that make up an ORDS RESTful web service. The sections for the other methods, will not be broken down in the same way, but the information in this section is applicable to them also.

Quick Build

The DEFINE_SERVICE procedure allows you to create a new module, template and handler in a single step. If the module already exists, it's replaced by the new definition.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_service(
    p_module_name    => 'rest-v1',
    p_base_path      => 'rest-v1/',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp',
    p_items_per_page => 0);

  COMMIT;
END;
/

The basic makeup of an ORDS RESTful web service is as follows.

We can see this pattern in the URL to access this web service. Remember, the default method for a browser URL is GET, so the final URL listed will work in a browser.

Base ORDS URL : http://localhost:8080/ords/
Schema (alias): http://localhost:8080/ords/hr/
Module        : http://localhost:8080/ords/hr/rest-v1/
Template      : http://localhost:8080/ords/hr/rest-v1/employees/

Views

We can see the definition in the database using the USER_ORDS_% views.

-- Modules
COLUMN name FORMAT A20
COLUMN uri_prefix FORMAT A20

SELECT id, name, uri_prefix
FROM   user_ords_modules
ORDER BY name;

	ID NAME 		URI_PREFIX
---------- -------------------- --------------------
     10006 rest-v1		/rest-v1/

SQL>


-- Templates
COLUMN uri_template FORMAT A20

SELECT id, module_id, uri_template
FROM   user_ords_templates
ORDER BY module_id;

	ID  MODULE_ID URI_TEMPLATE
---------- ---------- --------------------
     10007	10006 employees/

SQL>


-- Handlers.
COLUMN source_type FORMAT A15
COLUMN source FORMAT A20

SELECT id, template_id, source_type, method, source
FROM   user_ords_handlers
ORDER BY id;

        ID TEMPLATE_ID SOURCE_TYPE     METHOD     SOURCE
---------- ----------- --------------- ---------- --------------------
     10008       10007 json/collection GET        SELECT * FROM emp

SQL>

Manual Build

Rather than using the DEFINE_SERVICE procedure, we can build the same web service manually using the DEFINE_MODULE, DEFINE_TEMPLATE and DEFINE_HANDLER procedures. The following code creates a similar web service to that defined previously, but this time defining all the pieces manually.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v2',
    p_base_path      => 'rest-v2/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v2',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v2',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp',
    p_items_per_page => 0);
    
  COMMIT;
END;
/

The web service is available using the following URL.

http://localhost:8080/ords/hr/rest-v2/employees/

Multiple Templates

The following code creates a web service with two templates, one of which uses a parameter to return a single record. Notice the parameter is used in the associated query to limit the results returned.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v3',
    p_base_path      => 'rest-v3/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v3',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v3',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp',
    p_items_per_page => 0);
    
  ORDS.define_template(
   p_module_name    => 'rest-v3',
   p_pattern        => 'employees/:empno');

  ORDS.define_handler(
    p_module_name    => 'rest-v3',
    p_pattern        => 'employees/:empno',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp WHERE empno = :empno',
    p_items_per_page => 0);
    
  COMMIT;
END;
/

The web service is available using the following URLs.

http://localhost:8080/ords/hr/rest-v3/employees/
http://localhost:8080/ords/hr/rest-v3/employees/7499

Multiple Parameters

There are a number of ways to pass multiple parameters to a get services. The following code creates a web service to return a result set. Notice the parameters are not specified in the template, but are specified in the handler.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v3b',
    p_base_path      => 'rest-v3b/',
    p_items_per_page => 0);

  ORDS.define_template(
   p_module_name    => 'rest-v3b',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v3b',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp WHERE empno BETWEEN :empno_start AND :empno_end',
    p_items_per_page => 0);
  COMMIT;
END;
/

The web service is available using the following URL.

http://localhost:8080/ords/hr/rest-v3b/employees/?empno_start=7876&empno_end=7934

The following code recreates the previous web service to return a result set, but the parameters are specified in the template, so they are mandatory and the position is fixed.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v3b',
    p_base_path      => 'rest-v3b/',
    p_items_per_page => 0);

  ORDS.define_template(
   p_module_name    => 'rest-v3b',
   p_pattern        => 'employees/:empno_start/:empno_end');

  ORDS.define_handler(
    p_module_name    => 'rest-v3b',
    p_pattern        => 'employees/:empno_start/:empno_end',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp WHERE empno BETWEEN :empno_start AND :empno_end',
    p_items_per_page => 0);
  COMMIT;
END;
/

The web service is available using the following URL.

http://localhost:8080/ords/hr/rest-v3b/employees/7876/7934

Multi-Value Parameters

In some situations need the web service to deal with multiple values of the same parameter. One way you can do this is by passing a comma-separated list of values as a single parameter value. The web service will have to be coded to deal with this. The following example creates a service that handles a list of employee numbers.

There are several ways to deal with dynamic IN-lists, described here, but we will use the following pipelined table function to make the list appear as rows.

CREATE OR REPLACE TYPE t_in_list_tab AS TABLE OF VARCHAR2 (4000);
/

CREATE OR REPLACE FUNCTION in_list (p_in_list  IN  VARCHAR2)
  RETURN t_in_list_tab PIPELINED
AS
  l_text  VARCHAR2(32767) := p_in_list || ',';
  l_idx   NUMBER;
BEGIN
  LOOP
    l_idx := INSTR(l_text, ',');
    EXIT WHEN NVL(l_idx, 0) = 0;
    PIPE ROW (TRIM(SUBSTR(l_text, 1, l_idx - 1)));
    l_text := SUBSTR(l_text, l_idx + 1);
  END LOOP;

  RETURN;
END;
/

We incorporate this pipelined table function into a query to handle multiple employee number values.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v3c',
    p_base_path      => 'rest-v3c/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v3c',
   p_pattern        => 'employees/:empno');

  ORDS.define_handler(
    p_module_name    => 'rest-v3c',
    p_pattern        => 'employees/:empno',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT *
                         FROM   emp
                         WHERE  empno IN (SELECT * FROM TABLE(in_list(:empno)))
                         ORDER BY ename',
    p_items_per_page => 0);
    
  COMMIT;
END;
/

The web service is available using the following URL.

http://localhost:8080/ords/hr/rest-v3c/employees/7876,7934,7782

Stored Procedure (JSON)

The previous examples use handlers associated with queries, but they can be associated with stored procedures. I would suggest this is the best approach to REST enabling the database, as you can hide relational complexity behind a PL/SQL API. To show this, create the following procedure, which uses the APEX_JSON package to generate the JSON results.

CONN / AS SYSDBA
ALTER SESSION SET CONTAINER=pdb1;

GRANT CREATE PROCEDURE TO testuser1;

CONN testuser1/testuser1@pdb1

CREATE OR REPLACE PROCEDURE get_emp_json (p_empno IN emp.empno%TYPE DEFAULT NULL) AS
  l_cursor SYS_REFCURSOR;
BEGIN
  
  OPEN l_cursor FOR
    SELECT e.empno AS "empno",
           e.ename AS "employee_name",
           e.job AS "job",
           e.mgr AS "mgr",
           TO_CHAR(e.hiredate,'YYYY-MM-DD') AS "hiredate",
           e.sal AS "sal",
           e.comm  AS "comm",
           e.deptno AS "deptno"
    FROM   emp e
    WHERE  e.empno = DECODE(p_empno, NULL, e.empno, p_empno);

  APEX_JSON.open_object;
  APEX_JSON.write('employees', l_cursor);
  APEX_JSON.close_object;
END;
/

The following code creates a web service which calls this procedure. Notice the source type of SOURCE_TYPE_PLSQL and a PL/SQL block as the source.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v4',
    p_base_path      => 'rest-v4/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v4',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v4',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN get_emp_json; END;',
    p_items_per_page => 0);
    
  ORDS.define_template(
   p_module_name    => 'rest-v4',
   p_pattern        => 'employees/:empno');

  ORDS.define_handler(
    p_module_name    => 'rest-v4',
    p_pattern        => 'employees/:empno',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN get_emp_json(:empno); END;',
    p_items_per_page => 0);
    
  COMMIT;
END;
/

The web service is available using the following URLs.

http://localhost:8080/ords/hr/rest-v4/employees/
http://localhost:8080/ords/hr/rest-v4/employees/7499

Stored Procedure (XML)

Using a stored procedure is a simple way to produce XML REST web services. To show this, create the following procedure, which uses SQL/XML to generate the XML results.

CONN testuser1/testuser1@pdb1

CREATE OR REPLACE PROCEDURE get_emp_xml (p_empno IN emp.empno%TYPE DEFAULT NULL) AS
  l_clob CLOB;
BEGIN

  SELECT XMLELEMENT("employees",
           XMLAGG(
             XMLELEMENT("emp",
               XMLFOREST(e.empno AS "empno",
                         e.ename AS "employee_name",
                         e.job AS "job",
                         e.mgr AS "mgr",
                         TO_CHAR(e.hiredate,'YYYY-MM-DD') AS "hiredate",
                         e.sal AS "sal",
                         e.comm  AS "comm",
                         e.deptno AS "deptno"
               )
             )
           )
         ).getClobVal()
  INTO   l_clob
  FROM   emp e
  WHERE  e.empno = DECODE(p_empno, NULL, e.empno, p_empno);

  OWA_UTIL.mime_header('text/xml');  
  HTP.print(l_clob);

END;
/

The following code creates a web service that calls this procedure.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v5',
    p_base_path      => 'rest-v5/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v5',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v5',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN get_emp_xml; END;',
    p_items_per_page => 0);
    
  ORDS.define_template(
   p_module_name    => 'rest-v5',
   p_pattern        => 'employees/:empno');

  ORDS.define_handler(
    p_module_name    => 'rest-v5',
    p_pattern        => 'employees/:empno',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN get_emp_xml(:empno); END;',
    p_items_per_page => 0);
    
  COMMIT;
END;
/

The web service is available using the following URLs.

http://localhost:8080/ords/hr/rest-v5/employees/
http://localhost:8080/ords/hr/rest-v5/employees/7499

Create POST Web Services (Create/INSERT)

The POST method is typically used for a create operation, like inserting some data. Remember, from a database perspective, a create operation, like creating an employee, may involve multiple operations, not just inserts. The POST method expects the parameter values to be passed as a payload. I used the "Advanced REST client" extension for Chrome to send the requests here.

The source of the POST handler can be a regular PL/SQL block containing an insert statement, but it makes sense to enclose this in a stored procedure or packaged procedure. Create a stored procedure to insert rows.

CONN testuser1/testuser1@pdb1

CREATE OR REPLACE PROCEDURE create_employee (
  p_empno     IN  emp.empno%TYPE,
  p_ename     IN  emp.ename%TYPE,
  p_job       IN  emp.job%TYPE,
  p_mgr       IN  emp.mgr%TYPE,
  p_hiredate  IN  VARCHAR2,
  p_sal       IN  emp.sal%TYPE,
  p_comm      IN  emp.comm%TYPE,
  p_deptno    IN  emp.deptno%TYPE
)
AS
BEGIN
  INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
  VALUES (p_empno, p_ename, p_job, p_mgr, TO_DATE(p_hiredate, 'YYYY-MM-DD'), p_sal, p_comm, p_deptno);
EXCEPTION
  WHEN OTHERS THEN
    HTP.print(SQLERRM);
END;
/

The following code creates a web service with a POST handler that calls the stored procedure, passing the parameters from the payload.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v6',
    p_base_path      => 'rest-v6/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v6',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v6',
    p_pattern        => 'employees/',
    p_method         => 'POST',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           create_employee(p_empno    => :empno,
                                           p_ename    => :ename,
                                           p_job      => :job,
                                           p_mgr      => :mgr,
                                           p_hiredate => :hiredate,
                                           p_sal      => :sal,
                                           p_comm     => :comm,
                                           p_deptno   => :deptno);
                         END;',
    p_items_per_page => 0);

  COMMIT;
END;
/

The web service can be called using the following URL, method, header and payload.

URL        : http://localhost:8080/ords/hr/rest-v6/employees/
Method     : POST
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999, "ename": "HALL", "job": "ANALYST", "mgr": 7782, "hiredate": "2016-01-01", "sal": 1000, "comm": null, "deptno": 10 }

$ curl -i -X POST --data-binary @/tmp/insert-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v6/employees/

You can see a more complex example here.

Create PUT Web Services (Amend/UPDATE)

The PUT method is typically used to amend data and expects the parameter values to be passed in the payload. The PUT operation is often expected to perform a create if the data doesn't already exist, but I personally don't like this approach.

The source of the PUT handler can be as simple as a regular PL/SQL block containing an update statement, but it makes sense to enclose this in a stored procedure or packaged procedure. Create a stored procedure to update rows.

CONN testuser1/testuser1@pdb1

CREATE OR REPLACE PROCEDURE amend_employee (
  p_empno     IN  emp.empno%TYPE,
  p_ename     IN  emp.ename%TYPE,
  p_job       IN  emp.job%TYPE,
  p_mgr       IN  emp.mgr%TYPE,
  p_hiredate  IN  VARCHAR2,
  p_sal       IN  emp.sal%TYPE,
  p_comm      IN  emp.comm%TYPE,
  p_deptno    IN  emp.deptno%TYPE
)
AS
BEGIN
  UPDATE emp
  SET ename    = p_ename,
      job      = p_job,
      mgr      = p_mgr,
      hiredate = TO_DATE(p_hiredate, 'YYYY-MM-DD'),
      sal      = p_sal,
      comm     = p_comm,
      deptno   = p_deptno
  WHERE empno  = p_empno;
EXCEPTION
  WHEN OTHERS THEN
    HTP.print(SQLERRM);
END;
/

The following code creates a web service with a PUT handler that calls the stored procedure, passing the parameters from the payload.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v7',
    p_base_path      => 'rest-v7/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v7',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v7',
    p_pattern        => 'employees/',
    p_method         => 'PUT',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           amend_employee(p_empno    => :empno,
                                          p_ename    => :ename,
                                          p_job      => :job,
                                          p_mgr      => :mgr,
                                          p_hiredate => :hiredate,
                                          p_sal      => :sal,
                                          p_comm     => :comm,
                                          p_deptno   => :deptno);
                         END;',
    p_items_per_page => 0);

  COMMIT;
END;
/

The web service can be called using the following URL, method, header and payload.

URL        : http://localhost:8080/ords/hr/rest-v7/employees/
Method     : PUT
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999, "ename": "WOOD", "job": "ANALYST", "mgr": 7782, "hiredate": "2016-01-01", "sal": 1000, "comm": null, "deptno": 20 }

$ curl -i -X PUT --data-binary @/tmp/update-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v7/employees/

Create DELETE Web Services (Remove/DELETE)

Like the POST and PUT methods, the source of the DELETE handler can be a as simple as a PL/SQL block containing a delete statement, but it makes sense to enclose this in a stored procedure or packaged procedure. Create a stored procedure to delete rows.

CONN testuser1/testuser1@pdb1

CREATE OR REPLACE PROCEDURE remove_employee (
  p_empno  IN  emp.empno%TYPE
)
AS
BEGIN
  DELETE FROM emp WHERE empno = p_empno;
EXCEPTION
  WHEN OTHERS THEN
    HTP.print(SQLERRM);
END;
/

The following code creates a web service with a DELETE handler that calls the stored procedure, passing the parameter from the payload.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v8',
    p_base_path      => 'rest-v8/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v8',
   p_pattern        => 'employees/');

  ORDS.define_handler(
    p_module_name    => 'rest-v8',
    p_pattern        => 'employees/',
    p_method         => 'DELETE',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           remove_employee(p_empno => :empno);
                         END;',
    p_items_per_page => 0);

  COMMIT;
END;
/

The web service can be called using the following URL, method, header and payload.

URL        : http://localhost:8080/ords/hr/rest-v8/employees/
Method     : DELETE
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999 }

$ curl -i -X DELETE --data-binary @/tmp/delete-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v8/employees/

Complete Example

Each of the examples above is defined as a separate module, most of which have a single template with a single handler. This is not a true reflection of how many web services will be presented. The example below combines the templates and handlers above into a single web service, allowing records to be inserted, updated, deleted and queried. It uses some of the stored procedures defined in the previous sections.

CONN testuser1/testuser1@pdb1

BEGIN
  ORDS.define_module(
    p_module_name    => 'rest-v9',
    p_base_path      => 'rest-v9/',
    p_items_per_page => 0);
  
  ORDS.define_template(
   p_module_name    => 'rest-v9',
   p_pattern        => 'employees/');

  -- READ : All records.
  ORDS.define_handler(
    p_module_name    => 'rest-v9',
    p_pattern        => 'employees/',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp',
    p_items_per_page => 0);

  -- INSERT
  ORDS.define_handler(
    p_module_name    => 'rest-v9',
    p_pattern        => 'employees/',
    p_method         => 'POST',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           create_employee (p_empno    => :empno,
                                            p_ename    => :ename,
                                            p_job      => :job,
                                            p_mgr      => :mgr,
                                            p_hiredate => :hiredate,
                                            p_sal      => :sal,
                                            p_comm     => :comm,
                                            p_deptno   => :deptno);
                         END;',
    p_items_per_page => 0);

  -- UPDATE
  ORDS.define_handler(
    p_module_name    => 'rest-v9',
    p_pattern        => 'employees/',
    p_method         => 'PUT',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           amend_employee(p_empno    => :empno,
                                          p_ename    => :ename,
                                          p_job      => :job,
                                          p_mgr      => :mgr,
                                          p_hiredate => :hiredate,
                                          p_sal      => :sal,
                                          p_comm     => :comm,
                                          p_deptno   => :deptno);
                         END;',
    p_items_per_page => 0);

  -- DELETE
  ORDS.define_handler(
    p_module_name    => 'rest-v9',
    p_pattern        => 'employees/',
    p_method         => 'DELETE',
    p_source_type    => ORDS.source_type_plsql,
    p_source         => 'BEGIN
                           remove_employee(p_empno => :empno);
                         END;',
    p_items_per_page => 0);

  -- READ : One Record
  ORDS.define_template(
   p_module_name    => 'rest-v9',
   p_pattern        => 'employees/:empno');

  ORDS.define_handler(
    p_module_name    => 'rest-v9',
    p_pattern        => 'employees/:empno',
    p_method         => 'GET',
    p_source_type    => ORDS.source_type_collection_feed,
    p_source         => 'SELECT * FROM emp WHERE empno = :empno',
    p_items_per_page => 0);

  COMMIT;
END;
/

The web service supports the following URLs, methods, headers and payloads.

READ
====
http://localhost:8080/ords/hr/rest-v9/employees/
http://localhost:8080/ords/hr/rest-v9/employees/7499


INSERT
======
URL        : http://localhost:8080/ords/hr/rest-v9/employees/
Method     : POST
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999, "ename": "HALL", "job": "ANALYST", "mgr": 7782, "hiredate": "2016-01-01", "sal": 1000, "comm": null, "deptno": 10 }

$ curl -i -X POST --data-binary @/tmp/insert-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v9/employees/


UPDATE
======
URL        : http://localhost:8080/ords/hr/rest-v9/employees/
Method     : PUT
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999, "ename": "WOOD", "job": "ANALYST", "mgr": 7782, "hiredate": "2016-01-01", "sal": 1000, "comm": null, "deptno": 20 }

$ curl -i -X PUT --data-binary @/tmp/update-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v9/employees/


DELETE
======
URL        : http://localhost:8080/ords/hr/rest-v9/employees/
Method     : DELETE
Header     : Content-Type: application/json
Raw Payload: { "empno": 9999 }

$ curl -i -X DELETE --data-binary @/tmp/delete-payload.json -H "Content-Type: application/json" http://localhost:8080/ords/hr/rest-v9/employees/

For more information see:

Hope this helps. Regards Tim...

Back to the Top.