DEV Community

Cover image for Zero to Hero šŸš€ - Como criar uma AplicaĆ§Ć£o Fullstack
Renan Santos
Renan Santos

Posted on

Zero to Hero šŸš€ - Como criar uma AplicaĆ§Ć£o Fullstack

Como criar sua primeira aplicaĆ§Ć£o fullstack com Docker.

Organizar uma aplicaĆ§Ć£o completa do zero, pode ser uma tarefa bastante complicada no comeƧo, devido a alta quantidade de maneiras de resolver o mesmo problema. Muitos programadores ficam perdidos por onde comeƧar, principalmente os iniciantes, focam em tecnologia, quando oque Ć© uma das coisas mais legais, Ć© colocar a mĆ£o na massa.

Show me the code

Porque utilizar Docker?

Com docker Ć© possivel abstrair todo o ambiente e acoplar somente em um local, sendo facilmente utilizado
para coloca-lo em produĆ§Ć£o, ou colocar para funcionar em um projeto do amigo dev. AlĆ©m disso nĆ£o precisamos
instalar diversos pacotes dentro da nossa maquina, jĆ” que o prĆ³prio emula esse ambiente pra gente. Alguns benefĆ­cios:

  • fĆ”cil instalaĆ§Ć£o
  • fĆ”cil deploy
  • versionamento de build com docker hub
  • pode ser integrado com pipelines
  • evita instalar N VersƵes da aplicaĆ§Ć£o na prĆ³pria mĆ”quina
  • fĆ”cil reciprocidade da comunidade
  • receita de bolo

*Como organizar a aplicaĆ§Ć£o: *

Primeiro de tudo iremos escrever nossa receita docker onde a mesma irĆ” se encarregar de praticamente tudo, optei por escolher o banco de dados Postgres, devido ser bastante fĆ”cil de mexer, o cĆ³digo Ć© aberto, e tem muito conteudo na web. O projeto estĆ” divido em 4 camadas. Sendo elas, client onde serĆ” usado um framework React (pode ser qualquer um, como vue, angular etc..), server com node + express, redis, e banco de dados. *Nginx para chavear as portas da aplicaĆ§Ć£o e servir o frontend e o backend em portas dinamicas. *

Na raiz do projeto.

version: '3'
services:
  postgres:
    image: "postgres:latest"
    environment:
      - POSTGRES_PASSWORD=postgres_password
  redis:
    image: "redis:latest"
  nginx:
    restart: always
    build:
      dockerfile: Dockerfile.dev
      context: ./nginx
    ports:
      - "3050:80"
    depends_on:
      - api
      - client       
  api:
    build:
      dockerfile: Dockerfile.dev
      context : ./server
    volumes:
      - /app/node_modules 
      - ./server:/app  
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - PGUSER=postgres
      - PGHOST=postgres
      - PGDATABASE=postgres
      - PGPASSWORD=postgres_password
      - PGPORT=5432  
  client:
    build:
      dockerfile: Dockerfile.dev
      context: ./client
    volumes:
      - /app/node_modules
      - ./client:/app 
    environment:
      - WDS_SOCKET=0   
Enter fullscreen mode Exit fullscreen mode

Note que na receita Dockerfile acima estamos fazendo a referencia para seus devidos arquivos Dockerfile.dev
de cada serviƧo. Estou optando pelo arquivo .dev, pois assim apos o desenvolvedor buildar, podemos fazer versionamento e subir o build com uma esteira de deploy para produĆ§Ć£o.

ConfiguraĆ§Ć£o do servidor:

šŸ“ ./server/Dockerfile.dev

FROM node:14.14.0-alpine
WORKDIR '/app'
COPY package.json .
RUN npm install 
COPY . .
CMD ["npm", "run", "dev"]
Enter fullscreen mode Exit fullscreen mode

šŸ“ ./server/index.js

const keys = require('./keys')

const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const { Pool } = require('pg')
const redis = require('redis')

const app = express()
app.use(cors())
app.use(bodyParser.json())

const pgClient = new Pool({
  user: keys.pgUser,
  port: keys.pgPort,
  host: keys.pgHost,
  database: keys.pgDataBase,
  password: keys.pgPassword,
})

pgClient.on("connect", (client) => {
  client
    .query("CREATE TABLE IF NOT EXISTS values (number INT)")
    .catch((err) => console.error(err));
});

const redisClient = redis.createClient({
  host: keys.redisHost,
  port: keys.redisPort,
  retry_strategy: () => 1000
})

const redisPublisher = redisClient.duplicate()

// Express route handlers
app.get('/', (req, res) => {
  res.send('Hi')
})

app.get('/values/all', async (req, res) => {
  const values = await pgClient.query('SELECT * FROM values')

  res.send(values.rows)
})

app.get('/values/current', async (req, res) => {
  redisClient.hgetall('values', (err, values) => {
    res.send(values)
  })
})

app.post('/values', async (req, res) => {
  const index = req.body.index

  if (parseInt(index) > 40) {
    return res.status(422).send({ message: 'Index to high' })
  }

  redisClient.hset('values', index, 'Nothing yet!')
  redisPublisher.publish('insert', index)

  pgClient.query('INSERT INTO values(number) VALUES($1)', [index])

  res.send({ working: true })
})


app.listen(5000, () => {
  console.info('app on listening on port 5000!')
})
Enter fullscreen mode Exit fullscreen mode

O script acima Ʃ responsƔvel por iniciar o backend, como trata-se de somente um tutorial
optei por deixar todo o cĆ³digo em um arquivo para fĆ”cil entendimento, fique a vontade para quebra-lo camadas e arquivos separados, como service, e controllers (recomendado).

šŸ“ ./server/keys.js

module.exports = {
    redisHost: process.env.REDIS_HOST,
    redisPort: process.env.REDIS_PORT,
    pgUser: process.env.PGUSER,
    pgHost: process.env.PGHOST,
    pgDataBase: process.env.PGDATABASE,
    pgPassword: process.env.PGPASSWORD,
    pgPort: process.env.PGPORT
}
Enter fullscreen mode Exit fullscreen mode

Apos a instalaĆ§Ć£o de todos os pacotes, ainda nĆ£o sera possivel visualizar a aplicaĆ§Ć£o no browser, pois teriamos que fazer uma configuraĆ§Ć£o do nosso proxy nginx, onde o mesmo fica responsĆ”vel por expor as portas da aplicaĆ§Ć£o
para o mundo externo.

šŸ“ ./nginx/default.conf

upstream client {
  server client:3000;
}

upstream api {
  server api:5000;
}

server {
  listen 80;

  location / {
    proxy_pass http://client;
  }

  location /api {
    rewrite /api/(.*) /$1 break;
    proxy_pass http://api;
  }

  location /ws {
    proxy_pass http://client;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }
}

Enter fullscreen mode Exit fullscreen mode

A configuraĆ§Ć£o acima, acredito e julgo que seja muito ideal pois o frontend nĆ£o irĆ” precisar bater um endpoint separada pois tudo que comeƧa com [api] ja temos a referencia que Ć© a nosso serviƧo, logo oque vier depois disso trata-se somente do backend. Assim facilitando caso optar por deploy da aplicaĆ§Ć£o em ambiente de produĆ§Ć£o, poupando tempo em troca de ip ou URI.

Disclaimer, como trata-se de um tutorial deixarei a parte do frontend e escolha do programador, onde somente mostrarei apenas o arquivo de proxy que ficarĆ” dentro da pasta do client. Pode ser usado qualquer tipo de framework frontend, atĆ© mesmo js puro com webpack. Desde que seja adicionado o arquivo de configuraĆ§Ć£o do nginx, tambĆ©m fazendo as devidas correƧƵes para ouvir o arquivo de html do projeto.

šŸ“ ./client/nginx/default.conf

server {
    listen 3000;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}
Enter fullscreen mode Exit fullscreen mode

Como rodar o projeto šŸš€

O comando docker abaixo serƔ responsƔvel por subir todo o nosso projeto, sendo possivel acessƔ-lo
na porta 3050, para o frontend, e 5000 (/api/)para o backend.

docker compose -f docker-compose-dev.yml up
Enter fullscreen mode Exit fullscreen mode

Link do repositĆ³rio Gitlab

šŸ‘Øā€šŸ’» Volte sempre...

Top comments (1)

Collapse
 
jairsantana7 profile image
Jair Santana

ConteĆŗdo true!!!