Syntax highlighter header

Saturday 18 March 2023

Creating nginx docker image for a react application

 Recently we were trying to deploy a PWA application developed in react on production. We wanted to create a docker image for our application for an easy deployment. We did not want to run node process in our docker container because it will consume a lot of memory. So, we decided to compile our application as static files and serve these files using nginx to make it lightweight. 

We were developing the application on a Windows machine and deploy on a Linux machine so we decided to do the compilation in a docker build. We used following code in our Dockerfile.

FROM node:12 as mynode
ARG environment
# Create app directory
WORKDIR /usr/src/app
# Copy source code to build context
COPY . .
RUN npm install
RUN npm run build:${environment}

# Start from a clean nginx image
FROM nginx
#Copy the build generated file to target image
COPY --from=mynode /usr/src/app/build/ /usr/share/nginx/html
#delete the default nginx config
RUN rm /etc/nginx/conf.d/default.conf
#copy the required nginx config file to image
COPY ui-nginx.conf /etc/nginx/conf.d

Following is the content of ui-nginx.conf file:

server {
listen 80 default_server;
server_name localhost;
location / {
        root /usr/share/nginx/html;
        access_log /var/log/nginx/ui-access.log;
        error_log /var/log/nginx/ui-error.log;
        index index.html index.htm;
        try_files $uri /index.html;
        }
}

Following part is used in package.json file for making the build:

  "scripts": {
    "start": "env-cmd -f ./environments/.env.development react-scripts start",
    "build": "env-cmd -f ./environments/.env.development react-scripts build",
    "start:development": "env-cmd -f ./environments/.env.development react-scripts start",
    "build:development": "env-cmd -f ./environments/.env.development react-scripts build",
    "start:production": "env-cmd -f ./environments/.env.production react-scripts start",
    "build:production": "env-cmd -f ./environments/.env.production react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }

Following command is used for making the build. We use different environment variables for development and production environment. We pass required build as build args.

docker build --build-arg environment=production -t <imagename>:<tag>  . 

The above command generates the production build. Using the above method we generated a docker image which is less than 160MB in size. It can be deployed in AWS ECS container of 512MB size.

Sunday 12 March 2023

Streaming output asynchronously in spring boot

We are using spring boot in our application and return ResponseEntity<String> from our service method. The drawback of this is that we need to load whole result in the memory and due to memory requirement increases. We had a requirement to dump data from database to as CSV to the client. The total amount of data can be huge and we can't afford to load whole data in memory. 

What we were looking for was a way to dump data to result in chunks so that memory requirement does not increase. While searching the internet I came across this article which explains asynchronous processing of the http requests in spring boot. This page was really helpful in solving our problem. I am not copying any part of this article in my post. You can read the article directly at

Friday 10 March 2023

Client is configured for secret but secret was not received

Recently we were trying to authenticate using username and password of the user using a AWS Cognito client which was configured with a secret. We got the following error:

Client <client ID> is configured for secret but secret was not received (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: NotAuthorizedException; Request ID: e42de11b-380b-4935-b97e-1069ab925a35; Proxy: null)

We tried may ways for passing Cognito client secret to authenticate API but nothing worked. After trying a lot and searching internet I came to the following page which tells about calculating secret hash.

https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html#cognito-user-pools-computing-secret-hash

Following is the code for calculating secret hash. This code is taken from above link.

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
 
public static String calculateSecretHash(String userPoolClientId, String userPoolClientSecret, String userName) {
    final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
    
    SecretKeySpec signingKey = new SecretKeySpec(
            userPoolClientSecret.getBytes(StandardCharsets.UTF_8),
            HMAC_SHA256_ALGORITHM);
    try {
        Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
        mac.init(signingKey);
        mac.update(userName.getBytes(StandardCharsets.UTF_8));
        byte[] rawHmac = mac.doFinal(userPoolClientId.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(rawHmac);
    } catch (Exception e) {
        throw new RuntimeException("Error while calculating ");
    }
}

The secret hash calculated hash needs to passed to authentication API along with username and password.

Map<String, String> authParams = new LinkedHashMap<>() {{
	put("USERNAME", username);
	put("PASSWORD", password);
	put("SECRET_HASH", secret_hash);
}};

Using this method we were able to finally generate authentication token for Cognito client for which client secret is configured.