Dynamically Update Angular BASE URL Using Docker Env Variables
Tags: javascript, angular, docker, typescript
Published: 2022-09-12 19:22:46

In Angular the BASE/API URLs are be stored environment files which are present under **src/environments** directory. Usually we can create new file for each environment.

- environment.ts
- environment.dev.ts
- environment.test.ts
- environment.stage.ts
- environment.prod.ts

**environment.ts**
```
import packageInfo from '../../package.json';

export const environment = {
	production: false,
	BASE_URL: '//localhost:4200',
	VERSION: packageInfo.version,
};
```

During the build, a configuration flag is provided to swap the default `environment.ts` file with `environment.<CONFIGURATION>.ts` based on the flag
```
npm run build -- --configuration=stage
```

The problem with this approach is you can't reuse same build for multiple environments and promote DEV to TEST then to STAGE eventually to PROD since BASE_URL is hard coded during build. To prevent this we need to replace the BASE_URL in `main.*.js` file generated by Angular during the Docker container start up. 

First lets look at standard **Dockerfile** that follows multi stage Dockerfile. To better understand this, refer my [other post](https://dev.to/this-is-angular/multi-stage-docker-builds-with-angular-and-nginx-4j9d).

**Dockerfile**
```
// See my other post for Build stage, so skipping it here

############  User Nginx alpine image  ############
FROM nginx:stable-alpine

# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy nginx config file
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf

# Copy dist folder fro build stage to nginx public folder
COPY --from=builder /app/dist /usr/share/nginx/html

# Start NgInx services
CMD ["nginx", "-g", "daemon off;"]

```

In this we can add single that will update the BASE_URL during Docker container/service. Let's say out BASE_URL/API_URL is `employee-dev.example.com`, then it can be replaced with UNIX `sed` command as shown below

```
CMD sed -i "s/employee-dev.example.com/employee-$CONFIGURATION.example.com/g" /usr/share/nginx/html/main.*.js && nginx -g 'daemon off;'
```

So the updated Dockerfile looks as below
**Dockerfile (Updated)**
```
// See my other post for Build stage, so skipping it here

############  User Nginx alpine image  ############
FROM nginx:stable-alpine

# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy nginx config file
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf

# Copy dist folder fro build stage to nginx public folder
COPY --from=builder /app/dist /usr/share/nginx/html

# Start NgInx services
CMD sed -i "s/employee-dev.example.com/employee-$CONFIGURATION.example.com/g" /usr/share/nginx/html/main.*.js && nginx -g 'daemon off;'

```
and you can start the container with Docker run command

```
docker run -p 4000:80 -e CONFIGURATION=test <IMAGE_ID>
```
---
You can also use Docker ENTRYPOINT to achieve the same result. To do this using ENTRYPOINT, create new start.sh script file and copy the command. 

**start.sh**
```
#!/bin/sh
sed -i "s/employee-dev.example.com/employee-$CONFIGURATION.example.com/g" /usr/share/nginx/html/main.*.js
nginx -g 'daemon off;'
```
and make sure to copy this file, make it executable and refer in ENTRYPOINT

**Dockerfile (Updated with ENTRYPOINT)**
```
######  User Nginx alpine image  ######
FROM nginx:stable-alpine
ENV CONFIGURATION='dev'
# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy nginx config file
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf

# Copy dist folder fro build stage to nginx public folder
COPY /dist /usr/share/nginx/html
COPY start.sh /usr/share/nginx/html
RUN chmod +x /usr/share/nginx/html/start.sh

# Start NgInx service
ENTRYPOINT ["./usr/share/nginx/html/start.sh", "$CONFIGURATION"]

``` 
and start the container with Docker run command

```
docker run -p 4000:80 -e CONFIGURATION=test <IMAGE_ID>
```

---
If you are not using Docker you can also same approach, since `sed` is simple UNIX command