diff --git a/README.md b/README.md
index 49341f02..42d5b397 100644
--- a/README.md
+++ b/README.md
@@ -196,7 +196,7 @@ db:
 > `mySecondCollection` and `myThirdCollection`, respectively). FOCA will
 > automatically register and initialize these databases and collections for you
 > and add convenient clients to the app instance (accessible as children of
-> `current_app.config.foca` in an [application
+> `connexion.request.state.config` in an [application
 > context][res-flask-app-context]). The collections would be indexed by keys
 > `id`, `other_id` and `third_id`, respectively. Out of these, only `id`
 > will be required to be unique.  
@@ -443,12 +443,13 @@ my_custom_param_section:
 
 Once the application is created using `foca()`, one can easily access any
 configuration parameters from within the [application
-context][res-flask-app-context] through `current_app.config.foca like so:
+context][res-flask-app-context] through `connexion.request.state.config` like
+so:
 
 ```python
-from flask import current_app
+from connexion import request
 
-app_config = current_app.config.foca
+app_config = request.state.config
 
 db = app_config.db
 api = app_config.api
@@ -457,7 +458,6 @@ exceptions = app_config.exceptions
 security = app_config.security
 jobs = app_config.jobs
 log = app_config.log
-app_specific_param = current_app.config['app_specific_param']
 ```
 
 _Outside of the application context_, configuration parameters are available
diff --git a/TESTS/proTES/config.yaml b/TESTS/proTES/config.yaml
new file mode 100644
index 00000000..538514ac
--- /dev/null
+++ b/TESTS/proTES/config.yaml
@@ -0,0 +1,147 @@
+# FOCA configuration
+# Available in app context as attributes of `connexion.request.state.config`
+# Automatically validated via FOCA
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html
+
+# Server configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.ServerConfig
+server:
+  host: "0.0.0.0"
+  port: 8080
+  debug: True
+  environment: development
+  testing: False
+  use_reloader: False
+
+# Security configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.SecurityConfig
+security:
+  auth:
+    add_key_to_claims: True
+    algorithms:
+      - RS256
+    allow_expired: False
+    audience: null
+    validation_methods:
+      - userinfo
+      - public_key
+    validation_checks: any
+
+# Database configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.DBConfig
+db:
+  host: mongodb
+  port: 27017
+  dbs:
+    taskStore:
+      collections:
+        tasks:
+          indexes:
+            - keys:
+                task_id: 1
+                worker_id: 1
+              options:
+                "unique": True
+                "sparse": True
+        service_info:
+          indexes:
+            - keys:
+                id: 1
+
+# API configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.APIConfig
+api:
+  specs:
+    - path:
+        - petstore.yaml
+      add_operation_fields:
+        x-openapi-router-controller: controllers
+        security:
+          - bearerAuth: []
+      add_security_fields:
+        x-bearerInfoFunc: foca.security.auth.validate_token
+      disable_auth: True
+      connexion:
+        strict_validation: True
+        # current specs have inconsistency, therefore disabling response validation
+        # see: https://github.com/ga4gh/task-execution-schemas/issues/136
+        validate_responses: False
+        options:
+          swagger_ui: True
+          serve_spec: True
+
+# Logging configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.LogConfig
+log:
+  version: 1
+  disable_existing_loggers: False
+  formatters:
+    standard:
+      class: logging.Formatter
+      style: "{"
+      format: "[{asctime}: {levelname:<8}] {message} [{name}]"
+  handlers:
+    console:
+      class: logging.StreamHandler
+      level: 10
+      formatter: standard
+      stream: ext://sys.stderr
+  root:
+    level: 10
+    handlers: [console]
+
+jobs:
+  host: rabbitmq
+  port: 5672
+  backend: "rpc://"
+  include:
+    - pro_tes.tasks.track_task_progress
+
+# Exception configuration
+# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.ExceptionConfig
+exceptions:
+  required_members: [["message"], ["code"]]
+  status_member: ["code"]
+  exceptions: exceptions.exceptions
+
+controllers:
+  post_task:
+    db:
+      insert_attempts: 10
+    task_id:
+      charset: string.ascii_uppercase + string.digits
+      length: 6
+    timeout:
+      post: null
+      poll: 2
+      job: null
+    polling:
+      wait: 3
+      attempts: 100
+  list_tasks:
+    default_page_size: 5
+  celery:
+    monitor:
+      timeout: 0.1
+    message_maxsize: 16777216
+
+serviceInfo:
+  doc: Proxy TES for distributing tasks across a list of service TES instances
+  name: proTES
+  storage:
+    - file:///path/to/local/storage
+
+tes:
+  service_list:
+    - "https://csc-tesk-noauth.rahtiapp.fi"
+    - "https://funnel.cloud.e-infra.cz/"
+    - "https://tesk-eu.hypatia-comp.athenarc.gr"
+    - "https://tesk-na.cloud.e-infra.cz"
+    - "https://vm4816.kaj.pouta.csc.fi/"
+
+storeLogs:
+  execution_trace: True
+
+middlewares:
+  - - "pro_tes.plugins.middlewares.task_distribution.distance.TaskDistributionDistance"
+    - "pro_tes.plugins.middlewares.task_distribution.random.TaskDistributionRandom"
diff --git a/TESTS/proTES/docker-compose.yaml b/TESTS/proTES/docker-compose.yaml
new file mode 100644
index 00000000..edc3a6bb
--- /dev/null
+++ b/TESTS/proTES/docker-compose.yaml
@@ -0,0 +1,10 @@
+version: '3.6'
+services:
+
+  mongodb:
+    image: mongo:7.0
+    restart: unless-stopped
+    volumes:
+      - ./data/petstore/db:/data/db
+    ports:
+      - "27017:27017"
diff --git a/TESTS/proTES/exceptions.py b/TESTS/proTES/exceptions.py
new file mode 100644
index 00000000..c50a8682
--- /dev/null
+++ b/TESTS/proTES/exceptions.py
@@ -0,0 +1,97 @@
+"""proTES exceptions."""
+
+from connexion.exceptions import (  # type: ignore
+    BadRequestProblem,
+    ExtraParameterProblem,
+    Forbidden,
+    Unauthorized,
+)
+from pydantic import ValidationError
+from pymongo.errors import PyMongoError  # type: ignore
+from werkzeug.exceptions import (
+    BadRequest,
+    InternalServerError,
+    NotFound,
+)
+
+# pylint: disable="too-few-public-methods"
+
+
+class TaskNotFound(NotFound):
+    """Raised when task with given task identifier was not found."""
+
+
+class IdsUnavailableProblem(PyMongoError):
+    """Raised when task identifier is unavailable."""
+
+
+class NoTesInstancesAvailable(ValueError):
+    """Raised when no TES instances are available."""
+
+
+class MiddlewareException(ValueError):
+    """Raised when a middleware could not be applied."""
+
+
+class InvalidMiddleware(MiddlewareException):
+    """Raised when a middleware is invalid."""
+
+
+exceptions = {
+    Exception: {
+        "message": "An unexpected error occurred.",
+        "code": "500",
+    },
+    BadRequest: {
+        "message": "The request is malformed.",
+        "code": "400",
+    },
+    BadRequestProblem: {
+        "message": "The request is malformed.",
+        "code": "400",
+    },
+    ExtraParameterProblem: {
+        "message": "The request is malformed.",
+        "code": "400",
+    },
+    ValidationError: {
+        "message": "The request is malformed.",
+        "code": "400",
+    },
+    Unauthorized: {
+        "message": " The request is unauthorized.",
+        "code": "401",
+    },
+    Forbidden: {
+        "message": "The requester is not authorized to perform this action.",
+        "code": "403",
+    },
+    NotFound: {
+        "message": "The requested resource wasn't found.",
+        "code": "404",
+    },
+    TaskNotFound: {
+        "message": "The requested task wasn't found.",
+        "code": "404",
+    },
+    InternalServerError: {
+        "message": "An unexpected error occurred.",
+        "code": "500",
+    },
+    IdsUnavailableProblem: {
+        "message": "No/few unique task identifiers available.",
+        "code": "500",
+    },
+    NoTesInstancesAvailable: {
+        "message": "No valid TES instances available.",
+        "code": "500",
+    },
+    MiddlewareException: {
+        "message": "Middleware could not be applied.",
+        "code": "500",
+    },
+    InvalidMiddleware: {
+        "message": "Middleware is invalid.",
+        "code": "500",
+    },
+}
diff --git a/TESTS/proTES/petstore.yaml b/TESTS/proTES/petstore.yaml
new file mode 100644
index 00000000..2109d653
--- /dev/null
+++ b/TESTS/proTES/petstore.yaml
@@ -0,0 +1,152 @@
+openapi: 3.0.2
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+  description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
+  termsOfService: http://swagger.io/terms/
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0.html
+servers:
+  - url: http://localhost/
+paths:
+  /pets:
+    get:
+      description: |
+        Returns all pets from the system that the user has access to.
+      operationId: findPets
+      parameters:
+        - name: tags
+          in: query
+          description: tags to filter by
+          required: false
+          style: form
+          schema:
+            type: array
+            items:
+              type: string
+        - name: limit
+          in: query
+          description: maximum number of results to return
+          required: false
+          schema:
+            type: integer
+            format: int32
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+    post:
+      description: Creates a new pet in the store. Duplicates are allowed
+      operationId: addPet
+      requestBody:
+        description: Pet to add to the store
+        required: true
+        content:
+          application/json:
+            schema:
+              x-body-name: pet
+              $ref: '#/components/schemas/NewPet'
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /pets/{id}:
+    get:
+      description: Returns a user based on a single ID, if the user does not have access to the pet
+      operationId: findPetById
+      parameters:
+        - name: id
+          in: path
+          description: ID of pet to fetch
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+    delete:
+      description: deletes a single pet based on the ID supplied
+      operationId: deletePet
+      parameters:
+        - name: id
+          in: path
+          description: ID of pet to delete
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '204':
+          description: pet deleted
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+components:
+  schemas:
+    Pet:
+      allOf:
+        - $ref: '#/components/schemas/NewPet'
+        - type: object
+          required:
+          - id
+          properties:
+            id:
+              type: integer
+              format: int64
+
+    NewPet:
+      type: object
+      required:
+        - name  
+      properties:
+        name:
+          type: string
+        tag:
+          type: string
+
+    Error:
+      type: object
+      required:
+        - code
+        - message
+      properties:
+        code:
+          type: integer
+          format: int32
+        message:
+          type: string
diff --git a/TESTS/proTES/run.py b/TESTS/proTES/run.py
new file mode 100644
index 00000000..1354e396
--- /dev/null
+++ b/TESTS/proTES/run.py
@@ -0,0 +1,5 @@
+from foca import Foca
+
+foca = Foca(config_file="config.yaml")
+app = foca.create_app()
+celery_app = foca.create_celery_app()
diff --git a/examples/petstore-access-control/app.py b/examples/petstore-access-control/app.py
index 9d250aad..585b732d 100644
--- a/examples/petstore-access-control/app.py
+++ b/examples/petstore-access-control/app.py
@@ -2,9 +2,11 @@
 
 from foca import Foca
 
+from pathlib import Path
+
 if __name__ == '__main__':
     foca = Foca(
-        config_file="config.yaml"
+        config_file=Path("config.yaml")
     )
     app = foca.create_app()
-    app.run()
+    app.run(host="0.0.0.0", port=8080, lifespan="on")
diff --git a/examples/petstore-access-control/controllers.py b/examples/petstore-access-control/controllers.py
index cfc6a3f7..36f19e2a 100644
--- a/examples/petstore-access-control/controllers.py
+++ b/examples/petstore-access-control/controllers.py
@@ -2,27 +2,33 @@
 
 import logging
 
-from flask import (current_app, make_response)
-from pymongo.collection import Collection
-
-from exceptions import NotFound
+from connexion import request
+from flask import make_response
+from foca.models.config import Config
+from foca.utils.db import get_client
+from foca.utils.logging import log_traffic
 from foca.security.access_control.register_access_control import (
     check_permissions
 )
 
+from exceptions import NotFound
+
 logger = logging.getLogger(__name__)
 
 
 @check_permissions
+@log_traffic
 def findPets(limit=None, tags=None):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore-access-control']
-        .collections['pets'].client
+    config: Config = request.state.config
+    client = get_client(
+        config=config,
+        db='petstore-access-control',
+        collection='pets',
     )
     filter_dict = {} if tags is None else {'tag': {'$in': tags}}
     if not limit:
         limit = 0
-    records = db_collection.find(
+    records = client.find(
         filter_dict,
         {'_id': False}
     ).sort([('$natural', -1)]).limit(limit)
@@ -30,32 +36,38 @@ def findPets(limit=None, tags=None):
 
 
 @check_permissions
+@log_traffic
 def addPet(pet):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore-access-control']
-        .collections['pets'].client
+    config: Config = request.state.config
+    client = get_client(
+        config=config,
+        db='petstore-access-control',
+        collection='pets',
     )
     counter = 0
-    ctr = db_collection.find({}).sort([('$natural', -1)])
-    if not db_collection.count_documents({}) == 0:
+    ctr = client.find({}).sort([('$natural', -1)])
+    if not client.count_documents({}) == 0:
         counter = ctr[0].get('id') + 1
     record = {
         "id": counter,
         "name": pet['name'],
         "tag": pet['tag']
     }
-    db_collection.insert_one(record)
+    client.insert_one(record)
     del record['_id']
     return record
 
 
 @check_permissions
+@log_traffic
 def findPetById(id):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore-access-control']
-        .collections['pets'].client
+    config: Config = request.state.config
+    client = get_client(
+        config=config,
+        db='petstore-access-control',
+        collection='pets',
     )
-    record = db_collection.find_one(
+    record = client.find_one(
         {"id": id},
         {'_id': False},
     )
@@ -65,18 +77,21 @@ def findPetById(id):
 
 
 @check_permissions
+@log_traffic
 def deletePet(id):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore-access-control']
-        .collections['pets'].client
+    config: Config = request.state.config
+    client = get_client(
+        config=config,
+        db='petstore-access-control',
+        collection='pets',
     )
-    record = db_collection.find_one(
+    record = client.find_one(
         {"id": id},
         {'_id': False},
     )
     if record is None:
         raise NotFound
-    db_collection.delete_one(
+    client.delete_one(
         {"id": id},
     )
     response = make_response('', 204)
diff --git a/examples/petstore/app.py b/examples/petstore/app.py
index 9d250aad..5aff2b0b 100644
--- a/examples/petstore/app.py
+++ b/examples/petstore/app.py
@@ -1,10 +1,12 @@
 """Entry point for petstore example app."""
 
+from pathlib import Path
+
 from foca import Foca
 
 if __name__ == '__main__':
     foca = Foca(
-        config_file="config.yaml"
+        config_file=Path("config.yaml")
     )
     app = foca.create_app()
-    app.run()
+    app.run(host="0.0.0.0", port=8080, lifespan="on")
diff --git a/examples/petstore/controllers.py b/examples/petstore/controllers.py
index b8fad832..b61def37 100644
--- a/examples/petstore/controllers.py
+++ b/examples/petstore/controllers.py
@@ -2,54 +2,68 @@
 
 import logging
 
-from flask import (current_app, make_response)
-from pymongo.collection import Collection
+from connexion import request
+from flask import make_response
+from foca.utils.db import get_client
+from foca.utils.logging import log_traffic
+from foca.models.config import Config
 
-from exceptions import NotFound
+from exceptions import (
+    NotFound,
+    CustomException,
+    CustomExceptionStarlette,
+    CustomExceptionWerkzeug,
+    CustomExceptionConnexion,
+    WrappedException,
+    WrappedExceptionConnexion,
+    WrappedExceptionStarlette,
+    WrappedExceptionWerkzeug,
+)
+import connexion.exceptions
+import starlette.exceptions
+import werkzeug.exceptions
 
 logger = logging.getLogger(__name__)
 
 
+@log_traffic
 def findPets(limit=None, tags=None):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore']
-        .collections['pets'].client
-    )
+    config: Config = request.state.config
+    logger.warning(f"Config: {config}")
+    client = get_client(config=config, db='petstore', collection='pets')
     filter_dict = {} if tags is None else {'tag': {'$in': tags}}
     if not limit:
         limit = 0
-    records = db_collection.find(
+    records = client.find(
         filter_dict,
         {'_id': False}
     ).sort([('$natural', -1)]).limit(limit)
     return list(records)
 
 
+@log_traffic
 def addPet(pet):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore']
-        .collections['pets'].client
-    )
+    config: Config = request.state.config
+    client = get_client(config=config, db='petstore', collection='pets')
     counter = 0
-    ctr = db_collection.find({}).sort([('$natural', -1)])
-    if not db_collection.count_documents({}) == 0:
+    ctr = client.find({}).sort([('$natural', -1)])
+    if not client.count_documents({}) == 0:
         counter = ctr[0].get('id') + 1
     record = {
         "id": counter,
         "name": pet['name'],
         "tag": pet['tag']
     }
-    db_collection.insert_one(record)
+    client.insert_one(record)
     del record['_id']
     return record
 
 
+@log_traffic
 def findPetById(id):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore']
-        .collections['pets'].client
-    )
-    record = db_collection.find_one(
+    config: Config = request.state.config
+    client = get_client(config=config, db='petstore', collection='pets')
+    record = client.find_one(
         {"id": id},
         {'_id': False},
     )
@@ -58,19 +72,84 @@ def findPetById(id):
     return record
 
 
+@log_traffic
 def deletePet(id):
-    db_collection: Collection = (
-        current_app.config.foca.db.dbs['petstore']
-        .collections['pets'].client
-    )
-    record = db_collection.find_one(
+    config: Config = request.state.config
+    client = get_client(config=config, db='petstore', collection='pets')
+    record = client.find_one(
         {"id": id},
         {'_id': False},
     )
     if record is None:
         raise NotFound
-    db_collection.delete_one(
+    client.delete_one(
         {"id": id},
     )
     response = make_response('', 204)
     return response
+
+
+@log_traffic
+def exceptionWrapped():
+    raise WrappedException
+
+
+@log_traffic
+def exceptionWrappedConnexion():
+    raise WrappedExceptionConnexion
+
+
+@log_traffic
+def exceptionWrappedStarlette():
+    raise WrappedExceptionStarlette(
+        status_code=404,
+        detail="Wrapper exception Starlette."
+    )
+
+
+@log_traffic
+def exceptionWrappedWerkzeug():
+    raise WrappedExceptionWerkzeug
+
+
+@log_traffic
+def exceptionCustom():
+    raise CustomException
+
+
+@log_traffic
+def exceptionCustomConnexion():
+    raise CustomExceptionConnexion
+
+
+@log_traffic
+def exceptionCustomStarlette():
+    raise CustomExceptionStarlette
+
+
+@log_traffic
+def exceptionCustomWerkzeug():
+    raise CustomExceptionWerkzeug
+
+
+@log_traffic
+def exceptionBuiltin():
+    raise TypeError
+
+
+@log_traffic
+def exceptionBuiltinConnexion():
+    raise connexion.exceptions.Unauthorized
+
+
+@log_traffic
+def exceptionBuiltinStarlette():
+    raise starlette.exceptions.HTTPException(
+        status_code=404,
+        detail="Builtin exception Starlette."
+    )
+
+
+@log_traffic
+def exceptionBuiltinWerkzeug():
+    raise werkzeug.exceptions.Forbidden
diff --git a/examples/petstore/exceptions.py b/examples/petstore/exceptions.py
index 83a2d93a..86f5b001 100644
--- a/examples/petstore/exceptions.py
+++ b/examples/petstore/exceptions.py
@@ -1,10 +1,69 @@
 """Petstore exceptions."""
 
-from connexion.exceptions import BadRequestProblem
+from connexion.exceptions import BadRequestProblem, ProblemException
+from starlette.exceptions import HTTPException
 from werkzeug.exceptions import (
     InternalServerError,
     NotFound,
 )
+import connexion.exceptions
+import starlette.exceptions
+import werkzeug.exceptions
+
+
+class WrappedException(KeyError):
+    """Raised when task with given task identifier was not found."""
+
+
+class WrappedExceptionConnexion(connexion.exceptions.BadRequestProblem):
+    """Raised when task with given task identifier was not found."""
+
+
+class WrappedExceptionStarlette(starlette.exceptions.HTTPException):
+    """Raised when task with given task identifier was not found."""
+
+
+class WrappedExceptionWerkzeug(werkzeug.exceptions.NotFound):
+    """Raised when task with given task identifier was not found."""
+
+
+class CustomException(Exception):
+    def __init__(
+        self,
+        message: str = "Custom exception",
+    ):
+        super().__init__(message)
+
+
+class CustomExceptionConnexion(ProblemException):
+    def __init__(
+        self,
+        status: int = 403,
+        title: str = "CustomExceptionConnexion",
+        detail: str = "Custom exception Connexion",
+    ):
+        super().__init__(status=status, title=title, detail=detail)
+
+
+class CustomExceptionStarlette(HTTPException):
+    def __init__(
+        self,
+        status_code: int = 403,
+        detail: str = "Custom exception Starlette",
+    ):
+        super().__init__(status_code, detail)
+
+
+class CustomExceptionWerkzeug(werkzeug.exceptions.HTTPException):
+    def __init__(
+        self,
+        description: str = "Custom exception Werkzeug",
+        response=None,
+    ):
+        super().__init__(description=description, response=response)
+
+    code = 400
+
 
 exceptions = {
     Exception: {
diff --git a/examples/petstore/petstore.yaml b/examples/petstore/petstore.yaml
index 2109d653..138ec360 100644
--- a/examples/petstore/petstore.yaml
+++ b/examples/petstore/petstore.yaml
@@ -116,6 +116,138 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/Error'
+  /exceptions/wrapped:
+    get:
+      description: Raise wrapped Python exception.
+      operationId: exceptionWrapped
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/wrapped/connexion:
+    get:
+      description: Raise wrapped Connexion exception.
+      operationId: exceptionWrappedConnexion
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/wrapped/starlette:
+    get:
+      description: Raise wrapped Starlette exception.
+      operationId: exceptionWrappedStarlette
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/wrapped/werkzeug:
+    get:
+      description: Raise wrapped Werkzeug exception.
+      operationId: exceptionWrappedWerkzeug
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/custom:
+    get:
+      description: Raise custom Python exception.
+      operationId: exceptionCustom
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/custom/connexion:
+    get:
+      description: Raise custom Connexion exception.
+      operationId: exceptionCustomConnexion
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/custom/starlette:
+    get:
+      description: Raise custom Starlette exception.
+      operationId: exceptionCustomStarlette
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/custom/werkzeug:
+    get:
+      description: Raise custom Werkzeug exception.
+      operationId: exceptionCustomWerkzeug
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/builtin:
+    get:
+      description: Raise builtin Python exception.
+      operationId: exceptionBuiltin
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/builtin/connexion:
+    get:
+      description: Raise builtin Connexion exception.
+      operationId: exceptionBuiltinConnexion
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/builtin/starlette:
+    get:
+      description: Raise builtin Starlette exception.
+      operationId: exceptionBuiltinStarlette
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /exceptions/builtin/werkzeug:
+    get:
+      description: Raise builtin Werkzeug exception.
+      operationId: exceptionBuiltinWerkzeug
+      responses:
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 components:
   schemas:
     Pet:
diff --git a/examples/petstore/petstore_correct.yaml b/examples/petstore/petstore_correct.yaml
new file mode 100644
index 00000000..7797575c
--- /dev/null
+++ b/examples/petstore/petstore_correct.yaml
@@ -0,0 +1,152 @@
+openapi: 3.0.2
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+  description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
+  termsOfService: http://swagger.io/terms/
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0.html
+servers:
+  - url: http://localhost/
+paths:
+  /pets:
+    get:
+      description: |
+        Returns all pets from the system that the user has access to.
+      operationId: findPets
+      parameters:
+        - name: tags
+          in: query
+          description: tags to filter by
+          required: false
+          style: form
+          schema:
+            type: array
+            items:
+              type: string
+        - name: limit
+          in: query
+          description: maximum number of results to return
+          required: false
+          schema:
+            type: integer
+            format: int32
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+    post:
+      description: Creates a new pet in the store. Duplicates are allowed
+      operationId: addPet
+      requestBody:
+        x-body-name: pet
+        description: Pet to add to the store
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/NewPet'
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /pets/{id}:
+    get:
+      description: Returns a user based on a single ID, if the user does not have access to the pet
+      operationId: findPetById
+      parameters:
+        - name: id
+          in: path
+          description: ID of pet to fetch
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Pet'
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+    delete:
+      description: deletes a single pet based on the ID supplied
+      operationId: deletePet
+      parameters:
+        - name: id
+          in: path
+          description: ID of pet to delete
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '204':
+          description: pet deleted
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+components:
+  schemas:
+    Pet:
+      allOf:
+        - $ref: '#/components/schemas/NewPet'
+        - type: object
+          required:
+          - id
+          properties:
+            id:
+              type: integer
+              format: int64
+
+    NewPet:
+      type: object
+      required:
+        - name  
+      properties:
+        name:
+          type: string
+        tag:
+          type: string
+
+    Error:
+      type: object
+      required:
+        - code
+        - message
+      properties:
+        code:
+          type: integer
+          format: int32
+        message:
+          type: string
diff --git a/examples/petstore/test.yaml b/examples/petstore/test.yaml
new file mode 100644
index 00000000..bfafb01f
--- /dev/null
+++ b/examples/petstore/test.yaml
@@ -0,0 +1,32 @@
+responses:
+    default:
+        "*":
+            msg: Interal Server Error
+            status_code: 500
+    400:
+        "('operationId1', 'operationId2')":
+            msg: Bad Request
+            status_code: 400
+        operationId3:
+            msg: Extra Parameter
+            status_code: 400
+    401:
+        "*":
+            msg: Unauthorized
+            status_code: 401
+        operationId4:
+            msg: Very Unauthorized
+            status_code: 401
+    403:
+        "*":
+            msg: Forbidden
+            status_code: 403
+    500:
+        "*":
+            msg: Internal Server Error
+            status_code: 500
+
+    5XX:
+        "*":
+            msg: Internal Server Error
+            status_code: 500
diff --git a/foca/api/register_openapi.py b/foca/api/register_openapi.py
index 88810054..6ee86347 100644
--- a/foca/api/register_openapi.py
+++ b/foca/api/register_openapi.py
@@ -4,7 +4,7 @@
 from pathlib import Path
 from typing import Dict, List
 
-from connexion import App
+from connexion import FlaskApp
 
 from foca.models.config import SpecConfig
 from foca.config.config_parser import ConfigParser
@@ -14,9 +14,9 @@
 
 
 def register_openapi(
-        app: App,
+        app: FlaskApp,
         specs: List[SpecConfig],
-) -> App:
+) -> FlaskApp:
     """
     Register OpenAPI specifications with Connexion application instance.
 
@@ -87,10 +87,10 @@ def register_openapi(
 
         # Attach specs to connexion App
         logger.debug(f"Modified specs: {spec_parsed}")
-        spec.connexion = {} if spec.connexion is None else spec.connexion
+        spec.connexion = {} if spec.connexion is None else spec.connexion  # mod
         app.add_api(
             specification=spec_parsed,
-            **spec.model_dump().get('connexion', {}),
+            **spec.connexion,
         )
         logger.info(f"API endpoints added from spec: {spec.path_out}")
 
diff --git a/foca/errors/exceptions.py b/foca/errors/exceptions.py
index 0a62223e..78e39cc4 100644
--- a/foca/errors/exceptions.py
+++ b/foca/errors/exceptions.py
@@ -5,14 +5,15 @@
 from traceback import format_exception
 from typing import (Dict, List)
 
-from connexion import App
+import connexion
+from connexion import FlaskApp
+from connexion.lifecycle import ConnexionRequest, ConnexionResponse
 from connexion.exceptions import (
     ExtraParameterProblem,
     Forbidden,
     OAuthProblem,
     Unauthorized,
 )
-from flask import (current_app, Response)
 from json import dumps
 from werkzeug.exceptions import (
     BadRequest,
@@ -22,8 +23,11 @@
     NotFound,
     ServiceUnavailable,
 )
+import connexion.exceptions
+import werkzeug.exceptions
+import starlette.exceptions
 
-from foca.models.config import _get_by_path
+from foca.models.config import _get_by_path, ExceptionConfig
 
 # Get logger instance
 logger = logging.getLogger(__name__)
@@ -76,8 +80,39 @@
     }
 }
 
+exceptions_http = {
+    400: {
+        "title": "Bad Request",
+        "status": 400,
+    },
+    401: {
+        "title": "Unauthorized",
+        "status": 401,
+    },
+    403: {
+        "title": "Forbidden",
+        "status": 403,
+    },
+    404: {
+        "title": "Not Found",
+        "status": 404,
+    },
+    500: {
+        "title": "Internal Server Error",
+        "status": 500,
+    },
+    502: {
+        "title": "Bad Gateway",
+        "status": 502,
+    },
+    504: {
+        "title": "Gateway Timeout",
+        "status": 504,
+    }
+}
 
-def register_exception_handler(app: App) -> App:
+
+def register_exception_handler(app: FlaskApp) -> FlaskApp:
     """Register generic JSON problem handler with Connexion application
     instance.
 
@@ -88,8 +123,19 @@ def register_exception_handler(app: App) -> App:
         Connexion application instance with registered generic JSON problem
         handler.
     """
-    app.add_error_handler(Exception, _problem_handler_json)
-    logger.debug("Registered generic JSON problem handler with Connexion app.")
+    app.add_error_handler(
+        Exception,
+        exception_handler_builtin,
+    )
+    app.add_error_handler(
+        starlette.exceptions.HTTPException,
+        exception_handler_starlette,
+    )
+    app.add_error_handler(
+        connexion.exceptions.ProblemException,
+        exception_handler_connexion,
+    )
+    logger.debug("Registered FOCA problem handler with Connexion app.")
     return app
 
 
@@ -201,7 +247,10 @@ def _exclude_key_nested_dict(
     return obj
 
 
-def _problem_handler_json(exception: Exception) -> Response:
+def _problem_handler_json(
+    request: ConnexionRequest,
+    exception: Exception,
+) -> ConnexionResponse:
     """Generic JSON problem handler.
 
     Args:
@@ -210,9 +259,23 @@ def _problem_handler_json(exception: Exception) -> Response:
     Returns:
         JSON-formatted error response.
     """
-    # Look up exception & get status code
-    conf = current_app.config.foca.exceptions  # type: ignore[attr-defined]
+    logger.warning(f"Config: {connexion.request.state.config}")
+    logger.warning(f"ExceptionConfig: {connexion.request.state.config.exceptions}")
+    conf: ExceptionConfig = connexion.request.state.config.exceptions
+    assert conf.mapping is not None
     exc = type(exception)
+    logger.warning(f"Exception type: {exc}")
+    logger.warning(f"Exception type dir: {dir(exc)}")
+    logger.warning(f"Exception: {exception}")
+    logger.warning(f"Exception dir: {dir(exception)}")
+    logger.warning(f"Exception args: {exception.args}")
+    logger.warning(f"Exception args dir: {dir(exception.args)}")
+    logger.warning(f"Exception detail: {exception.detail}")
+    logger.warning(f"Exception detail dir: {dir(exception.detail)}")
+    logger.warning(f"Exception headers: {exception.headers}")
+    logger.warning(f"Exception headers dir: {dir(exception.headers)}")
+    logger.warning(f"Exception status code: {exception.status_code}")
+    logger.warning(f"Exception status code dir: {dir(exception.status_code)}")
     if exc not in conf.mapping:
         exc = Exception
     try:
@@ -226,8 +289,8 @@ def _problem_handler_json(exception: Exception) -> Response:
                 exc=exception,
                 format=conf.logging.value
             )
-        return Response(
-            status=500,
+        return ConnexionResponse(
+            status_code=500,
             mimetype="application/problem+json",
         )
     # Log exception JSON & traceback
@@ -253,8 +316,69 @@ def _problem_handler_json(exception: Exception) -> Response:
                 key_sequence=member,
             ))
     # Return response
-    return Response(
-        response=dumps(keep),
-        status=status,
+    return ConnexionResponse(
+        status_code=status,
+        mimetype="application/problem+json",
+        body=dumps(keep),
+    )
+
+
+def exception_handler_builtin(
+    request: ConnexionRequest,
+    exception: Exception,
+) -> ConnexionResponse:
+    """Return JSON-formatted error response for built-in exceptions.
+
+    Return the ``500`` error response defined in the OpenAPI specification for
+    the operation during which the exception was raised. If not defined, return
+    the corresponding default response for that operation instead. If also not
+    defined, or if the exception is not associated with a specific operation,
+    return the defined global default response. If none of these are defined,
+    return a generic RFC 9457-compliant error response.
+
+    Args:
+        request: Connexion request object.
+        exception: Raised exception.
+
+    Returns:
+        JSON-formatted error response.
+    """
+    logger.warning(
+        f'Exception "{exception}" of type "{type(exception)}" raised by '
+        '"builtins.Exception" handler.'
+    )
+    return ConnexionResponse(
+        status_code=500,
+        mimetype="application/problem+json",
+        body=dumps({"title": "Internal Server Error", "status": 500}),
+    )
+
+
+def exception_handler_connexion(
+    request: ConnexionRequest,
+    exception: Exception,
+) -> ConnexionResponse:
+    logger.warning(
+        f'Exception "{exception}" of type "{type(exception)}" raised by '
+        '"connexion.exceptions.ProblemException" handler.'
+    )
+    return ConnexionResponse(
+        status_code=500,
+        mimetype="application/problem+json",
+        body=dumps({"title": "Internal Server Error", "status": 500}),
+    )
+
+
+def exception_handler_starlette(
+    request: ConnexionRequest,
+    exception: Exception,
+) -> ConnexionResponse:
+    logger.warning(
+        f'Exception "{exception}" of type "{type(exception)}" raised by '
+        '"starlette.exceptions.HTTPException" handler.'
+    )
+    return ConnexionResponse(
+        status_code=500,
         mimetype="application/problem+json",
+        body=dumps({"title": "Internal Server Error", "status": 500}),
     )
diff --git a/foca/factories/connexion_app.py b/foca/factories/connexion_app.py
index b39b7f6c..81935bc3 100644
--- a/foca/factories/connexion_app.py
+++ b/foca/factories/connexion_app.py
@@ -1,10 +1,11 @@
 """Factory for creating and configuring Connexion application instances."""
 
+import contextlib
 from inspect import stack
 import logging
-from typing import Optional
+from typing import AsyncIterator, Optional
 
-from connexion import App
+from connexion import FlaskApp, ConnexionMiddleware
 
 from foca.models.config import Config
 
@@ -12,7 +13,7 @@
 logger = logging.getLogger(__name__)
 
 
-def create_connexion_app(config: Optional[Config] = None) -> App:
+def create_connexion_app(config: Optional[Config] = Config()) -> FlaskApp:
     """Create and configure Connexion application instance.
 
     Args:
@@ -21,57 +22,18 @@ def create_connexion_app(config: Optional[Config] = None) -> App:
     Returns:
         Connexion application instance.
     """
+
+    @contextlib.asynccontextmanager
+    async def config_handler(app: ConnexionMiddleware) -> AsyncIterator:
+        yield {"config": config}
+
     # Instantiate Connexion app
-    app = App(
+    app = FlaskApp(
         __name__,
-        skip_error_handlers=True,
+        lifespan=config_handler,
     )
 
     calling_module = ':'.join([stack()[1].filename, stack()[1].function])
     logger.debug(f"Connexion app created from '{calling_module}'.")
 
-    # Configure Connexion app
-    if config is not None:
-        app = __add_config_to_connexion_app(
-            app=app,
-            config=config,
-        )
-
-    return app
-
-
-def __add_config_to_connexion_app(
-    app: App,
-    config: Config,
-) -> App:
-    """Replace default Flask and Connexion settings with FOCA configuration
-    parameters.
-
-    Args:
-        app: Connexion application instance.
-        config: Application configuration.
-
-    Returns:
-        Connexion application instance with updated configuration.
-    """
-    conf = config.server
-
-    # replace Connexion app settings
-    app.host = conf.host
-    app.port = conf.port
-    app.debug = conf.debug
-
-    # replace Flask app settings
-    app.app.config['DEBUG'] = conf.debug
-    app.app.config['ENV'] = conf.environment
-    app.app.config['TESTING'] = conf.testing
-
-    logger.debug('Flask app settings:')
-    for (key, value) in app.app.config.items():
-        logger.debug('* {}: {}'.format(key, value))
-
-    # Add user configuration to Flask app config
-    setattr(app.app.config, 'foca', config)
-
-    logger.debug('Connexion app configured.')
     return app
diff --git a/foca/foca.py b/foca/foca.py
index a249c896..50a20ebd 100644
--- a/foca/foca.py
+++ b/foca/foca.py
@@ -5,22 +5,22 @@
 from typing import Optional
 
 from celery import Celery
-from connexion import App
+from connexion import FlaskApp
 
-from foca.security.access_control.register_access_control import (
-    register_access_control,
-)
-from foca.security.access_control.constants import (
-    DEFAULT_SPEC_CONTROLLER,
-    DEFAULT_ACCESS_CONTROL_DB_NAME,
-    DEFAULT_ACESS_CONTROL_COLLECTION_NAME,
-)
 from foca.api.register_openapi import register_openapi
 from foca.config.config_parser import ConfigParser
 from foca.database.register_mongodb import register_mongodb
 from foca.errors.exceptions import register_exception_handler
-from foca.factories.connexion_app import create_connexion_app
 from foca.factories.celery_app import create_celery_app
+from foca.factories.connexion_app import create_connexion_app
+from foca.security.access_control.constants import (
+    DEFAULT_SPEC_CONTROLLER,
+    DEFAULT_ACCESS_CONTROL_DB_NAME,
+    DEFAULT_ACESS_CONTROL_COLLECTION_NAME,
+)
+from foca.security.access_control.register_access_control import (
+    register_access_control,
+)
 from foca.security.cors import enable_cors
 
 # Get logger instance
@@ -83,32 +83,31 @@ def __init__(
             logger.info(f"Configuration file '{self.config_file}' parsed.")
         else:
             logger.info("Default app configuration used.")
+        logger.info(f"App configuration: {self.conf}.")
 
-    def create_app(self) -> App:
+    def create_app(self) -> FlaskApp:
         """Set up and initialize FOCA-based Connexion app.
 
         Returns:
             Connexion application instance.
         """
         # Create Connexion app
-        cnx_app = create_connexion_app(self.conf)
+        connexion_app = create_connexion_app(self.conf)
         logger.info("Connexion app created.")
 
-        # Register error handlers
-        cnx_app = register_exception_handler(cnx_app)
-        logger.info("Error handler registered.")
-
         # Enable cross-origin resource sharing
         if self.conf.security.cors.enabled is True:
-            enable_cors(cnx_app.app)
+            connexion_app = enable_cors(connexion_app)
             logger.info("CORS enabled.")
-        else:
-            logger.info("CORS not enabled.")
+
+        # Register error handler
+        connexion_app = register_exception_handler(connexion_app)
+        logger.info("Error handler registered.")
 
         # Register OpenAPI specs
         if self.conf.api.specs:
-            cnx_app = register_openapi(
-                app=cnx_app,
+            connexion_app = register_openapi(
+                app=connexion_app,
                 specs=self.conf.api.specs,
             )
         else:
@@ -116,49 +115,49 @@ def create_app(self) -> App:
 
         # Register MongoDB
         if self.conf.db:
-            cnx_app.app.config.foca.db = register_mongodb(
-                app=cnx_app.app,
+            self.conf.db = register_mongodb(
+                app=connexion_app.app,
                 conf=self.conf.db,
             )
             logger.info("Database registered.")
         else:
             logger.info("No database support configured.")
 
-        # Register permission management and casbin enforcer
-        if self.conf.security.auth.required:
-            if (
-                self.conf.security.access_control.api_specs is None
-                or self.conf.security.access_control.api_controllers is None
-            ):
-                self.conf.security.access_control.api_controllers = (
-                    DEFAULT_SPEC_CONTROLLER
-                )
-
-            if self.conf.security.access_control.db_name is None:
-                self.conf.security.access_control.db_name = (
-                    DEFAULT_ACCESS_CONTROL_DB_NAME
-                )
-
-            if self.conf.security.access_control.collection_name is None:
-                self.conf.security.access_control.collection_name = (
-                    DEFAULT_ACESS_CONTROL_COLLECTION_NAME
-                )
-
-            cnx_app = register_access_control(
-                cnx_app=cnx_app,
-                mongo_config=self.conf.db,
-                access_control_config=self.conf.security.access_control,
-            )
-        else:
-            if (
-                self.conf.security.access_control.api_specs
-                or self.conf.security.access_control.api_controllers
-            ):
-                logger.error(
-                    "Please enable security config to register access control."
-                )
-
-        return cnx_app
+        # # Register permission management and Casbin enforcer
+        # if self.conf.security.auth.required:
+        #     if (
+        #         self.conf.security.access_control.api_specs is None
+        #         or self.conf.security.access_control.api_controllers is None
+        #     ):
+        #         self.conf.security.access_control.api_controllers = (
+        #             DEFAULT_SPEC_CONTROLLER
+        #         )
+
+        #     if self.conf.security.access_control.db_name is None:
+        #         self.conf.security.access_control.db_name = (
+        #             DEFAULT_ACCESS_CONTROL_DB_NAME
+        #         )
+
+        #     if self.conf.security.access_control.collection_name is None:
+        #         self.conf.security.access_control.collection_name = (
+        #             DEFAULT_ACESS_CONTROL_COLLECTION_NAME
+        #         )
+
+        #     connexion_app, self.conf.db = register_access_control(
+        #         cnx_app=connexion_app,
+        #         mongo_config=self.conf.db,
+        #         access_control_config=self.conf.security.access_control,
+        #     )
+        # else:
+        #     if (
+        #         self.conf.security.access_control.api_specs
+        #         or self.conf.security.access_control.api_controllers
+        #     ):
+        #         logger.error(
+        #             "Please enable security config to register access control."
+        #         )
+
+        return connexion_app
 
     def create_celery_app(self) -> Celery:
         """Set up and initialize FOCA-based Celery app.
@@ -166,32 +165,32 @@ def create_celery_app(self) -> Celery:
         Returns:
             Celery application instance.
         """
-        # Create Connexion app
-        cnx_app = create_connexion_app(self.conf)
-        logger.info("Connexion app created.")
-
-        # Register error handlers
-        cnx_app = register_exception_handler(cnx_app)
-        logger.info("Error handler registered.")
-
-        # Register MongoDB
-        if self.conf.db:
-            cnx_app.app.config.foca.db = register_mongodb(
-                app=cnx_app.app,
-                conf=self.conf.db,
-            )
-            logger.info("Database registered.")
-        else:
-            logger.info("No database support configured.")
-
-        # Create Celery app
-        if self.conf.jobs:
-            celery_app = create_celery_app(cnx_app.app)
-            logger.info("Support for background tasks set up.")
-        else:
-            raise ValueError(
-                "No support for background tasks configured. Please use the "
-                "'jobs' keyword section in your configuration file."
-            )
-
-        return celery_app
+        # # Create Connexion app
+        # connexion_app = create_connexion_app(self.conf)
+        # logger.info("Connexion app created.")
+
+        # # Register error handlers
+        # connexion_app = register_exception_handler(connexion_app)
+        # logger.info("Error handler registered.")
+
+        # # Register MongoDB
+        # if self.conf.db:
+        #     self.conf.db = register_mongodb(
+        #         app=connexion_app.app,
+        #         conf=self.conf.db,
+        #     )
+        #     logger.info("Database registered.")
+        # else:
+        #     logger.info("No database support configured.")
+
+        # # Create Celery app
+        # if self.conf.jobs:
+        #     celery_app = create_celery_app(connexion_app.app)
+        #     logger.info("Support for background tasks set up.")
+        # else:
+        #     raise ValueError(
+        #         "No support for background tasks configured. Please use the "
+        #         "'jobs' keyword section in your configuration file."
+        #     )
+
+        # return celery_app
diff --git a/foca/security/access_control/access_control_server.py b/foca/security/access_control/access_control_server.py
index e55474a6..98013975 100644
--- a/foca/security/access_control/access_control_server.py
+++ b/foca/security/access_control/access_control_server.py
@@ -4,12 +4,14 @@
 
 from typing import (Dict, List)
 
+import connexion
 from flask import (request, current_app)
-from pymongo.collection import Collection
 from werkzeug.exceptions import (InternalServerError, NotFound)
 
-from foca.utils.logging import log_traffic
+from foca.models.config import Config
 from foca.errors.exceptions import BadRequest
+from foca.utils.db import get_client
+from foca.utils.logging import log_traffic
 
 logger = logging.getLogger(__name__)
 
@@ -62,22 +64,20 @@ def putPermission(
     """
     request_json = request.json
     if isinstance(request_json, dict):
-        app_config = current_app.config
+        app_config: Config = connexion.request.state.config
         try:
-            security_conf = \
-                app_config.foca.security  # type: ignore[attr-defined]
-            access_control_config = \
-                security_conf.access_control  # type: ignore[attr-defined]
-            db_coll_permission: Collection = (
-                app_config.foca.db.dbs[  # type: ignore[attr-defined]
-                    access_control_config.db_name]
-                .collections[access_control_config.collection_name].client
+            access_control_config = app_config.security.access_control
+            assert access_control_config.db_name is not None
+            assert access_control_config.collection_name is not None
+            client = get_client(
+                config=app_config,
+                db=access_control_config.db_name,
+                collection=access_control_config.collection_name
             )
-
             permission_data = request_json.get("rule", {})
             permission_data["id"] = id
             permission_data["ptype"] = request_json.get("policy_type", None)
-            db_coll_permission.replace_one(
+            client.replace_one(
                 filter={"id": id},
                 replacement=permission_data,
                 upsert=True
@@ -102,19 +102,19 @@ def getAllPermissions(limit=None) -> List[Dict]:
     Returns:
         List of permission dicts.
     """
-    app_config = current_app.config
-    access_control_config = \
-        app_config.foca.security.access_control  # type: ignore[attr-defined]
-    db_coll_permission: Collection = (
-        app_config.foca.db.dbs[  # type: ignore[attr-defined]
-            access_control_config.db_name
-        ].collections[access_control_config.collection_name].client
+    app_config: Config = connexion.request.state.config
+    access_control_config = app_config.security.access_control
+    assert access_control_config.db_name is not None
+    assert access_control_config.collection_name is not None
+    client = get_client(
+        config=app_config,
+        db=access_control_config.db_name,
+        collection=access_control_config.collection_name
     )
-
     if not limit:
         limit = 0
     permissions = list(
-        db_coll_permission.find(
+        client.find(
             filter={},
             projection={'_id': False}
         ).sort([('$natural', -1)]).limit(limit)
@@ -146,16 +146,16 @@ def getPermission(
     Returns:
         Permission data for the given id.
     """
-    app_config = current_app.config
-    access_control_config = \
-        app_config.foca.security.access_control  # type: ignore[attr-defined]
-    db_coll_permission: Collection = (
-        app_config.foca.db.dbs[  # type: ignore[attr-defined]
-            access_control_config.db_name
-        ].collections[access_control_config.collection_name].client
+    app_config: Config = connexion.request.state.config
+    access_control_config = app_config.security.access_control
+    assert access_control_config.db_name is not None
+    assert access_control_config.collection_name is not None
+    client = get_client(
+        config=app_config,
+        db=access_control_config.db_name,
+        collection=access_control_config.collection_name
     )
-
-    permission = db_coll_permission.find_one(filter={"id": id})
+    permission = client.find_one(filter={"id": id})
     if permission is None:
         raise NotFound
     del permission["_id"]
@@ -182,17 +182,16 @@ def deletePermission(
     Returns:
         Delete permission identifier.
     """
-    app_config = current_app.config
-    access_control_config = \
-        app_config.foca.security.access_control  # type: ignore[attr-defined]
-    db_coll_permission: Collection = (
-        app_config.foca.db.dbs[  # type: ignore[attr-defined]
-            access_control_config.db_name
-        ].collections[access_control_config.collection_name].client
+    app_config: Config = connexion.request.state.config
+    access_control_config = app_config.security.access_control
+    assert access_control_config.db_name is not None
+    assert access_control_config.collection_name is not None
+    client = get_client(
+        config=app_config,
+        db=access_control_config.db_name,
+        collection=access_control_config.collection_name
     )
-
-    del_obj_permission = db_coll_permission.delete_one({'id': id})
-
+    del_obj_permission = client.delete_one({'id': id})
     if del_obj_permission.deleted_count:
         return id
     else:
diff --git a/foca/security/access_control/constants.py b/foca/security/access_control/constants.py
index eda4d1ed..aa6aed0d 100644
--- a/foca/security/access_control/constants.py
+++ b/foca/security/access_control/constants.py
@@ -1,5 +1,4 @@
-"""File to store permission based constants.
-"""
+"""File to store permission based constants."""
 
 DEFAULT_ACCESS_CONTROL_DB_NAME = "access_control_db"
 DEFAULT_ACESS_CONTROL_COLLECTION_NAME = "policy_rules"
diff --git a/foca/security/access_control/register_access_control.py b/foca/security/access_control/register_access_control.py
index ffd1894a..41287928 100644
--- a/foca/security/access_control/register_access_control.py
+++ b/foca/security/access_control/register_access_control.py
@@ -6,7 +6,7 @@
 from pathlib import Path
 from typing import (Callable, Optional, Tuple)
 
-from connexion import App
+from connexion import FlaskApp
 from connexion.exceptions import Forbidden
 from flask import current_app
 from flask.wrappers import Response
@@ -30,10 +30,10 @@
 
 
 def register_access_control(
-    cnx_app: App,
+    cnx_app: FlaskApp,
     mongo_config: Optional[MongoConfig],
     access_control_config: AccessControlConfig
-) -> App:
+) -> Tuple[FlaskApp, MongoConfig]:
     """Register access control configuration with flask app.
 
     Args:
@@ -66,8 +66,6 @@ def register_access_control(
     else:
         mongo_config.dbs[access_control_db] = access_db_conf
 
-    cnx_app.app.config.foca.db = mongo_config
-
     # Register new database for access control.
     add_new_database(
         app=cnx_app.app,
@@ -88,11 +86,11 @@ def register_access_control(
         access_control_config=access_control_config
     )
 
-    return cnx_app
+    return cnx_app, mongo_config
 
 
 def register_permission_specs(
-    app: App,
+    app: FlaskApp,
     access_control_config: AccessControlConfig
 ):
     """Register open api specs for permission management.
@@ -140,10 +138,10 @@ def register_permission_specs(
 
 
 def register_casbin_enforcer(
-    app: App,
+    app: FlaskApp,
     access_control_config: AccessControlConfig,
     mongo_config: MongoConfig
-) -> App:
+) -> FlaskApp:
     """Method to add casbin permission enforcer.
 
     Args:
diff --git a/foca/security/auth.py b/foca/security/auth.py
index d0f3468c..7d5caccf 100644
--- a/foca/security/auth.py
+++ b/foca/security/auth.py
@@ -4,9 +4,10 @@
 import logging
 from typing import (Dict, Iterable, List, Optional)
 
+import connexion
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
-from flask import current_app, request
+from flask import request
 import jwt
 from jwt.exceptions import InvalidKeyError
 import requests
@@ -36,7 +37,7 @@ def validate_token(token: str) -> Dict:
     oidc_config_claim_public_keys: str = 'jwks_uri'
 
     # Fetch security parameters
-    conf = current_app.config.foca.security.auth  # type: ignore[attr-defined]
+    conf = connexion.request.state.config.security.auth
     add_key_to_claims: bool = conf.add_key_to_claims
     allow_expired: bool = conf.allow_expired
     audience: Optional[Iterable[str]] = conf.audience
@@ -125,8 +126,7 @@ def validate_token(token: str) -> Dict:
     for key, val in claims.items():
         req_headers[key] = val
     req_headers['user_id'] = claims[claim_identity]
-    request.headers = \
-        ImmutableMultiDict(req_headers)  # type: ignore[assignment]
+    request.headers = ImmutableMultiDict(req_headers)  # type: ignore
 
     # Return token info
     return {
diff --git a/foca/security/cors.py b/foca/security/cors.py
index bf110178..8c9bcf34 100644
--- a/foca/security/cors.py
+++ b/foca/security/cors.py
@@ -1,20 +1,28 @@
 """Resources for cross-origin resource sharing (CORS)."""
 
 import logging
-from flask import Flask
 
-from flask_cors import CORS
+from connexion import FlaskApp
+from connexion.middleware import MiddlewarePosition
+from starlette.middleware.cors import CORSMiddleware
 
 # Get logger instance
 logger = logging.getLogger(__name__)
 
 
-def enable_cors(app: Flask) -> None:
-    """Enables cross-origin resource sharing (CORS) for Flask application
-    instance.
+def enable_cors(app: FlaskApp) -> FlaskApp:
+    """Enables cross-origin resource sharing (CORS).
 
     Args:
-        app: Flask application instance.
+        app: Connexion application instance.
     """
-    CORS(app)
-    logger.debug('Enabled CORS for Flask app.')
+    app.add_middleware(
+        CORSMiddleware,  # type: ignore
+        position=MiddlewarePosition.BEFORE_EXCEPTION,
+        allow_origins=["*"],
+        allow_credentials=True,
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+    logger.debug('Enabled CORS for Connexion app.')
+    return app
diff --git a/foca/utils/db.py b/foca/utils/db.py
index e44604c5..494bd061 100644
--- a/foca/utils/db.py
+++ b/foca/utils/db.py
@@ -5,6 +5,8 @@
 from bson.objectid import ObjectId
 from pymongo.collection import Collection
 
+from foca.models.config import Config
+
 
 def find_one_latest(collection: Collection) -> Optional[Mapping[Any, Any]]:
     """Return newest document, stripped of `ObjectId`.
@@ -39,3 +41,31 @@ def find_id_latest(collection: Collection) -> Optional[ObjectId]:
         return collection.find().sort([('_id', -1)]).limit(1).next()['_id']
     except StopIteration:
         return None
+
+
+def get_client(config: Config, db: str, collection: str) -> Collection:
+    """Get client for a given database collection.
+
+    Args:
+        config: Application configuration.
+
+    Raises:
+        AssertionError: If the database configuration is invalid or incomplete,
+            or the specified database or collection is missing.
+    """
+    assert config.db is not None, "Database configuration is missing."
+    assert config.db.dbs is not None, "Database connections are missing."
+
+    my_db = config.db.dbs.get(db)
+    assert my_db is not None, f"Database '{db}' is missing."
+    assert my_db.collections is not None, f"Database '{db}' has no collections."
+
+    my_collection = my_db.collections.get(collection)
+    assert my_collection is not None, (
+        f"Database collection '{collection}' is missing."
+    )
+    assert my_collection.client is not None, (
+        f"Database collection '{collection}' is missing."
+    )
+
+    return my_collection.client
diff --git a/foca/utils/logging.py b/foca/utils/logging.py
index 2a47aaa9..f0d650f2 100644
--- a/foca/utils/logging.py
+++ b/foca/utils/logging.py
@@ -1,7 +1,7 @@
 """Utility functions for logging."""
 
 import logging
-from connexion import request
+from flask import request
 from functools import wraps
 from typing import (Callable, Optional)
 
diff --git a/requirements.txt b/requirements.txt
index d5d98faf..ef0b661c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,17 +1,13 @@
-addict~=2.2
-celery~=5.2
-connexion~=2.11
-cryptography~=42.0
-Flask~=2.2
-flask-authz~=2.5
-Flask-Cors~=4.0
+addict~=2.4
+celery~=5.4
+connexion[swagger-ui,flask,uvicorn]~=3.0
+#cryptography~=42.0
+flask-authz~=2.6
 Flask-PyMongo~=2.3
 pydantic~=2.7
-PyJWT~=2.4
+#PyJWT~=2.4
 pymongo~=4.7
-PyYAML~=6.0
-requests~=2.31
-swagger-ui-bundle~=0.0
-toml~=0.10
-typing~=3.7
-Werkzeug~=2.2
+#PyYAML~=6.0
+#requests~=2.31
+#toml~=0.10
+#typing~=3.7
diff --git a/requirements_dev.txt b/requirements_dev.txt
index 7e66e879..7961f93b 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -5,6 +5,7 @@ mypy>=0.991
 mypy-extensions>=0.4.4
 pylint>=3.2
 pytest>=7.4
+pytest-mock>=3.14
 python-semantic-release>=9.7
 types-PyYAML
 types-requests
diff --git a/setup.py b/setup.py
index c29774bf..fc4e9a37 100644
--- a/setup.py
+++ b/setup.py
@@ -12,9 +12,9 @@
     long_description = _file.read()
 
 install_requires = []
-req = root_dir / 'requirements.txt'
-with open(req, "r") as _file:
-    install_requires = _file.read().splitlines()
+# req = root_dir / 'requirements.txt'
+# with open(req, "r") as _file:
+#     install_requires = _file.read().splitlines()
 
 docs_require = []
 req = root_dir / 'requirements_docs.txt'
@@ -34,7 +34,7 @@
 
 setup(
     name="foca",
-    version=__version__,  # noqa: F821
+    version=__version__,  # noqa: F821  # type: ignore
     description=(
         "Archetype for OpenAPI microservices based on Flask and Connexion"
     ),
diff --git a/templates/config.yaml b/templates/config.yaml
index 68e29506..b27c9b58 100644
--- a/templates/config.yaml
+++ b/templates/config.yaml
@@ -1,13 +1,13 @@
 # FOCA CONFIGURATION
 
-# Available in app context as attributes of `current_app.config.foca`
+# Available in app context as attributes of `connexion.request.state.config`
 # Automatically validated via FOCA
 # Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html
 
 # SERVER CONFIGURATION
 # Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.ServerConfig
 server:
-  host: '0.0.0.0'
+  host: "0.0.0.0"
   port: 8080
   debug: True
   environment: development
@@ -17,8 +17,8 @@ server:
 # EXCEPTION CONFIGURATION
 # Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.ExceptionConfig
 exceptions:
-  required_members: [['msg'], ['status']]
-  status_member: ['status']
+  required_members: [["msg"], ["status"]]
+  status_member: ["status"]
   exceptions: my_app.exceptions.exceptions
 
 # SECURITY CONFIGURATION
@@ -66,14 +66,14 @@ db:
             - keys:
                 id: 1
               options:
-                'unique': True
+                "unique": True
 
 # WORKER CONFIGURATION
 # Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.JobsConfig
 jobs:
   host: rabbitmq
   port: 5672
-  backend: 'rpc://'
+  backend: "rpc://"
   include:
     - my_app.tasks.my_task_1
     - my_app.tasks.my_task_2
@@ -98,16 +98,15 @@ log:
     level: 10
     handlers: [console]
 
-
 # CUSTOM APP CONFIGURATION
-# Available in app context as attributes of `current_app.config.foca`
+# Available in app context as attributes of `connexion.request.state.config`
 
 # Can be validated by FOCA by passing a Pydantic model class to the
 # `custom_config_model` parameter in the `foca.Foca()` constructor
 custom:
-  my_param: 'some_value'
+  my_param: "some_value"
 
 # Any other sections/parameters are *not* validated by FOCA; if desired,
 # validate parameters in app
 custom_params_not_validated:
-  my_other_param: 'some_other_value'
+  my_other_param: "some_other_value"
diff --git a/tests/errors/test_errors.py b/tests/errors/test_exceptions.py
similarity index 55%
rename from tests/errors/test_errors.py
rename to tests/errors/test_exceptions.py
index 93eb91dd..a1cbfb84 100644
--- a/tests/errors/test_errors.py
+++ b/tests/errors/test_exceptions.py
@@ -5,10 +5,11 @@
 from copy import deepcopy
 import json
 
-from flask import (Flask, Response)
-from connexion import App
+from connexion import FlaskApp
+from connexion.lifecycle import ConnexionResponse
 import pytest
 
+
 from foca.errors.exceptions import (
     _exc_to_str,
     _exclude_key_nested_dict,
@@ -50,11 +51,30 @@ class UnknownException(Exception):
     pass
 
 
+@pytest.fixture
+def foca_app():
+    """Create a Connexion app."""
+    app = FlaskApp(__name__)
+    setattr(app.app.config, 'foca', Config())
+    return app
+
+
+@pytest.fixture
+def mock_connexion_request(mocker):
+    request = mocker.MagicMock()
+    request.headers = {}
+    request.args = {}
+    request.json = {}
+    request.method = "GET"
+    request.path = "/test_endpoint"
+    return request
+
+
 def test_register_exception_handler():
     """Test exception handler registration with Connexion app."""
-    app = App(__name__)
+    app = FlaskApp(__name__)
     ret = register_exception_handler(app)
-    assert isinstance(ret, App)
+    assert isinstance(ret, FlaskApp)
 
 
 def test__exc_to_str():
@@ -100,53 +120,60 @@ def test__exclude_key_nested_dict():
     assert res == EXPECTED_EXCLUDE_RESULT
 
 
-def test__problem_handler_json():
+def test__problem_handler_json(foca_app, mock_connexion_request):
     """Test problem handler with instance of custom, unlisted error."""
-    app = Flask(__name__)
-    setattr(app.config, 'foca', Config())
-    EXPECTED_RESPONSE = app.config.foca.exceptions.mapping[Exception]
-    with app.app_context():
-        res = _problem_handler_json(UnknownException())
-        assert isinstance(res, Response)
-        assert res.status == '500 INTERNAL SERVER ERROR'
+    EXPECTED_RESPONSE = (
+        foca_app.app.config.foca.exceptions.mapping[Exception]  # type: ignore
+    )
+    with foca_app.app.app_context():
+        res = _problem_handler_json(mock_connexion_request, UnknownException())
+        assert isinstance(res, ConnexionResponse)
+        assert res.status_code == 500
         assert res.mimetype == "application/problem+json"
-        response = json.loads(res.data.decode('utf-8'))
+        response = json.loads(res.body)  # type: ignore
         assert response == EXPECTED_RESPONSE
 
 
-def test__problem_handler_json_no_fallback_exception():
+def test__problem_handler_json_no_fallback_exception(
+    foca_app,
+    mock_connexion_request
+):
     """Test problem handler; unlisted error without fallback."""
-    app = Flask(__name__)
-    setattr(app.config, 'foca', Config())
-    del app.config.foca.exceptions.mapping[Exception]
-    with app.app_context():
-        res = _problem_handler_json(UnknownException())
-        assert isinstance(res, Response)
-        assert res.status == '500 INTERNAL SERVER ERROR'
+    del foca_app.app.config.foca.exceptions.mapping[Exception]  # type: ignore
+    with foca_app.app.app_context():
+        res = _problem_handler_json(mock_connexion_request, UnknownException())
+        assert isinstance(res, ConnexionResponse)
+        assert res.status_code == 500
         assert res.mimetype == "application/problem+json"
-        response = res.data.decode("utf-8")
-        assert response == ""
+        response = res.body
+        assert response is None
 
 
-def test__problem_handler_json_with_public_members():
+def test__problem_handler_json_with_public_members(
+    foca_app,
+    mock_connexion_request
+):
     """Test problem handler with public members."""
-    app = Flask(__name__)
-    setattr(app.config, 'foca', Config())
-    app.config.foca.exceptions.public_members = PUBLIC_MEMBERS
-    with app.app_context():
-        res = _problem_handler_json(UnknownException())
-        assert isinstance(res, Response)
-        assert res.status == '500 INTERNAL SERVER ERROR'
+    foca_app.app.config.foca.exceptions.public_members = (  # type: ignore
+        PUBLIC_MEMBERS
+    )
+    with foca_app.app.app_context():
+        res = _problem_handler_json(mock_connexion_request, UnknownException())
+        assert isinstance(res, ConnexionResponse)
+        assert res.status_code == 500
         assert res.mimetype == "application/problem+json"
 
 
-def test__problem_handler_json_with_private_members():
+def test__problem_handler_json_with_private_members(
+    foca_app,
+    mock_connexion_request
+):
     """Test problem handler with private members."""
-    app = Flask(__name__)
-    setattr(app.config, 'foca', Config())
-    app.config.foca.exceptions.private_members = PRIVATE_MEMBERS
-    with app.app_context():
-        res = _problem_handler_json(UnknownException())
-        assert isinstance(res, Response)
-        assert res.status == '500 INTERNAL SERVER ERROR'
+    foca_app.app.config.foca.exceptions.private_members = (  # type: ignore
+        PRIVATE_MEMBERS
+    )
+    with foca_app.app.app_context():
+        res = _problem_handler_json(mock_connexion_request, UnknownException())
+        assert isinstance(res, ConnexionResponse)
+        assert res.status_code == 500
         assert res.mimetype == "application/problem+json"
diff --git a/tests/security/test_cors.py b/tests/security/test_cors.py
index 376b120f..ed375cdf 100644
--- a/tests/security/test_cors.py
+++ b/tests/security/test_cors.py
@@ -1,15 +1,33 @@
 """Unit test for security.cors.py"""
 
-from unittest.mock import patch
+import functools
 
-from flask import Flask
+from connexion import FlaskApp
+from starlette.middleware.cors import CORSMiddleware
 
 from foca.security.cors import enable_cors
 
 
 def test_enable_cors():
     """Test that CORS is called with app as a parameter."""
-    app = Flask(__name__)
-    with patch('foca.security.cors.CORS') as mock_cors:
-        enable_cors(app)
-        mock_cors.assert_called_once_with(app)
+    app = FlaskApp(__name__)
+    expected_middleware = functools.partial(
+        CORSMiddleware,
+        allow_origins=["*"],
+        allow_credentials=True,
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+    assert not any(
+        isinstance(item, functools.partial)
+        and item.func == expected_middleware.func
+        and item.args == expected_middleware.args
+        for item in app.middleware.middlewares
+    )
+    enable_cors(app)
+    assert any(
+        isinstance(item, functools.partial)
+        and item.func == expected_middleware.func
+        and item.args == expected_middleware.args
+        for item in app.middleware.middlewares
+    )