Clonado de endpoints en ORDS para versionado de APIs (cuando la consola no es suficiente)

Uno de los escenarios más habituales al trabajar con Oracle REST Data Services (ORDS) es el versionado de APIs.
Antes de entender qué hace el script de clonado, es importante comprender cómo modela ORDS una API internamente.
ORDS no trabaja con “endpoints sueltos”, sino con una jerarquía bien definida.
El diagrama resume esta estructura:
Módulo
└── Template (ruta)
└── Handler (método HTTP)
└── Parámetros
Por ejemplo:
/JsonConsulta/v1
/JsonConsulta/v2
El nombre del API y del recurso se mantiene, y lo único que queremos cambiar es la versión (v1, v2, etc.) en nuestro caso.
El problema es que ORDS no permite clonar un endpoint desde la consola web.
Cuando un endpoint tiene:
SQL complejo
Múltiples parámetros
Lógica probada en producción
Copiarlo a mano para una nueva versión es lento y propenso a errores.
Para resolver esto, se puede clonar directamente el endpoint desde el diccionario de ORDS, usando PL/SQL.
Objetivo del script
Este procedimiento:
Clona un endpoint ORDS existente (
v1) a una nueva versión (v2) o simplemente copiarlo con un nombre nuevo.Mantiene el mismo módulo
Copia handlers, SQL y parámetros
Evita duplicados (puede ejecutarse más de una vez sin romper nada)
Está pensado especialmente para entornos donde se quiere evolucionar una API sin tocar la versión anterior.
Script completo de clonado de endpoint ORDS
Para tener el proceso controlado conectamos con el propietario del esquema ORDS.
Variables principales
v_module_name := 'nombremodulo';
v_template_src := 'nombretemplateorigen/vx';
v_template_dst := 'nombretemplatedestino/vx';
Módulo ORDS donde existe el API
Template origen (endpoint actual)
Template destino (nueva versión)
SET SERVEROUTPUT ON
DECLARE
v_module_id NUMBER;
v_template_exists NUMBER;
v_handler_exists NUMBER;
v_param_exists NUMBER;
-- Nombre lógico del módulo
v_module_name VARCHAR2(100) := 'nombremodulo';
-- Template origen y template destino (clonado)
v_template_src VARCHAR2(100) := 'JsonConsulta/v1';
v_template_dst VARCHAR2(100) := 'JsonConsulta/v2';
BEGIN
-- Obtener el ID del módulo ORDS
SELECT id
INTO v_module_id
FROM user_ords_modules
WHERE name = v_module_name;
-- Crear el template destino si no existe
SELECT COUNT(*)
INTO v_template_exists
FROM user_ords_templates
WHERE module_id = v_module_id
AND uri_template = v_template_dst;
IF v_template_exists = 0 THEN
ORDS.DEFINE_TEMPLATE(
p_module_name => v_module_name,
p_pattern => v_template_dst,
p_priority => 0,
p_etag_type => 'HASH',
p_comments => 'Clonado desde ' || v_template_src
);
DBMS_OUTPUT.PUT_LINE('Template ' || v_template_dst || ' creado.');
ELSE
DBMS_OUTPUT.PUT_LINE('Template ' || v_template_dst || ' ya existe.');
END IF;
-- Clonar todos los handlers (GET, POST, etc.)
FOR src_handler IN (
SELECT h.method,
h.source_type,
h.source,
h.items_per_page,
h.mimes_allowed,
h.comments
FROM user_ords_handlers h
JOIN user_ords_templates t ON h.template_id = t.id
WHERE t.module_id = v_module_id
AND t.uri_template = v_template_src
ORDER BY h.id
) LOOP
SELECT COUNT(*)
INTO v_handler_exists
FROM user_ords_handlers h
JOIN user_ords_templates t ON h.template_id = t.id
WHERE t.module_id = v_module_id
AND t.uri_template = v_template_dst
AND h.method = src_handler.method;
IF v_handler_exists = 0 THEN
ORDS.DEFINE_HANDLER(
p_module_name => v_module_name,
p_pattern => v_template_dst,
p_method => src_handler.method,
p_source_type => src_handler.source_type,
p_items_per_page => src_handler.items_per_page,
p_mimes_allowed => src_handler.mimes_allowed,
p_comments => 'Clonado desde ' || v_template_src,
p_source => src_handler.source
);
DBMS_OUTPUT.PUT_LINE(
'Handler ' || src_handler.method || ' creado.'
);
ELSE
DBMS_OUTPUT.PUT_LINE(
'Handler ' || src_handler.method || ' ya existe.'
);
END IF;
END LOOP;
-- Clonar los parámetros de cada handler
FOR src_param IN (
SELECT p.name,
p.bind_variable_name,
p.source_type,
p.param_type,
p.access_method,
p.comments,
h.method
FROM user_ords_parameters p
JOIN user_ords_handlers h ON p.handler_id = h.id
JOIN user_ords_templates t ON h.template_id = t.id
WHERE t.module_id = v_module_id
AND t.uri_template = v_template_src
ORDER BY h.method, p.name
) LOOP
SELECT COUNT(*)
INTO v_param_exists
FROM user_ords_parameters p
JOIN user_ords_handlers h ON p.handler_id = h.id
JOIN user_ords_templates t ON h.template_id = t.id
WHERE t.module_id = v_module_id
AND t.uri_template = v_template_dst
AND p.name = src_param.name
AND h.method = src_param.method;
IF v_param_exists = 0 THEN
ORDS.DEFINE_PARAMETER(
p_module_name => v_module_name,
p_pattern => v_template_dst,
p_method => src_param.method,
p_name => src_param.name,
p_bind_variable_name => src_param.bind_variable_name,
p_source_type => src_param.source_type,
p_param_type => src_param.param_type,
p_access_method => src_param.access_method,
p_comments => src_param.comments
);
DBMS_OUTPUT.PUT_LINE(
'Parámetro ' || src_param.name ||
' (' || src_param.method || ') creado.'
);
ELSE
DBMS_OUTPUT.PUT_LINE(
'Parámetro ' || src_param.name ||
' (' || src_param.method || ') ya existe.'
);
END IF;
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('Clonación del endpoint completada.');
END;
/
Ejemplo salida:
Template JsonConsulta/v2 creado.
Handler JsonConsulta/v2 [GET] creado.
Parametro parm1 para método GET creado.
Parametro parm2 para método GET creado.
Parametro parm3 para método GET creado.
Parametro parm4 para método GET creado.
Parametro parm5 para método GET creado.
Parametro parm6 para método GET creado.
Parametro parm7 para método GET creado.
Parametro parm8 para método GET creado.
Parametro parm9 para método GET creado.
Parametro parm10 para método GET creado.
Parametro parm11 para método GET creado.
Clonación completada exitosamente.
PL/SQL procedure successfully completed.
¿Qué hace realmente este script?
A alto nivel, el procedimiento reproduce internamente lo que haríamos a mano en ORDS, pero de forma segura:
Usa las vistas
USER_ORDS_*para leer la definición actual del APIReplica exactamente:
La ruta del endpoint (solo cambia la versión/nombre)
Los métodos HTTP
El SQL asociado
Todos los parámetros y binds
Comprueba siempre si algo ya existe antes de crearlo
Esto permite ejecutar el script varias veces sin riesgo y mantener un versionado limpio y controlado.
¿Por qué hacerlo así y no a mano?
✔ Evita errores copiando SQL
✔ Permite versionado limpio de APIs
✔ Es repetible y controlado
✔ Suple una limitación real de la consola ORDS
Especialmente útil en:
Entornos de pruebas
Versionado de APIs
Cambios controlados antes de producción
Conclusión
Aunque ORDS es una herramienta potente, no todo se puede hacer desde la consola web.
Conociendo su diccionario interno y las APIs PL/SQL (ORDS.DEFINE_*), es posible automatizar tareas avanzadas como el clonado completo de endpoints.
Este procedimiento convierte una tarea manual y propensa a errores en un proceso seguro y limpio.
Y como refrexión personal, poner botón para clonar un endpoint desde la consola web por lo que vemos no debería ser tan complejo implementarlo.



