<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mac</title>
    <description>The latest articles on Forem by Mac (@macru).</description>
    <link>https://forem.com/macru</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F16133%2Fb2e9221e-3945-4b3c-b15f-20b1f9f3bf51.jpg</url>
      <title>Forem: Mac</title>
      <link>https://forem.com/macru</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/macru"/>
    <language>en</language>
    <item>
      <title>Dockerizing React app and Express API with MongoDB</title>
      <dc:creator>Mac</dc:creator>
      <pubDate>Wed, 26 Feb 2020 22:26:47 +0000</pubDate>
      <link>https://forem.com/macru/dockerizing-react-app-and-express-api-with-mongodb-2pdm</link>
      <guid>https://forem.com/macru/dockerizing-react-app-and-express-api-with-mongodb-2pdm</guid>
      <description>&lt;p&gt;Simple guide on how to move your React app, Express API and MongoDB to Docker using containers.&lt;/p&gt;

&lt;p&gt;For sake of simplicity I just assume that you have working front-end and back-end as well as connected database.&lt;/p&gt;

&lt;p&gt;Best idea is to have both api and client repos in one folder. You can have one remote repo with both of them or use two separate remote repos and then combine them with parent remote using git submodules. That’s how I did that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtqitsfc3s7xhkq5db08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtqitsfc3s7xhkq5db08.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  React App
&lt;/h2&gt;

&lt;p&gt;I used Create-React-App (CRA) with TypeScript for my project. It was simple blog with couple views.&lt;/p&gt;

&lt;p&gt;First thing is to create Dockerfile in client root folder. To do that just type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open file and lets fill it out. I’m using TypeScript with my CRA to first I have to build my application and then take what I get and host it as static files. To achieve that we’ll got with &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;two stage docker build&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First stage is using node to build app. I use alpine version as it’s the lightest so our container’ll be tiny.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:12-alpine as builder

WORKDIR /app
COPY package.json /app/package.json
RUN npm install
COPY . /app
RUN npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s how beginning of the Dockerfile looks like. We’re using node:alpine as builder, then setting up working directory to /app, that’s gonna create new folder in our container. We copy our package.json to new folder in the container and install all packages. Next, we copy everything from /services/client folder and paste it to our container. Last bit of that step is to build everything.&lt;/p&gt;

&lt;p&gt;Now we have to host our freshly created build. To do that we’re gonna use nginx, again as alpine version to cut on size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx:1.16.0-alpine
COPY --from=builder /app/build /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We copy the build from the previous step and paste it to nginx folder. Then expose port 80, that’s gonna be port on which our container’ll be listening for connections. Last line is to start nginx.&lt;/p&gt;

&lt;p&gt;That’s all for client part. Whole Dockerfile should look like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:12-alpine as build

WORKDIR /app
COPY package.json /app/package.json
RUN npm install
COPY . /app
RUN npm run build

FROM nginx:1.16.0-alpine
COPY --from=build /app/build /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Express API
&lt;/h2&gt;

&lt;p&gt;API is quite simple as well, RESTful routing to create posts, auth etc. Lets start with creating Dockerfiler in api root folder, same as in the previous part.&lt;/p&gt;

&lt;p&gt;I used ES6 features so I have to compile everything to vanilla JS to run it and I went with Babel. As you can guess, that’s gonna be two stage build, again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:12-alpine as builder

WORKDIR /app
COPY package.json /app/package.json
RUN apk --no-cache add --virtual builds-deps build-base python
RUN npm install
COPY . /app
RUN npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s very similar to client’s Docker file so I won’t be explaining it again. There’s only one difference, though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN apk --no-cache add --virtual builds-deps build-base python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used &lt;a href="https://www.npmjs.com/package/bcrypt" rel="noopener noreferrer"&gt;bcrypt&lt;/a&gt; to hash my passwords before saving them to the database. Very popular package but it has some problems when using apline images. You might find errors similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node-pre-gyp WARN Pre-built binaries not found for bcrypt@3.0.8 and node@12.16.1 (node-v72 ABI, musl) (falling back to source compile with node-gyp)

npm ERR! Failed at the bcrypt@3.0.8 install script.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s well know problem and the &lt;a href="https://github.com/kelektiv/node.bcrypt.js/wiki/Installation-Instructions#docker" rel="noopener noreferrer"&gt;solution&lt;/a&gt; is to install additional packages and python before installing npm packages.&lt;/p&gt;

&lt;p&gt;Next stage, similarly as for the client, is to take the build api and run it with node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:12-alpine

WORKDIR /app
COPY --from=builder /app/dist /app
COPY package.json /app/package.json
RUN apk --no-cache add --virtual builds-deps build-base python
RUN npm install --only=prod

EXPOSE 808
CMD ["npm", "start"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One exception is to install only production packages. We don’t need Babel anymore as everything was complied in step one. Then we expose port 8080 to listen to requests and start node.&lt;/p&gt;

&lt;p&gt;Whole Dockerfile should looks like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:12-alpine as builder

WORKDIR /app
COPY package.json /app/package.json
RUN apk --no-cache add --virtual builds-deps build-base python
RUN npm install
COPY . /app
RUN npm run build

FROM node:12-alpine
WORKDIR /app
COPY --from=builder /app/dist /app
COPY package.json /app/package.json
RUN apk --no-cache add --virtual builds-deps build-base python
RUN npm install --only=prod

EXPOSE 808
CMD ["npm", "start"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker-compose
&lt;/h2&gt;

&lt;p&gt;Last step is to combine the api and client containers with MongoDB container. To do that we use docker-compose file, that is placed in our parent repo root directory as it have to get access to both client and api’s Dockerfiles.&lt;/p&gt;

&lt;p&gt;Let’s create docker-compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should ended up with file structure like the one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtqitsfc3s7xhkq5db08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frtqitsfc3s7xhkq5db08.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the docker-compose file with following code and I’ll explain it afterwards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3"

services:
  api:
    build: ./services/api
    ports:
      - "8080:8080"
    depends_on:
      - db
    container_name: blog-api

  client:
    build: ./services/client
    ports:
      - "80:80"
    container_name: blog-client

  db:
    image: mongo
    ports:
      - "27017:27017"
    container_name: blog-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s really simple as that. We have three services, client, api and mongo. There is no Dockerfile for mongo, Docker’ll download image from it’s hub and create container out of it. That means our database it perishable but for beginning is enough.&lt;/p&gt;

&lt;p&gt;In the api and client we have build key, which points to Dockerfile locations for both services respectively (root folder). Ports bind container port assigned in Dockerfile to our docker-compose network port so containers can talk to each other. The api service also has depends_on key, it tells Docker to wait with starting it until the db container is fully running. Because of that we’re gonna avoid connection errors from the api container.&lt;/p&gt;

&lt;p&gt;One more bit for MongoDB. In our codebase for the back-end we have to update mongo connection string. Usually we point to localhost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongodb://localhost:27017/blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with docker-compose it have to point to container name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongodb://blog-db:27017/blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final touch is to run everything with following command in parent repo root directory (where the docker-compose.yml is):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all. More reading than coding I guess. Thanks for staying till the end :)&lt;/p&gt;

</description>
      <category>docker</category>
      <category>express</category>
      <category>react</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Cookies with React, Express and Docker</title>
      <dc:creator>Mac</dc:creator>
      <pubDate>Tue, 02 Jul 2019 16:16:34 +0000</pubDate>
      <link>https://forem.com/macru/cookies-with-react-express-and-docker-4h97</link>
      <guid>https://forem.com/macru/cookies-with-react-express-and-docker-4h97</guid>
      <description>&lt;p&gt;Recently I had to move JWT to cookies to used it between Docker microservises seamlessly. We run each container on separate url prefix with was tricker to figure out as cookie have to be there when url is changed. I couldn’t find any straightforward solution so I decided to write it as it might be useful for someone or even for future me.&lt;/p&gt;

&lt;p&gt;I just assume you have working front-end and back-end containers and everything run fine. I won’t be explaining what cookies are as there’re plenty of better articles on that topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic setup
&lt;/h3&gt;

&lt;p&gt;Let’s use simple Express server as example how to send cookies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.js

const express = require("express")
const session = require("express-session");

const app = express()

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: false,
      secure: false,
    },
  }),
);

app.get("/cookie", (req, res) =&amp;gt; {
  const options = {
    secure: false,
    httpOnly: false,
    domain: ".your.domain.com"
  }

  return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})

app.listen(8080)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case whenever we send request to &lt;em&gt;localhost:8080/cookie&lt;/em&gt; server responds with Set-Cookie header. That works fine when you type it directly in your browser or in app like Postman. Problem starts when you run you client on &lt;em&gt;client.your.domain.com&lt;/em&gt; and server on &lt;em&gt;server.your.domain.com.&lt;/em&gt; We start getting CORS issues.&lt;/p&gt;

&lt;p&gt;Let’s see basic setup for out client app. I used create-react-app and just modified it by adding &lt;a href="https://www.npmjs.com/package/superagent"&gt;superagent&lt;/a&gt; (great library for requests) and sending request whenever I click on the link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// App.js

import React from "react";
import superagent from "superagent";
import logo from "./logo.svg";
import "./App.css";

function App() {

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;header className="App-header"&amp;gt;
        &amp;lt;img src={logo} className="App-logo" alt="logo" /&amp;gt;
        &amp;lt;p&amp;gt;
          Edit &amp;lt;code&amp;gt;src/App.js&amp;lt;/code&amp;gt; and save to reload.
        &amp;lt;/p&amp;gt;
        &amp;lt;a
          className="App-link"
          onClick={() =&amp;gt;
            superagent
              .get("http://localhost:8080/cookie")
              .then(response =&amp;gt; {
                console.log(response);
              })
          }
        &amp;gt;
          Get Cookie
        &amp;lt;/a&amp;gt;
      &amp;lt;/header&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  CORS
&lt;/h3&gt;

&lt;p&gt;Since we sending request from different originator we get CORS issues. Simple solution to do that is to install &lt;a href="https://www.npmjs.com/package/cors"&gt;cors&lt;/a&gt; package and add it as by simple example in their docs.&lt;/p&gt;

&lt;p&gt;Again, simple cors with wildcard &lt;code&gt;(*/*)&lt;/code&gt; won’t work. We have to set up some custom config for cors and catch pre-flight OPTION check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.js

const express = require("express")
const session = require("express-session");
const cors = require("cors")
const app = express()

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: false,
      secure: false,
    },
  }),
);

const corsOptions = {
  origin: /\.your.domain\.com$/,    // reqexp will match all prefixes
  methods: "GET,HEAD,POST,PATCH,DELETE,OPTIONS",
  credentials: true,                // required to pass
  allowedHeaders: "Content-Type, Authorization, X-Requested-With",
}

// intercept pre-flight check for all routes
app.options('*', cors(corsOptions))

// add cors middleware to route 
app.get("/cookie", cors(corsOptions), (req, res) =&amp;gt; {
  const options = {
    secure: false,
    httpOnly: false,
    domain: ".your.domain.com"
  }

return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})

app.listen(8080)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is one more change on the front-end. Since our server now accepts request with credentials we have to send one to pass the cookie. It’s literally one line extra&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// App.js

import React from "react";
import superagent from "superagent";
import logo from "./logo.svg";
import "./App.css";

function App() {

return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;header className="App-header"&amp;gt;
        &amp;lt;img src={logo} className="App-logo" alt="logo" /&amp;gt;
        &amp;lt;p&amp;gt;
          Edit &amp;lt;code&amp;gt;src/App.js&amp;lt;/code&amp;gt; and save to reload.
        &amp;lt;/p&amp;gt;
        &amp;lt;a
          className="App-link"
          onClick={() =&amp;gt;
            superagent
              .get("http://localhost:8080/cookie")
              .withCredentials()           // it's simple as that
              .then(response =&amp;gt; {
                console.log(response);
              })
          }
        &amp;gt;
          Get Cookie
        &amp;lt;/a&amp;gt;
      &amp;lt;/header&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Secured cookies
&lt;/h3&gt;

&lt;p&gt;As you might noticed i used unsecured cookies in above examples. Thet’s for dev/local purposes only. If you want to use it in production you have to take care about security. Secure cookies will work only on https so you have to take care of that as well. Good idea is to set cookies security dependent on NODE_ENV, so we don’t have to remember about it when working on dev and then deploy to prod.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.js

===

app.use(
  session({
    secret: process.env.LOGIN_SERVER_SECRET,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: true,            // change both to true
      secure: true,
    },
  }),
);

===

// dynamic change
const isCookieSecure = 
  process.env.NODE_ENV === "production" ? true : false;

// add cors middleware to route 
app.get("/cookie", cors(corsOptions), (req, res) =&amp;gt; {
  const options = {
    secure: isCookieSecure,
    httpOnly: isCookieSecure,
    domain: ".your.domain.com"
  }


return res
    .cookie("cookieName", "cookieValue", options)
    .status(200)
    .send("cookie sent")
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s basically it. You can add as many apps and servers to your docker as you want and enjoy cookies everywhere. They’ll be passed automatically in the request and responses headers. Thanks you all for getting that far, hopefully that’s gonna be useful for someone :)&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;CORS&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS"&gt;OPTIONS&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies"&gt;cookies&lt;/a&gt; at MDN. Any questions or feedback just post a comment. Thanks 🙏&lt;/p&gt;

</description>
      <category>docker</category>
      <category>express</category>
      <category>react</category>
      <category>cookies</category>
    </item>
  </channel>
</rss>
