Use CarboneJS into Docker Container 🐳
Tags: docker, javascript, node, tutorial
Published: 2020-04-26 09:12:10

## Introduction

This tutorial is about using CarboneJS into Docker containers and generating an invoice as PDF. It can be useful to split the Carbone process into containers to generate the reports.

This article is part of a series:
- [Carbone Turorial - Fast and Easy report generator (as fast as a spaceship 🚀)](https://dev.to/steeve/carbone-tutorial-fast-and-easy-report-generator-as-fast-as-a-spaceship-54bc)
- [Generate Dynamic sheets from ODS templates](https://dev.to/steeve/generate-dynamic-sheets-from-ods-templates-and-node-with-carbonejs-4c5m)
- CarboneJS into Docker Container 🐳 (here we are).

## Preparing the template

The following document `invoice_template.odt` has been made with Libre Office. All markers `{d.}` in the document will be replaced by data. 

![Invoice Template](https://thepracticaldev.s3.amazonaws.com/i/ttmaa0qk1j3xleu8h481.png)

## Preparing the data to inject

Bellow is the JSON data to inject into the template. The key names should match the template markers. The file is called `invoice_data.json`:
```json
{
  "id": 42, # the corresponding marker to use in the template is {d.id}
  "date": 1492012745, # {d.date}
  "company": {
    "name": "awesomeCompany", # {d.company.name}
    "address": "77 Bleecker Street", # {d.company.address}
    "city": "Manhattan, New York",
    "postalCode": 10012
  },
  "customer": {
    "name": "customerName",
    "address": "2 Random Street",
    "city": "Far Far Away",
    "postalCode": 85000
  },
  "products": [
    {
      "name": "product 1", # {d.products[i=0].name}
      "priceUnit": 0.1,
      "quantity": 10,
      "priceTotal": 1
    },
    {
      "name": "product 2",
      "priceUnit": 0.2,
      "quantity": 20,
      "priceTotal": 4
    }
  ],
  "total": 5
}
```
## Carbone setup

Firstly, install the Carbone dependency:
```
# NPM
$ npm install carbone --save
# YARN
$ yarn add carbone
```
In the `index.js`, insert the code to generate the report:
```js
/** 1 . dependencies */
const carbone = require('carbone');
const fs = require('fs');

/** 2 . input template file name and output file name */
const _fileInputName = './invoice_template.odt';

/** 3 . Data to inject */
const _data = require("./invoice_data.json")

/** 4 . Options object */
const _options = {
  convertTo: 'pdf',
  reportName: 'invoice_{d.id}.pdf'
};

/** 5 . Call carbone render function */
carbone.render(_fileInputName, _data, _options, (err, result, reportName) => {
  if (err) {
    console.log(err);
  } else {
    fs.writeFileSync('./build/' + reportName, result);
  }
  process.exit();
});
```
That's a lot of code, here is the explanation:
1. It instantiates Carbone.
2. Definition of the filename template.
3. Import the JSON data.
4. An optional object can be passed to Carbone to define complement data, variables, translations, and [more...](https://github.com/Ideolys/carbone/#api-reference) In this case, the `convertTo` option defines the file export type and `reportName` the report filename. 
5. The Carbone render function generates the report by taking as argument the template file name, the data object, and the options.  If it succeeds, the final report is created with `fs.writeFileSync`. If the rendering fails, the callback function responds by filling the `err` variable. In this case, the error will be printed. To make the program stop at the end, `process.exit()` is necessary.

## Preparing the Dockerfile
Before diving into Containers, you may need Docker basics before continuing. You can learn from the [official Docker documentation 🐳](https://docs.docker.com/get-started/) <b>OR</b>  awesome [dev.to tutorials ⚡️](https://dev.to/search?q=docker).

To make Carbone work properly, the environment needs to be defined:
- Required: Carbone is based on NodeJs (Node 8 minimum).
- Optional: Libre Office has to be installed to generate the report into PDF format. [Learn more about PDF export](https://github.com/Ideolys/carbone#pdf-generation-document-conversion).

Let's create a `Dockerfile` with these specifications:
```docker
# 1
FROM ideolys/carbone-env-docker

# 2
ENV DIR /app

# 3
WORKDIR ${DIR}

# 4
COPY . ${DIR}

# 5
RUN npm install

# 6
CMD [ "node", "index.js" ]
```
Details about this Dockerfile:
1. "ideolys/carbone-env-docker" is a Node 8 image with Libre Office already Installed. You can check the DockerFile on [Github](https://github.com/Ideolys/carbone-env-docker/blob/master/Dockerfile). During the `docker run` command, the image will be pulled from the [Docker Hub](https://hub.docker.com/r/ideolys/carbone-env-docker).
2. the `ENV` instruction defines the app directory into environment variables
3. the `WORKDIR` instruction sets the working directory to `/app`
4. the `COPY` instruction copies the local files and directory into the working directory
5. Command to install the npm dependencies
6. the `CMD` instruction is used to define the default command to run. In our case, `node index.js` is executed.

Before continuing, here is the actual project directory:

![Project directory](https://thepracticaldev.s3.amazonaws.com/i/s127207iabx3tbfom5ke.png)

Run the following command on your terminal to build the image from the Dockerfile :

```shell
$ docker build . -t myapp-carbone:1.0
```
On success, the message "Successfully tagged myapp-carbone:1.0" appears at the end of the process. 

To verify if the image has been built, run `docker image ls`:
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/qcxrfabdi9w2lmw3pb5r.png)

Finally, run the image in a container through this command:
```shell
$ docker run -v $(pwd)/build:/app/build myapp-carbone:1.0
$ # `-v` argument is used to persist and save the generated report into a `build` folder
```
After running the container, the generated report appears on the build folder 🎉 Let's take a look:

![Generated report](https://thepracticaldev.s3.amazonaws.com/i/263ovz6cwqk1asehqhkg.png)

The use of Docker can be pushed further, for example, use a `template` directory as a volume to share templates with the container.

## Conclusion

First, We covered how to set up a Carbone project with an invoice ODT template and the data to inject. Thereafter, we created the Dockerfile to make Carbone work properly. Finally, we executed the container to generate the invoice.

Do not hesitate to contact me. If you need information about Carbone, here is an article to learn the basis: [Carbone Turorial - Fast and Easy report generator (as fast as a spaceship 🚀)](https://dev.to/steeve/carbone-tutorial-fast-and-easy-report-generator-as-fast-as-a-spaceship-54bc)

Leave a like to support my article or follow me to be notified of my next articles 🔥

Thanks for reading!