Skip to Content
Technical Articles
Author's profile photo Pieter Janssens

Solving the complexity of using default-env.json in a project with multiple tiers or targets

Note: as David Kunz pointed out below, for CAP projects it is now possible to use cds bind, which can load the environment variables from the BTP CF services based on the current targeted CF space. This makes the use of default-env.json obsolete. 🥳


When I locally want to run a Node.js application that requires one or more services running on SAP BTP, I use a default-env.json file to make these services’ credentials available to the modules in my application. It is named default-env.json by convention, which is determined by the @sap/xsenv package. Modules like @sap/approuter,  @sap-cloud-sdk/, @sap/cds, etc., which are using @sap/xsenv all have a common way to setup the runtime environment with these variables.

It can be cumbersome to switch between different default-env.json files whenever another system (read BTP subaccount) has to be targeted for running the application locally. In this short blog post I will show you how I solve this complexity by using git hooks.

The problem

Imagine having a project that has 3 git branches with the names ‘development’, ‘acceptance’ and ‘production’. These individual branches keep track of the differences in source code between each tier. By default, when making a change to the default-env.json file, the change will be tracked and when merging one branch (development) into another (acceptance), it will overwrite the defaul-env.json with the credentials from ‘development’.

To avoid overwriting the changes in the default-env.json files when merging changes, you could look into merge policies using .gitattributes, however this has disadvantages. For example, this would only ignore changes during merge when there are conflicts, but more importantly, that would require committing the default-env.json (containing sensitive credentials) to a source code repository.

Solution: use of a git post-checkout hook

I create a directory ‘./default-envs’ where I put one subfolder for each branch name that I have. Then I create a git post-checkout script that, based on the name of the current branch, will get the correct default-env.json and copy that to the intended location(s) for running the project locally.

Based on the example project above, I would have the following structure for a CAP project:

> sample-project
  > .githooks
    - post-checkout
  > default-envs
    > development
      - default-env.json
    > acceptance
      - default-env.json
    > production
      - default-env.json
  > app
  > srv
  - .gitignore
  - package.json

These are the contents of my post-checkout script:


BRANCH=`git reflog | awk 'NR==1{ print $8; exit }'`

cp "./default-envs/$BRANCH/default-env.json" "./default-env.json"
cp "./default-envs/$BRANCH/default-env.json" "./app/default-env.json"

exit 0

Note: by default ‘cp’ will overwrite and that is what we want to happen here.

To make this approach work in a collaborative environment, I move the git hook script from the default ./.git/hooks location to a ./.githooks directory as part of the project structure. To make git aware that it need to look for hooks in that directory I execute this command once:

$ git config --local core.hooksPath .githooks/

I make sure that this instruction of the initial setup is documented in my project’s and to add the following lines to .gitignore:




This setup works very well for my development flow. Let me know in the comments if you were struggling with similar issues or if you have another approach to this that works for you.

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo David Kunz
      David Kunz


      There's also a newly introduced cds bind command which works without storing any credentials to disk.

      Uwe Klinger  and Joerg Mann did a great job here!

      Author's profile photo Pieter Janssens
      Pieter Janssens
      Blog Post Author

      Hi David,


      I didn't know about that feature, thanks for pointing out!

      I see that it also works for a standalone approuter in ./app, so this covers my local env needs for CAP projects. I'll add this info as a note to the top of this blog.


      Best regards,



      Author's profile photo David Kunz
      David Kunz

      Great, thanks Pieter!