How not expose my backend
The layer that you're missing is nginx proxy. See my toy Mafia Game project here how to do it:

Shell deployment project (uses either Docker compose or k8s) - https://github.com/taleodor/mafia-deployment
Vue.js frontend - https://github.com/taleodor/mafia-vue
Express.js backend - https://github.com/taleodor/mafia-express
(Optional) detailed write-up about CI/CD and how it's built from DevOps prospective - https://itnext.io/building-kubernetes-cicd-pipeline-with-github-actions-argocd-and-reliza-hub-e7120b9be870

The missing piece in your case is nginx proxy that you use in your UI.
So what you need to do is to define some location for your api - i.e. /api. Then your proxy it via nginx - see my nginx configuration for the project above here - https://github.com/taleodor/mafia-vue/tree/master/nginx. At the same time it's highly recommended for performance to deploy static resources on the same nginx.
Use Dockerfile to define environment variables and defaults, i.e. in my project - https://github.com/taleodor/mafia-vue/blob/master/Dockerfile
Finally, you can update configuration via environment variables in your docker-compose yaml or k8s definitions.