# Working with docker

Every time you start a new project you need a different version of the tools or packages that you use. It is manageable by some version managers, but there is a better way. You can start every project and install all the dependencies on a docker container. I will show you how to do it without getting into the whole boring stuff that docker for beginners tutorials cover.

First things first. You need to have docker installed on your system.

This post covers the example react application. I'm planning to add at least one more (rails application with Postgres database).

Create a folder that will store the application code, then create a `Dockerfile` file. A `Dockerfile` is a file that describes the container that you want to build.

```bash
mkdir react_on_docker
cd react_on_docker
```

As a first step we just want to install an Alpine Linux with nodeJS installed. Here is the content of the `Dockerfile` that does that:

```docker
FROM node:17.5.0-alpine3.15
WORKDIR /code
```

The first line says that we want to install container based on a `node` image and we want to use `17.5.0-alpine3.15` version of the image. You can find those images and tags (latest version etc.) on docker hub website (eg. [node docker hub](https://hub.docker.com/_/node)). 

The second line instructs Docker that we want to create and start working in the `code` directory of the container. The `code` directory in docker container will be the same as the main project directory on your system.

Next thing that we need to setup is `docker-compose.yml` file. Using this file we can describe a few services (containers) that are dependent on each other. `docker-compose` allows us to use one command to run the whole application (eg. frontend and backend). There are a lot of use cases, but let's skit the theory for now.

```yaml
version: "3.9"
services:
  app:
    build: .
    command: npm start
    volumes:
      - .:/code
    ports:
      - "3005:3000"
```

As you can see above we described a service called `app` that has the build instructions in the `.` directory (current). The command that will start this service is `npm start`, we added a volume `/code`. The ports section mapped the 3005 port from our system to 3000 port from the inside of the container (`create-react-app` sets 3000 as a default port for the application). 

Next thing to do is to initialise the react application using `create-react-app`. But we don't want to install node on our system, we want to use node that is installed inside the container.

```bash
docker-compose build
docker-compose run --rm app npx create-react-app app
```

To run something in a docker container we need to create it first. The first is responsible for that. It builds an image according to instructions from `Dockerfile`.

Second command starts the container, but it is not using the default command stated in `docker-compose.yml`, but executes the `npx create-react-app app` command to install react application in the `app` folder. Using the `--rm` option we ask docker to remove the container after the specified command finishes.

If you know a bit more about docker-compose you will probably ask yourself why we haven’t used the `docker-compose exec` here? The answer is simple: Because the service is not running yet. What to do then to run the service? We need to run `docker-compose up`, but it won’t work, because it tries to start application using `npm start` command. That's why we used the run command. We just need to set up the app, we don't have anything to run yet.

Now we can change the `WORKDIR` in the `Dockerfile` to be `/code/app` because create-react-app installed react in the `app` folder and this will be our working directory now.

```docker
FROM node:17.5.0-alpine3.15
WORKDIR /code/app
```

To run the application, we need to rebuild the container (because we did some changes in `Dockerfile`). And then start it:

```bash
docker-compose build
docker-compose up
```

Now the application is running. You can visit the `[localhost:3005](http://localhost:3005)` (or whatever port you specified in your `docker-compose`) address and you will see the placeholder react application. You can start creating you own web application now.

If you want to run scripts from the inside of the container (examples given below), run:

```bash
docker-compose exec app sh
docker-compose exec app npm install package
docker-compose exec app npm run test
```

To be able to setup the application faster we can add the node packages installation step to the `Dockerfile`. Thanks to that step, when somebody clones our application, they can just run `docker-compose build` and everything will be setup. Without that step, after any change in the `package.json`, you need to execute `npm install` command inside the container.

Let’s get to it. Add following changes to the `Dockerfile`. The first two added lines are responsible for copying the package files into the docker container. Third added line runs installation of `npm` packages. 

```bash
FROM node:17.5.0-alpine3.15
WORKDIR /code/app

COPY /app/package.json ./
COPY /app/package-lock.json ./
RUN npm install
```

As you should remember you need to build and run the container once again, because you changed the `Dockerfile`.

Now you can develop your application. You don’t need to build the container after each change to see it. If you will add a package or change some config you will probably need to restart the container or build it once again.

That’s it. You haven’t installed anything (but Docker) on you system. The drawback of this solution is that the setup of the application is a lot more difficult and time consuming. I hope that using this tutorial it won’t be so hard and you will use it for other projects as well.

Thank you!
