diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py
index da2bcdb5b..97a26520e 100644
--- a/src/services/api/graphql/libs/op_mode.py
+++ b/src/services/api/graphql/libs/op_mode.py
@@ -1,100 +1,106 @@
 # Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 import re
 import typing
 import importlib.util
+from typing import Union
+from humps import decamelize
 
 from vyos.defaults import directories
+from vyos.opmode import _normalize_field_names
 
 def load_as_module(name: str, path: str):
     spec = importlib.util.spec_from_file_location(name, path)
     mod = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(mod)
     return mod
 
 def load_op_mode_as_module(name: str):
     path = os.path.join(directories['op_mode'], name)
     name = os.path.splitext(name)[0].replace('-', '_')
     return load_as_module(name, path)
 
 def is_op_mode_function_name(name):
     if re.match(r"^(show|clear|reset|restart)", name):
         return True
     return False
 
 def is_show_function_name(name):
     if re.match(r"^show", name):
         return True
     return False
 
 def _nth_split(delim: str, n: int, s: str):
     groups = s.split(delim)
     l = len(groups)
     if n > l-1 or n < 1:
         return (s, '')
     return (delim.join(groups[:n]), delim.join(groups[n:]))
 
 def _nth_rsplit(delim: str, n: int, s: str):
     groups = s.split(delim)
     l = len(groups)
     if n > l-1 or n < 1:
         return (s, '')
     return (delim.join(groups[:l-n]), delim.join(groups[l-n:]))
 
 # Since we have mangled possible hyphens in the file name while constructing
 # the snake case of the query/mutation name, we will need to recover the
 # file name by searching with mangling:
 def _filter_on_mangled(test):
     def func(elem):
         mangle = os.path.splitext(elem)[0].replace('-', '_')
         return test == mangle
     return func
 
 # Find longest name in concatenated string that matches the basename of an
 # op-mode script. Should one prefer to concatenate in the reverse order
 # (script_name + '_' + function_name), use _nth_rsplit.
 def split_compound_op_mode_name(name: str, files: list):
     for i in range(1, name.count('_') + 1):
         pair = _nth_split('_', i, name)
         f = list(filter(_filter_on_mangled(pair[1]), files))
         if f:
             pair = (pair[0], f[0])
             return pair
     return (name, '')
 
 def snake_to_pascal_case(name: str) -> str:
     res = ''.join(map(str.title, name.split('_')))
     return res
 
 def map_type_name(type_name: type, optional: bool = False) -> str:
     if type_name == str:
         return 'String!' if not optional else 'String = null'
     if type_name == int:
         return 'Int!' if not optional else 'Int = null'
     if type_name == bool:
         return 'Boolean!' if not optional else 'Boolean = false'
     if typing.get_origin(type_name) == list:
         if not optional:
             return f'[{map_type_name(typing.get_args(type_name)[0])}]!'
         return f'[{map_type_name(typing.get_args(type_name)[0])}]'
     # typing.Optional is typing.Union[_, NoneType]
     if (typing.get_origin(type_name) is typing.Union and
             typing.get_args(type_name)[1] == type(None)):
         return f'{map_type_name(typing.get_args(type_name)[0], optional=True)}'
 
     # scalar 'Generic' is defined in schema.graphql
     return 'Generic'
+
+def normalize_output(result: Union[dict, list]) -> Union[dict, list]:
+    return _normalize_field_names(decamelize(result))
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index c2c1db1df..0b77b1433 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -1,174 +1,177 @@
 # Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 import json
 
 from ariadne import convert_camel_case_to_snake
 
 from vyos.config import Config
 from vyos.configtree import ConfigTree
 from vyos.defaults import directories
 from vyos.template import render
 from vyos.opmode import Error as OpModeError
 
 from api.graphql.libs.op_mode import load_op_mode_as_module, split_compound_op_mode_name
+from api.graphql.libs.op_mode import normalize_output
 
 op_mode_include_file = os.path.join(directories['data'], 'op-mode-standardized.json')
 
 class Session:
     """
     Wrapper for calling configsession functions based on GraphQL requests.
     Non-nullable fields in the respective schema allow avoiding a key check
     in 'data'.
     """
     def __init__(self, session, data):
         self._session = session
         self._data = data
         self._name = convert_camel_case_to_snake(type(self).__name__)
 
         try:
             with open(op_mode_include_file) as f:
                 self._op_mode_list = json.loads(f.read())
         except Exception:
             self._op_mode_list = None
 
     def show_config(self):
         session = self._session
         data = self._data
         out = ''
 
         try:
             out = session.show_config(data['path'])
             if data.get('config_format', '') == 'json':
                 config_tree = ConfigTree(out)
                 out = json.loads(config_tree.to_json())
         except Exception as error:
             raise error
 
         return out
 
     def save_config_file(self):
         session = self._session
         data = self._data
         if 'file_name' not in data or not data['file_name']:
             data['file_name'] = '/config/config.boot'
 
         try:
             session.save_config(data['file_name'])
         except Exception as error:
             raise error
 
     def load_config_file(self):
         session = self._session
         data = self._data
 
         try:
             session.load_config(data['file_name'])
             session.commit()
         except Exception as error:
             raise error
 
     def show(self):
         session = self._session
         data = self._data
         out = ''
 
         try:
             out = session.show(data['path'])
         except Exception as error:
             raise error
 
         return out
 
     def add_system_image(self):
         session = self._session
         data = self._data
 
         try:
             res = session.install_image(data['location'])
         except Exception as error:
             raise error
 
         return res
 
     def delete_system_image(self):
         session = self._session
         data = self._data
 
         try:
             res = session.remove_image(data['name'])
         except Exception as error:
             raise error
 
         return res
 
     def system_status(self):
         import api.graphql.session.composite.system_status as system_status
 
         session = self._session
         data = self._data
 
         status = {}
         status['host_name'] = session.show(['host', 'name']).strip()
         status['version'] = system_status.get_system_version()
         status['uptime'] = system_status.get_system_uptime()
         status['ram'] = system_status.get_system_ram_usage()
 
         return status
 
     def gen_op_query(self):
         session = self._session
         data = self._data
         name = self._name
         op_mode_list = self._op_mode_list
 
         # handle the case that the op-mode file contains underscores:
         if op_mode_list is None:
             raise FileNotFoundError(f"No op-mode file list at '{op_mode_include_file}'")
         (func_name, scriptname) = split_compound_op_mode_name(name, op_mode_list)
         if scriptname == '':
             raise FileNotFoundError(f"No op-mode file named in string '{name}'")
 
         mod = load_op_mode_as_module(f'{scriptname}')
         func = getattr(mod, func_name)
         try:
             res = func(True, **data)
         except OpModeError as e:
             raise e
 
+        res = normalize_output(res)
+
         return res
 
     def gen_op_mutation(self):
         session = self._session
         data = self._data
         name = self._name
         op_mode_list = self._op_mode_list
 
         # handle the case that the op-mode file name contains underscores:
         if op_mode_list is None:
             raise FileNotFoundError(f"No op-mode file list at '{op_mode_include_file}'")
         (func_name, scriptname) = split_compound_op_mode_name(name, op_mode_list)
         if scriptname == '':
             raise FileNotFoundError(f"No op-mode file named in string '{name}'")
 
         mod = load_op_mode_as_module(f'{scriptname}')
         func = getattr(mod, func_name)
         try:
             res = func(**data)
         except OpModeError as e:
             raise e
 
         return res