Create Basic RESTful Web Services Using PL/SQL

Use SQLcl to Display ORDS Web Service Definitions

useful link
https://oracle-base.com/articles/misc/oracle-rest-data-services-ords-create-basic-rest-web-services-using-plsql

https://oracle-base.com/articles/misc/oracle-rest-data-services-ords-use-sqlcl-to-display-ords-web-service-definitions

=============================================================================================================

Basic Information (schemas, modules, privileges)

('Module --> Template --> Handler')
=============================================================================================================

=> Schema

CONN sys/OraPassword1@pdb1 AS SYSDBA
Connected.
SQL> REST schemas
PARSING_SCHEMA PATTERN STATUS
-------------- ------- -------
TESTUSER1      hr      ENABLED

=> Module

SQL> CONN testuser1/testuser1@pdb1
Connected.
SQL> REST modules
NAME    PREFIX    STATUS    ITEMS_PER_PAGE
------- --------- --------- --------------
rest-v1 /rest-v1/ PUBLISHED 0
rest-v2 /rest-v2/ PUBLISHED 0

=> privileges

SQL> CONN testuser1/testuser1@pdb1
Connected.
SQL> REST privileges
NAME                            LABEL DESCRIPTION COMMENTS
------------------------------- ----- ----------- --------
oracle.soda.privilege.developer


=============================================================================================================
A) Quick Build
=============================================================================================================

1) Enable Schema

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

    COMMIT;
END;

2) Create RESTful Web Services:- The DEFINE_SERVICE procedure allows you to create a new module,
   template and handler in a single step. If the module already exists, it 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);

  ORDS.define_service(
    p_module_name    => 'rest-v2',
    p_base_path      => '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;

3)  View from SQL

=> Module

COLUMN name FORMAT A20
COLUMN uri_prefix FORMAT A20


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

=> Templates

COLUMN uri_template FORMAT A20

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

=> Handler

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

=============================================================================================================

B) 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.
=============================================================================================================

1)

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/

2) Multiple Templates

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


3) Multiple Parameters


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;
/

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

=============================================================================================================

C) 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;
/

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

D) Stored Procedure (JSON)

=============================================================================================================

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;
/
=============================================================================================================
E) 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.
=============================================================================================================



ONN 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;
/


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;
/


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;
/
=============================================================================================================
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;
/

=============================================================================================================

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;
/

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;
/


=============================================================================================================

Insert , Update , Delete , Read  All In One Example

=============================================================================================================



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;
/
=============================================================================================================
Web Service Definitions (export)

=============================================================================================================

SQL> REST export

SQL> REST export rest-v1

SQL> REST export /rest-v2/

Comments

Popular posts from this blog

Telegram Integration with Oracle

The username or password for the connection pool named |apex||, are invalid, expired, or the account is locked

Enabled SQL Developer Web (SDW) & ORDS Database API ORDS 19.4