Transform Client - Server command-line interface Node.js application into a Docker container

My Node.js application consists of two separate components: an Express.js server and a CLI built with vanilla JavaScript and inquirer.js. These components communicate with each other through a REST API. The main reason for this setup is to adhere to the SOLID single responsibility principle, allowing flexibility to potentially switch out clients using different frameworks like React.js while maintaining the same server-database structure.

The app is currently functioning smoothly, but I aim to simplify its launch process by having a single command from the root folder that contains both client and server directories. After experimenting with npm packages on my own, I decided to delve into research to explore the possibility of utilizing Docker. A helpful article shed light on my query, prompting me to seek further information about Docker.

Thus far, I have created Dockerfiles for each component and attempted to utilize docker-compose to manage the entire environment. Drawing insights from a relevant Stackoverflow Answer, I refined my approach working with docker-compose. Additionally, I came across an informative article highlighting dockerization strategies for applications similar to mine.

Despite exploring various resources, I am still seeking guidance on how to establish a command to run my CLI app from its root folder. Although the solutions presented seem promising, integrating them within my project remains a challenge despite referencing official documentation.

This personal project serves as part of my backend development studies, and I aspire to enhance my proficiency with Docker. Rather than solely obtaining a solution to my immediate dilemma, I consider this an opportune moment to engage with the community, reaffirming concepts I've acquired. Please feel free to pinpoint any areas I should revisit for further study.

  • Docker image: Standardizes the development environment to promote consistency among team members and avoid compatibility discrepancies.
  • Docker container: Contains all necessary elements for an application to function within a specified environment, encompassing source code and dependencies
  • Docker files: Record sequences of steps followed by Docker to generate a container, including image creation, dependency installation, and source code allocation
  • Docker compose file: Orchestrates and manages the development environment settings.

While the discussion has primarily revolved around the development environment, production-related aspects have yet to be addressed. Specifically, if users are expected to interact with my CLI application, they would require Docker installed on their machines for seamless operation. Is Docker indeed the ultimate panacea in this scenario?

Objectives:

  1. Create a streamlined command enabling users to launch the app from the root directory
  2. Prioritize server initiation before client activation; prevent client execution without an active server
  3. Run the server discretely in the background, eliminating the need for additional terminal windows
  4. Elegantly conclude both client and server operations upon SIGINT or SIGTERM

Based on existing knowledge, it appears that docker-compose effectively addresses objectives 2, 3, and 4, although uncertainties persist regarding the first goal.

Project Structure:

root
  docker-compose.yaml
  client
    Dockerfile
    node_modules
    package.json
    package-lock.json
    ...source code
  server
    Dockerfile
    node_modules
    package.json
    package-lock.json
    ...source code

Should clarification be required on any aspect of my query, please don't hesitate to reach out.

Update

An important realization pointed out by Dave Maze emphasized the necessity of including both client and server Dockerfiles along with the docker-compose.yaml file to provide a comprehensive overview.

./client/Dockerfile

FROM node:latest
WORKDIR /client
COPY package.json package.json
RUN npm install
RUN npm install dateformat
COPY . .
ENTRYPOINT ["npm", "start"]

./server/Dockerfile

FROM node:latest
WORKDIR /server
COPY package.json package.json
RUN npm install
RUN npm install express-async-handler
ENV NODE_ENV=${NODE_ENV}
COPY . .
ENTRYPOINT ["npm","start"]

./docker-compose.yaml

version: "3.3"
services:
  backend:
    build:
      context: "./server"
    container_name: server
    ports:
    - "3000:3000"
    command: node ./server/src/index.js
  frontend:
    depends_on:
      - backend
    build: ./client

Recent adjustments based on feedback received have ensured correct image creation via Docker Compose. However, operational execution deviates from the intended design, an area warranting elaboration within the initial question description.

Design:

The CLI program facilitates blog post creation, editing, basic administrative tasks under the author profile, and enables readers to comment and peruse posts via the reader profile. This functionality is contingent upon specific command line flags:

blogcli -a grants access to the author profile. blogcli -r provides access to the reader profile. blogcli or incorrect options yield help prompts.

Running docker-compose initiates program execution without facilitating flag input during frontend startup. While the EntryPoint directive in ./client/Dockerfile governs expected behavior, I ponder alternative methods granting users executing docker-compose up the ability to apply desired flags directly. Your insights on this matter would be greatly appreciated.

Answer №1

There are a few reasons why running interactive CLI applications via Compose, and often not in a container at all, is not typically done. One main reason is that attaching to the stdin/stdout of a Compose container requires an extra command. Additionally, Compose expects its containers to remain running, so you may need to constantly restart the container with docker-compose up -d each time you want to use the CLI.

For a CLI application connected to a backend server, it's recommended to run only the backend in a container. The Compose definition provided for the backend should suffice, and can possibly be simplified further to:

version: '3.8'
services:
  backend:
    build: ./server
    ports:
      - '3000:3000'

Start the server using docker-compose up -d. With the specified ports:, the server can be accessed at http://localhost:3000/ from the host system outside of a container on most modern Docker setups.

Run the CLI directly on the host machine, just as you do currently:

cd client
npm install
./blogcli -h http://localhost:3000/ -a
./blogcli -h http://localhost:3000/ -r

If there is a specific need to run the backend in a container, consider using docker-compose run. This allows running a one-time command based on an existing container definition, replacing its original command:.

services:
  backend: # same as above
  cli:
    build: ./client
    depends_on:
      - backend
    entrypoint: /app/blogcli
    environment:
      BACKEND_URL: http://backend:3000/
    profiles:
      - cli
docker-compose run --rm cli -a
docker-compose run --rm cli -r

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Animating a Bootstrap 4 card to the center of the screen

I am attempting to achieve the following effect: Display a grid of bootstrap 4 cards Upon clicking a button within a card, animate it by rotating 180 degrees, adjusting its height/width from 400px - 350px to the entire screen, and positioning it at the c ...

Tips for executing a Python function from JavaScript, receiving input from an HTML text box

Currently, I am facing an issue with passing input from an HTML text box to a JavaScript variable. Once the input is stored in the JavaScript variable, it needs to be passed to a Python function for execution. Can someone provide assistance with this pro ...

The attempt to connect with react-native-fetch-blob has not been successful

How do I resolve the issue of displaying a PDF from my assets folder in an Expo, React Native project using react-native-pdf? While scanning folders for symlinks, encountered an error with RNFetchBlob library in my project directory. The automatic linki ...

The functionality of findDOMNode is no longer supported

My website, built using React, features a calendar that allows users to select a date and time range with the help of the react-advanced-datetimerange-picker library. However, I encounter some warnings in my index.js file due to the use of <React.Stric ...

Showing text instantly upon clicking a radio button, in real-time

I'm working with a set of radio buttons that are linked to numbers ranging from 1 to 24. I need to show the selected number in another part of the page when a radio button is clicked. What would be the best way to achieve this? ...

Using jQuery to send a GET request to the current page with specified parameters

Within my application, hosted on a PHP page, I am aiming to trigger a GET request upon selecting an option from a dropdown menu. The URL of the page is: www.mydomain.it/admin/gest-prenotazioni-piazzola.php I intend to utilize jQuery to execute this GET r ...

Is it possible for me to reconstruct the "reducer" each time the useReducer hook is rendered?

Here is an example taken from: https://reactjs.org/docs/hooks-reference.html#usereducer const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; ...

Click on a div in AngularJS to be directed to a specific URL

I'm currently working on developing an Angular mobile app and I want to be able to navigate to a specific URL, like www.google.com, when a particular div is clicked. Unfortunately, I'm still new to the world of Angular and struggling to achieve t ...

Nodemailer seems to be having trouble delivering emails at the moment

I've configured the transformer like this: var transporter = nodemailer.createTransport({ service: "Gmail", host: "smtp.gmail.com", port: 587, secure: false, auth: { user: process.env.GMAIL, pass: process.env.GPASSWORD, }, }); Her ...

Comparison: Express and Socket.io

Recently, I started delving into socket.io while also having prior experience with express. It's clear that socket.io allows for bidirectional communication whereas express is limited to client to server interactions. This got me thinking, would it m ...

Improving Page Load Speed with HTML Caching: Strategies for Enhancing Performance when over half of the data transferred is for navigation menus

I manage a complex and expansive website that contains a significant amount of repetitive HTML elements such as the navigation menu and top ribbon. Loading a single page on my site can be resource-intensive, with up to 300KB of data required, half of whic ...

Ways to combine duplicate entries within a column using Ruby on Rails?

I need some help with a filtering issue related to sign names. I am trying to merge the content together if there is more than one name of the sign. You can refer to the image attached for better clarity, where I have two server names but I only want to di ...

The error message displays "window.addEventListener is not a function", indicating that

Recently, I've been dealing with this code snippet: $(document).ready(function(){ $(window).load(function() { window.addEventListener("hashchange", function() { scrollBy(0, -50);}); var shiftWindow = function() { scrollBy(0, - ...

Problems encountered when transferring information from jQuery to PHP through .ajax request

Hey there! I am currently working with Yii and facing an issue while trying to pass some data to a controller method called events. This is how my jQuery ajax call looks like: var objectToSend = { "categories" : [selectedOption],"datefrom" : month + "" + ...

What steps should I follow to run my JavaScript application locally on Linux Mint?

Currently, I am diligently following a tutorial and ensuring that each step is completed accurately. My goal is to locally host my javascript app at localhost:3000. Unfortunately, I am facing difficulties as every attempt to run npm run dev results in an e ...

Manipulate the value of the <input> element when focused through JavaScript

After I focus on the input field, I was expecting to see Bond-Patterson, but instead, I am only getting Bond. What could be causing this discrepancy and how can it be fixed? $('input[name="surname"]').attr("onfocus", "this.placeholder='Bo ...

Converting php array submitted from a form using ajax

I have a form on my website that collects user input and sends it to an email address using php. The form includes a checkbox input where users can select multiple options, which are then stored in an array. However, when the form is submitted, the email r ...

Node receiving empty array as result after processing post request

My current task involves testing the post method on Postman. Strangely, every time I post the result it shows an empty array []. Upon further investigation by console logging on the node side, it also returns an empty array. CREATE TABLE users ( user_ ...

My data is not appearing with ng-repeat or ng-bind

One issue I am encountering is that my ng-repeat / ng-bind is not displaying the data within $scope.articles, even though I am getting the expected data in the console. To help identify the problem more easily, I have created a code snippet below: var A ...

Implementing email validation on the view layer for the yii framework using JavaScript

<script type="text/javascript"> function validate() { var email = document.getElementById('email').value; var phone = document.getElementById('phone').value; var emailFilter = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([ ...