Skip to content

Marcelo-Diament/next-level-week-02

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

84 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Next Level Week #02 | Proffy

Prática da Next Level Week #02 | Rocketseat

Banner topo

Introdução


Projeto Proffy

Esse repositório contempla a prática proposta na segunda edição da <nwl/> (NextLevelWeek), cujo projeto se chama Proffy.

Proffy

O nome Proffy é uma homenagem ao dia 06 de Agosto, Dia dos Professores.

O projeto chama-se Proffy e consiste numa plataforma de e-learning, onde se conecta alunos e professores numa plataforma de estudos online com opções de criar perfil, cadastro de matérias, horários e valores de aula, buscar por registro de professores (com filtros por horário e matéria) e agendar aulas (não contempla vídeo chamadas). Os dados serão salvos (storage) do lado do cliente. Utilizamos o conceito bubble first (mobile first), privilegiando os dispositivos mobile.

Configuração do Ambiente

Antes de iniciarmos precisamos garantir que temos todas as ferramentas necessárias disponíveis em nosso ambiente local. A Rocketseat preparou um documento com o passo a passo para realizarmos essa configuração - seja para instalar as dependências, seja para atualizá-las, além de um documento com alguns dos erros mais comuns que podem ocorrer durante essa configuração.

Antes de prosseguir, execute os seguintes comandos no terminal, para garantir que estamos com a mesma versão do node e do npm:

node -v
// deve retornar v12.8.3

npm -v
// deve retornar 6.14.6

Layout

Garanta que possui acesso aos arquivos de layout do projeto no Figma para ter uma melhor visão do que será construído.

Funcionalidades

Conexões
  • Rota de listagem de conexões realizadas (operação de soma dos registros)

  • Rota de criação de nova conexão (ativada quando clicarem em Entrar em contato)

Aulas
  • Rota de listagem de aulas [filtros por matéria, dia da semana e hora]

  • Rota de Criação de nova aula

Tecnologias

Confira as principais tecnologias utilizadas no projeto:

React

React é uma biblioteca de construção de interfaces SPA que via otimizar a experiência do usuário. Também existem 'sub pacotes' do React, de acordo com o ambiente da aplicação - veja alguns dos principais:

ambiente 'sub pacote'
Web ReactJS
Mobile App React Native
Realidade Virtual ReactVR
Televisão ReactTV

Independentemente do 'sub pacote', sempre utilizaremos o React. O React também utiliza bibliotecas adicionais para integrar-se às interfaces de acordo com o ambiente. Por exemplo, na web utilizamos o ReactJS com o ReactDOM. Já para apps mobile utilizamos o [ReactNative][ReactNative] com uma biblioteca adicional também chamada de [ReactNative][ReactNative].

Node.js

É uma plataforma que nos permite utilizar o mesmo JavaScript (puro ou com ReactJS, ...) no back-end.

  • Non-blocking IO: conseguimos controlar a assincronicidade

  • Streams (nos permite consumir dados no estilo 'LazyLoad') e WorkerThreads (nos permite trabalhar o core)

TypeScript

O TypeScript é uma extensão do JavaScript que utiliza a tipagem que facilita a manutenção do código e garante a consistência de nossas soluções, pois captura um erro antes mesmo de sua execução. Também possui recursos para IDEs que otimizam o desenvolvimento.

Knex

O Knex é um query builder, que possibilita escrevermos as queries de forma mais simples, em JavaScript.

SQLite

O sqlite3 é um pacote de banco de dados mais leve, não requer instalações na máquina. Usaremos a sintaxe do knex para trabalharmos com o sqlite3.


Passo a Passo

Aula 01 | ReactJS e Estrutura Web

01.01 Criando projeto

Criaremos o projeto dentro da pasta do projeto/repositório. Caso utilize yarn execute o comando yarn create react-app web --template typescript . Caso contrário, utilize o [npx][npx] com o comando npx create react-app web --template typescript .

Após a criação do projeto (que chamamos de web), acesse a pasta com o comando cd web e então abra o [VSCode][VSCode] (ou sua IDE de preferência) com o comando code . . E, para visualizarmos o projeto rodando, rode yarn start (ou npm start ).

01.02 Limpando o projeto

Podemos remover os seguintes arquivos:

./web/README.md ./web/src/App.css ./web/src/App.text.tsx ./web/src/index.css ./web/src/logo.svg ./web/src/serviceWorker.ts ./web/src/setupTests.ts

Para que os erros que apareceram sejam corrigidos, precisamos:

  • Remover a chamada dos arquivos serviceWorker.ts e index.css no arquivo index.tsx

  • Remover o método unregister() do serviceWorker.ts do final do arquivo index.tsx (e os respectivos comentários)

  • Remover a chamada dos arquivos logo.svg e App.css no arquivo App.tsx

  • Substituir a tag <header></header> (e todas as demais tags contidas nela) por uma tag <h1></h1> com o texto de preferência dentro dela

  • Manter somente o arquivo index.html na pasta ./public e remover todos os demais arquivos

  • No arquivo index.html , deixar apenas as tags meta charset, meta viewport, meta theme-color e title (atualizá-lo para Proffy, o nome da aplicação). Limpar também os comentários.

01.03 Assets

Vamos incluir a pasta disponível como material extra da aula #01 (pasta images ) dentro de src/assets . Estamos importando para o projeto algumas imagens e ícones para utilizarmos ao longo do projeto. Estamos usando .svg para que as imagens fiquem mais leves.

01.04 Global Style

Crie o arquivo src/assets/styles/global.css e aplique o inicial - incluindo as variáveis disponibilizadas nos arquivos extras da aula.

Crie o link do App.tsx com o estilo global com import './assets/styles/global.css'; .

Importe as fontes Archivo (Pesos 400 - Regular -, e 700 - Bold) e Poppins (pesso 400 - Regular) do GoogleFonts com o seguinte trecho de código no index.html :

<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;700&family=Poppins&display=swap" rel="stylesheet">
01.05 Landing Page

index.tsx

Crie o arquivo src/pages/Landing/index.tsx e inclua o trecho import React from 'react'; para importar o React.

No arquivo App.tsx , altere o conteúdo da div#root para <Landing></Landing> (trazendo assim o componente Landing).

E então criaremos a função Landing() , seguida do trecho que à torna disponível para os outros arquivos: export default Landing; . E detalharemos a função Landing() , incluindo logo, título, imagem, dois botões com ícones e um span - sempre usando o atributo className para podermos estilizar esses trechos.

styles.css

Crie o arquivo src/pages/Landing/styles.css para que possamos aplicar estilo especificamente no componente Landing, aplique o estilo e então inclua o caminho relativo para o estilo no index.tsx : import './styles.css'; .

01.06 Listagem

Vamos repetir o processo de criarmos novos componentes - TeachersList e TeachersForm.

Rotas

O primeiro passo é instalarmos o reactRouterDOM - basta executar yarn add react-router-dom - ele nos ajudará a criarmos e manipularmos as rotas da aplicação.

Se instalado com sucesso, iremos criar o arquivo routes.tsx na pasta ./src - ele é um componente que conterá todas as nossas rotas. Também precisaremos rodar yarn add @types/react-router-dom -D para que os componentes do react-router-dom possam ser importados.

Devemos ainda importar o componente Routes no .App.tsx e chamar o componente dentro da div.App .

E no arquivo ./src/Landing/index.tsx importamos Link e alteramos as tags <a></a> por <Link></Link> (trocando os atributos href por to ), para evitar loadings desnecessários.

Quebrando em Componentes

Criamos o conteúdo inicial do componente pages/TeacherList . Mas, como podemos reaproveitar vários 'pedaços' dessa página em outras, tornaremos esses elementos repetitivos diferentes componentes, como, por exemplo, o components/PageHeader .

Basicamente salvaremos esses componentes reutilizáveis na página /components e trocaremos o trecho de HTML pela tag do respectivo componente (definindo os devidos atributos). Lembre-se de verificar as importações (mover ou copiar os imports necessários para cada componente e importar o componente novo nos arquivos de onde foi removido e onde mais venha a ser utilizado).

Propriedades

Agora precisamos alterar o título de cada instância do componente PageHeader . Para isso faremos os seguintes passos:

  • Transformar a function PageHeader() em uma const (usando arrow function). Também definiremos que o PageHeader é um componente do tipo Function Component (FC), que receberá a interface PageHeaderProps . E, como parâmetro único, receberemos props (que contém todas as propriedades declaradas na interface). Ficará assim:
const PageHeader: React.FC<PageHeaderProps> = props => {
  // ...
}
  • Definir uma interface (chamada PageHeaderProps ) que conterá as props do componente - nessa interface definiremos sua tipagem:
interface PageHeaderProps {
  title: string;
}
  • Trocar o conteúdo do título pela prop (usando interpolação):
<strong>{props.title}</strong>
  • No componente que importa o PageHeader , trocaremos o conteúdo do título pela prop title:
<PageHeader title="Que incrível que você quer dar aulas!"/>

Aula 02 | Back End com Node.js

02.01 Ambiente de Desenvolvimento

Conferir no início do documento - Configuração de Ambiente (Instalação ou Atualização).

02.02 Criando Projeto e Configurando o Ambiente

Criar a pasta server , acessá-la pelo terminal e executar:

yarn init -y

Ou, se não estiver usando yarn , rode npm init -y .

Será criado o arquivo package.json - que contém as principais informações sobre esse projeto e suas dependências.

Vamos criar o arquivo ./server/src/server.ts , que será o principal arquivo da aplicação (será o primeiro a ser carregado, tudo partirá dele).

Usaremos TypeScript no projeto, por isso precisamos instá-lo com o comando yarn add typescript -D ou npm install typescript -D .

E precisamos criar o arquivo tsconfig.json , executando yarn tsc --init ou npx tsc --init . Esse arquivo determina a forma que o TypeScript gerará o arquivo JavaScript.

Por fim, instalaremos outra dependência ( ts-node-dev ), com os comandos yarn add ts-node-dev -D ou npm install ts-node-dev -D . Essa dependência será usada apenas em ambiente de desenvolvimento, ficará 'rodando' e observando o script constantemente, refletindo atualizações e alterações do nosso código.

02.03 Iniciando o Projeto

Precisamos incluir um script de start em nosso package.json , para podermos rodar yarn start ou npm start e iniciarmos nosso projeto.

Acrescentaremos uma sessão scripts e, nela, o comando start. O trecho --transpile-only evita que erros sejam verificados, facilitando o desenvolvimento. Já o trecho --ignore-watch node_modules evita que aplicações de terceiros sejam convertidas. E --respawn evita que fique iniciando e finalizando. Ele só encerra após o 'Control C' no terminal.

Deve ficar assim:

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "ts-node-dev --transpile-only --ignore-watch node_modules src/server.ts"
  },
  "devDependencies": {
    "ts-node-dev": "^1.0.0-pre.56",
    "typescript": "^3.9.7"
  }
}

express

Instalamos o express (um "micro framework" com algumas funcionalidades prontas):

yarn add express

ou

npm install express

No server.ts importamos o express e definimos as configurações básicas do express (const app, listen e o método get()). No método get() simulamos o acesso a '/users' e retornamos um objeto como exemplo.

02.04 Criando o Banco de Dados (SQLite)

Utilizaremos o SQLite por conta de sua simplicidade, mas poderíamos utilizar outros sem problemas. Para instalarmos o pacote no projeto, precisamos executar: yarn add knex sqlite3 ou npm install knex sqlite3 . O knex é um query builder, que possibilita escrevermos as queries de forma mais simples, em JavaScript.

Depois da instalação, vamos criar a pasta src/database , onde ficarão os arquivos referentes ao BD (Banco de Dados). O primeiro arquivo a ser criado será o connection.ts . Nele importaremos o knex e o path (para facilitar o uso dos recursos das rotas). Então definiremos a const db, que - usando knex - definirá o tipo de banco (sqlite3), a connexão e, no caso do sqlite3, definiremos a propriedade useNullAsDefault como true .

Migration

O Knex (um query builder) é escrito em .js e não .ts , por isso precisaremos criar o arquivo ./server/knexfile.ts . Nele faremos o seguinte:

import path from 'path';

module.exports = {
  client: 'sqlite3',
  connection: {
    filename: path.resolve(__dirname, 'src', 'database', 'database.sqlite')
  },
  migrations: {
    directory: path.resolve(__dirname, 'src', 'database', 'migrations')
  },
  useNullAsDefault: true,
};

A migration serve para facilitar o trabalho em equipe ao garantir uma mesma estrutura de Banco de Dados, replicando todo o 'passo a passo'/histórico das schemas com simples comandos.

Para criar as migrations, vamos salvar os arquivos dentro de ./server/src/database/migrations . As migrations devem estar ordenadas de modo que a ordem de criação/manipulação dos dados e tabelas seja respeitado. Por isso ordenaremos com um prefixo com 2 dígitos e o nome descritivo da migration, como 00_create_users.ts .

Como o knex não lê JavaScript, customizaremos os comandos de terminal para rodar as últimas migrations e para realizar o rollback das mesmas (no arquivo ./server/package.json ):

"knex:migrate": "knex knexfile --knexfile.ts migrate:latest",
"knex:migrate:rollback": "knex knexfile --knexfile.ts migrate:rollback"

Para rodar os comandos basta acrescentar yarn ou npm antes ( yarn knex:migrate ).

Como vamos trabalhar com o sqlite3, é recomenrável usar a extensão SQLite do VSCode. Selecione Open database para visualizar a tabela users criada e as 2 tabelas da migration criada.

Agora vamos criar as demais migrations (class_schedule, classes e connections).

02.05 Routes

Vamos criar o arquivo ./server/src/routes.ts . E vamos mover o trecho em que definimos a rota app.get() no arquivo server.ts .

Para cada rota, teremos um método (GET, POST, DELETE) e um retorno (a listagem dos registros, os detalhes de um registro, a criação ou atualização de um novo registro ou a exclusão de um registro). Portanto as rotas são responsáveis por executar as queries no banco de dados de acordo com as requests e responses.

Podemos utilizar o Insomnia para visualizarmos melhor o consumo e manipulação dos dados do banco sqilte3. Faremos essas operações de forma a usar o await/async nas promises, a desestruturação de objetos e outros conceitos já vistos até esse ponto. Exemplos:

GET | Lista Classes | sintaxe knex
routes.get('/classes',async (request, response) => {
  const allClasses = await db('classes');
  return response.json(allClasses);
});
GET | Detalhe Classe | sintaxe knex
routes.get('/classes/:id',async (request, response) => {
  const classesId = request.params.id;
  const classesItem = await (await db('classes').where('id', classesId));
  return response.json(classesItem);
});
POST | Novo Usuário | sintaxe knex
routes.post('/users', async (request, response) => {
  const {
    name,
    avatar,
    whatsapp,
    bio
  } = request.body;

  const insertedUser = await db('users').insert({
    name,
    avatar,
    whatsapp,
    bio
  });

  return response.send();
});
DELETE | Exclusão Classe | sintaxe knex

Schedules

Como salvamos os horários como string, precisaremos tratar esse dado. Para isso faremos o seguinte (no arquivo routes.ts):

{ ... },
const routes = express.Router();

interface ScheduledItem {
  week_day: number,
  from: string,
  to: string
};

{ ... }

const classSchedule = schedule.map((scheduledItem: ScheduledItem) => {
    return {
      class_id,
      week_day: scheduledItem.week_day,
      from: convertHourToMinutes(scheduledItem.from),
      to: convertHourToMinutes(scheduledItem.to)
    };
  });
}

Observação: a função convertHourToMinutes() foi declarada em src/utils/convertHourToMinutes/convertHourToMinutes.ts .

Por fim, vamos atualizar a operação do banco de dados dentro da const classSchedule :

await db('class_schedule').insert(classSchedule);
02.06 Refatoração

Transactions

Vamos criar uma transaction ( trx ) para podermos realizar todas operações em paralelo, evitando executar um registro antes de outra query gerar um erro. Para isso basta definirmos const trx = await db.transaction(); e substituir await db... por await.trx... . Antes de retornar nossa response, vamos commitar todas as queries guardadas em nossa transaction: await trx.commit(); .

Outro recurso bacana do uso das transactions é podermos realizar o rollback do que já estava pronto para ser commitado: await trx.rollback(); .

Tratativa de Erros

Vamos incluir um try/catch, para - caso ocorra algum erro - podermos capturar os erros e tratá-los e/ou comunicá-los.

02.07 - Listagem das Aulas

Controllers

Vamos reproduzir uma arquitetura MVC, por isso vamos isolar controllers de models e views. Então vamos criar a pasta src/controllers/ e seus arquivos.

Basicamente apontaremos uma rota (_routes.ts) para um controller, que por sua vez, determinará que ação será executada, em que tabela, etc.. Faremos isso com a ajuda do knex.

Criaremos também o src/controllers/ConnectionsController.ts.

02.08 - CORS

Permite o controle de requisições de outras origens (Corss Origin). Para isso baixaremos o pacote cors para o ambiente de desenvolvimento - yarn add cors e yarn add @types/cors -D e importaremos no arquivo server.ts.


Informações Complementares

Conceitos

ReactJS | Criando Componentes

Componentes são basicamente funções que retornam trechos de HTML. No React criamos componentes para reaproveitarmos código, de forma a tornar o uso de elementos que se repetem ao longo dos documentos mais fácil (ao invés de reescrevermos o elemento a cada repetição, usamos o componente).

Sempre que criamos componentes precisamos:

  • Iniciar o nome do componente com letra maiúscula, para não ser interpretado como uma tag HTML

  • Sempre que formos incluir o HTML dento do código JavaScript (.jsx, .tsx) precisamos importar o React, com o seguinte trecho:

import React from 'react';
  • Usar a extensão .tsx, pois estamos usando TypeScript

  • Criamos uma pasta com o nome do componente (de acordo com a organização do projeto e tipo de componente - page, component, etc.) e criamos o arquivo index.tsx e seu estilo - styles.css . Depois simplesmente importamos e utilizamos a respectiva tag do componente importado.

ReactJS | Props

Propriedades são uma forma de transmitirmos informações entre os componentes. Funcionam como as propriedades de um objeto JavaScript. Podemos defini-las numa interface (já definindo sua tipagem) e definirmos o componente como FC (FunctionComponent), de modo que receba as props definidas na interface e permita usarmos as propriedades desejadas dentro do HTML do componente.

Outra maneira de usarmos uma prop (sem termos de definir na interface) é através da propriedade children ( props.children ). Essa propriedade nos permite definir o conteúdo dentro do contexto onde o componente está sendo utilizado (como o form de TeacherList dentro do PageHeader ).

ReactJS | States

State são os estados dos componentes. Sempre que precisamos alterar uma propriedade através da interação com o usuário, utilizamos as interfaces - que é montada a partir das informações que recebe (via JavaScript).

ReactJS | Rotas

Para trabalharmos com as rotas (endereços da aplicação), instalamos o react-router-dom ( yarn add react-router-dom ) e importamos o BrowserRouter e o Route no arquivo routes.tsx (que também é um componente):

import React from 'react';
import { BrowserRouter, Route} from 'react-router-dom';
import ComponenteDesejado from './local/ComponenteDesejado';

function Routes() {
  return (
    <BrowserRouter>
      <Route
        path="/" // Caminho da rota
        exact={true} // Se a correspondência deve ser exata
        component={HomeBanner} // Componente a ser exibido
      />
      <Route path="/caminho" component={ComponenteDesejado} />
    </BrowserRouter>
  );
}

export default Routes;

E no arquivo ./src/ComponenteDesejado/index.tsx importamos também o Link - import { Link } from 'react-router-dom - e alteramos as tags <a></a> por <Link></Link> (trocando os atributos href por to ), para evitar loadings desnecessários. O Link nos permite aplicar o conceito de SPA (Single Page Application), pois depois de carregado o conteúdo da página, não será carregado novamente.

Também precisaremos rodar yarn add @types/react-router-dom -D para que os componentes do react-router-dom possam ser importados.

Node.js

É uma plataforma que nos permite utilizar o mesmo JavaScript (puro ou com ReactJS, ...) no back-end.

  • Non-blocking IO: conseguimos controlar a assincronicidade

  • Streams (nos permite consumir dados no estilo 'LazyLoad') e WorkerThreads (nos permite trabalhar o core)

Node.js | Express, Rotas e Recursos

express

Iremos instalar algumas dependências como o express (um "micro framework" que traz algumas funcionalidades prontas). Para instalarmos, usamos:

yarn add express

ou

npm install express

Após a instalação devemos importá-lo no nosso server.ts ( import express from 'express'; ). Pode ser necessário executar yarn add @types/express -D também (caso o pacote não venha com TypeScript definido, aí baixamos esses pacotes com a tipagem).

Rotas

No arquivo server.ts , declaramos const app = express(); , definimos os métodos do app e depois definimos a porta que o listen() usará (como 3333), com app.listen(3333); .

O arquivo deverá ficar assim, por enquanto. O método get() retorna a const users nesse exemplo:

import express from 'express';

const app = express();

app.get('/users', (request, response) => {
  const users = [
    { name: 'Fulano', age: 25 },
    { name: 'Ciclano', age: 52 },
  ];
  return response.json(users);
});

app.listen(3333);

Recursos

São os trechos enviados após o endereço 'base' (como /users no exemplo anterior).

Métodos

São os métodos usados nas requisições HTTP, como get, post, put e delete, usados para listar, incluir, atualizar ou deletar dados. Usaremos o Insomnia para visualizarmos, consultarmos e manipularmos esses dados. Se preferir, pode utilizar o Postman também.

Na ferramenta, basta criarmos uma request, selecionarmos o endereço (endpoint), o método (GET, POST, PUT, DELETE...) e, se necessário, passarmos os parâmetros. No caso, temos como endereço http://localhost:3333/users e como método, post.

Para o express ler JSON, precisamos rodar app.use(express.json()); após criarmos a const app.

Parâmetros

Há 3 tipos de parâmetros que podemos utilizar. São:

  • Request Body: normalmente são os dados para criação ou atualização de um registro. Acessamos através de request.body .

  • Route Param: recursos da nossa rota (como /users/12 , onde 12 seria o ID do usuário). Na rota é representado por /users:id . Acessamos através de request.params .

  • Query Param: parâmetros mais utilizados para paginação, filtros e ordenação. São exibidos na URL a partir do ? após o recurso e concatenados com & . Cada query param possui uma chave e um valor (exemplo: /users?offset=0&orderby=ASC&limit=25 ). Acessamos através de request.query .

Knex

O Knex é um query builder, que possibilita escrevermos as queries de forma mais simples, em JavaScript.

Links Úteis e Interessantes

Ambiente

Configuração do ambiente Instalação das dependências Atualização das dependências Erros mais comuns

Tecnologias e Ferramentas

Figma | Insomnia | node | npm | notion | Postman | React | ReactDOM | ReactJS | React Native

Rocketseat

Rocketseat

YouTube | Twitter | Facebook | Instagram


Obrigado pela visita!

Vamos nos conectar? Se quiser trocar idéias, experiências e figurinhas, entre em contato comigo!

Marcelo Diament | Desenvolvedor Front End Pleno e Instrutor Rocketseat | Github | LinkedIn

About

Prática proposta na NextLevelWeek #02

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published