You should be using
wp_get_post_terms() queries the db to get post term info, where as
get_the_terms() returns the post term objects directly from the post term cache and only queries the db if that info is not available.
By default, all post's terms are cached by the query being executed.
I cannot comment (crappy mobile), but there is no native way to return post objects with their respective post terms and meta data in one single object. However, when any instance of
WP_Query is executed either through the main query,
get_posts() or a new instance of
WP_Query, by default,
WP_Query returns all relative post terms and post meta belonging to the posts in the
$posts property of the query object.
These post terms and post meta are however not returned as part of the post object, those data are directly stored into their relative caches, namely the post term cache and the post meta cache, and they remain there until queried, in the case of post terms, via functions like
get_the_terms(). These caches are set up in order to avoid extra db calls being made to return a post/posts term and/or meta data. What that means is, whether you make 1 or a 1000 calls to
get_the_terms(), you would still have made zero db calls.
So to conclude, when you think you are making extra db calls to return post term data, you are in fact not making any db calls at all, all post term data is returned from the term cache