Exception handling and releasing of OracleDB connection
There really isn't too much to say about your posted code.

Answering your specific question: 

Yes, the connection will always be released as either your then or your catch method will always run. However, if res.json({data: result.rows}); (or more complex code once you have developed further) throws, your code will try to release the connection twice. This will likely result in an uncaught promise rejection. You can solve this problem a couple different ways. 


Use the second callback parameter of the .then method.

con.execute('SELECT * FROM BTO_MDS.AlARMS', {}, { maxRows: 22000 })
    .then(result =&gt; {
        con.release();
        res.json({data: result.rows});
    }, ex =&gt; {
        con.release();
        res.status(500).json({message: ex.message});
    })

Use async / await and take advantage of the finally in try..catch..finally chain. This solution also has the benefit of avoiding the duplicate code sending error messages. I like this solution the best, but it would probably be even better to have an execute method on your Database class that handles releasing the connection for you.

export let getTest = async (req: Request, res: Response, next: NextFunction) =&gt; {
    const db = new Database();

    try {
        const con = await db.getConnection();
        try {
            const result = await con.execute('SELECT * FROM BTO_MDS.AlARMS', {}, { maxRows: 22000 });
            res.json({ data: result.rows });
        } finally {
            con.release();
        }
    } catch (ex) {
        res.status(500).json({ message: ex.message });
    }
};

For the future: Promise.prototype.finally is coming, currently a stage 3 proposal. Proposal. I wouldn't use this just yet.

db.getConnection()
    .then(con =&gt; {
        return con.execute('SELECT * FROM BTO_MDS.AlARMS', {}, { maxRows: 22000 })
            .then(result =&gt; {
                res.json({ data: result.rows });
            })
            .finally(() =&gt; {
                con.release();
            })
    })
    .catch(ex =&gt; {
        res.status(500).json({ message: ex.message });
    });



The trick to using promises effectively is to return them whenever possible. The original function can be somewhat simplified by re-throwing the error and returning the connection promise. This doesn't help a ton but it does remove the duplicate res.status(500) line, making it easier to change in the future if necessary. 

db.getConnection()
    .then(con =&gt; {
        return con.execute('SELECT * FROM BTO_MDS.AlARMS', {}, { maxRows: 22000 })
            .then(result =&gt; {
                con.release();
                res.json({ data: result.rows });
            }, ex =&gt; {
                con.release();
                throw ex;
            })
    })
    .catch(ex =&gt; {
        res.status(500).json({ message: ex.message });
    });