Desenvolver um site educacional de vídeos que utilize o paradigma de SPA (Single Page Application). O site deve possuir uma página de login, uma página de vídeos e uma página de gerenciamento, que permita adicionar ou remover um novo vídeo ao site.
O projeto é dividido em duas pastas: Front-End e Back-End.
Tem como função servir de plataforma pública que seja capaz de:
- Renderizar o HTML e a estilização do mesmo, criando uma interface com a qual o usuário pode interagir e enviar informações que descrevam quais partes da aplicação este quer acessar ou modificar.
- Enviar requisições HTTP que especifiquem qual método (POST, GET, DELETE) será solicitado e encapsular de maneira estruturada a informação relacionada a esta operação ao BackEnd.
- Escutar as respostas geradas pelo Backend quando este responde a uma requisição HTTP.
- Aplicar uma lógica interna que, a partir das respostas do backend, decida de qual maneira a interface do site será manipulada.
- Re-Renderizar a aplicação de maneira a refletir para o usuário a mudança do estado da aplicação feita durante as aplicações de lógica interna, bem como continuar apta a novas interações como usuário.
Tem como função servir de plataforma lógica comunicação entre o Front-End o banco de dados, que seja capaz de:
- Receber requisições HTTP enviadas pelo Front-End
- Aplicar uma lógica interna que desestruture a informação recebida pelo protocolo HTTP
- Verificar permissão. Normalmente, um token é gerado na criação de usuário e passa a ser enviado no cabeçalho das requisições. Algumas operações podem ser feitas por visitantes, já outras exigem um token de usuário, e outras token de administrador (roles)
- Enviar ao banco a requisição, agora tratada pela lógica interna
- Escutar a resposta do banco sobre a requisição feita.
- Aplicar uma lógica interna que trate o conteúdo recebido pela requisição ao banco de maneira a gerar a resposta esperada pelo FrontEnd
- Responder a requisição http ao frontend
Será utilizado o Cloud Firestorm do Google Firebase. Não será necessário o conhecimento de programação de bancos de dados, visto que este processo é automatizado pela ferramenta. Em suma, o banco recebe e responde requisições ao Back-End.
Assim como o Back-End, o banco de dados possui uma lógica interna para autenticação, caso contrário, seria possível transpassar o backend e enviar solicitações diretamente ao banco. Normalmente, é verificado se o IP que solicitou a requisição é o mesmo do IP do Back-End. Tal método de verificação já é implementado pelo FireBase.
Tanto as tecnologias utilizadas front-end quanto as no back-end ancoram-se no node.js e seu gerenciador de pacotes, o npm. Em suma, Node trata-se de um interpretador de código JavaScript de modo assíncrono e orientado a eventos, com objetivo de permitir a execução de código Javascript independentemente do Browser, ou seja, nativamente por um Servidor.
Para rodar um código javascript pelo Node, basta:
node index.js
Segue um tutorial de instalação no ubuntu e um no Windows. A versão LTS do Node já instala automaticamente o npm, gerenciador que permite instalação e distribuição facilitadas de pacotes voltados ao ambiente node.
.1 Programação funcional vs POO
.2 Event Loop
.3 JS Assíncrono: Callbacks, Promises e Async/Await
.5 Desestruturação (Essencial para entender States)
Ferramenta de versionamento que permite manter repositórios com snapshots de diversos momentos de um projeto. Github é uma plataforma que permite compartilhamento online de repositórios git. recomendamos fortemente o uso de git, pois o mesmo facilita o desenvolvimento remoto de um mesmo projeto por vários autores diferentes, bem como manter um backup da versão atual e anteriores do projeto.
Em suma, possui quatro locais/momentos de desenvolvimento:
1 - WorkSpace:
Local onde os arquivos estão sendo modificados. Para fazer de um diretório um WorkSpace de git, basta utilizar
git init
Pode ser mais fácil, entretanto, criar primeiramente um repositório no github, e então utilizar:
git clone url_do_repositorio
Pois isto fará o estágio de push mais prático.
2 - Stage
Local onde se pode acumular diversas alterações de um arquivo, sem de fato salvá-las no repositório local. Serve como uma "caixa intermediária" entre o workspace e o repositório local.
Para adicionar um arquivo ao stage, basta utilizar
git add nome_do_arquivo
E, para adicionar uma pasta inteira,
git add .
3 - Repositório Local
Local que mantém a versão mais atualizada do projeto, bem como o histórico de snapshots do mesmo com todas as versões que foram commitadas. Para adicionar o Stage ao Repositório local, basta:
git commit -m "Mensagem de commit"
4 - Repositório na Nuvem - GitHub
Para subir o repositório local para a nuvem:
1- Caso tenha sido utilizado o git clone
na etapa 1. , basta utilizar:
git push
2 - Caso tenha sido utilizado o git init
, é necessário utilizar na primeira vez:
git push --set-upstream url_do_repositorio
Após isto, git push
por sí só poderá ser utilizado para commits futuros.
Para boas práticas e informações mais avançadas sobre git, como Fetch/Rebase e Branches, consultar o tutorial de Git fortemente recomendado por UCLSanca
Deve ser utilizando o React para a criação da SPA, com a abordagem de Componentes Funcionais (Hooks).
A instalação e a criação de um app React é feita com:
npx create-react-app my_app
Para iniciar a aplicação:
cd my_app
npm start
Sugere-se o uso do TailWind como FrameWork de CSS para a estilização da página. O tutorial da instalação completa em um projeto, bem como exemplos de utilização podem ser encontrado na documentação. Para desenvolvimento, por praticidade, sugere-se a instalação via CDN.
Basta incluir:
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
Na tag <head> </head>
do index.html criado automaticamente na pasta public
do app pelo React.
Axios é um cliente que tem por objetivo enviar requests HTTP e escutar suas respostas. Segue uma boa vídeo aula que exemplifica o uso de axios em React.
Sua instalação também depende do npm, e deve ser feita Dentro de my_app
npm install axios
Por fim, deve ser importado no arquivo .js com
import axios from "axios";
Sugere-se começar o desenvolvimento da aplicação pelo design das páginas, bem como sua implementação por HTML/Tailwind/CSS. Caso desejado, é possível desenhar todo o design da aplicação com alguma ferramenta de prototipação, como o Marvel App ou Adobe XD. Exemplo de páginas mockadas: Protótipo Site Digimon e Protótipo Ganesh. A aplicação não precisa ser responsiva (Mas ficaremos muito felizes se for :)), ficando a cargo do desenvolvedor escolher desenvolvê-la com foco em navegação desktop ou mobile.
Em seguida, sugere-se desenvolver a lógica da página de gerenciamento de vídeos, especificamente as funções de adicionar e remover vídeos, bem como retornar a lista. Futuramente, a lista de vídeos será requerida no Back-End. Por hora, sugere-se contornar este problema com a criação de um arquivo lista.js
na pasta pública do projeto. Tal arquivo será importado por index.js
e deverá possuir, além de uma lista inicialmente, vazias, funcoes getLista, AdicionaItem e DeleteItem. Componentes que se utilizem da lista também deverão importá-lo.
Não é necessário carregar vídeos inteiros na aplicação. A intenção é que se use o <embed></embed>
de vídeos do youtube. A aplicação apenas salvará links para estes vídeos, mesmo quando o Back-End for implementado. A parte lógica da página de Login é amarrada ao Back-End e não é prioridade nesta etapa de desenvolvimento.
A página gerada não é persistiva, isto é, modificações na lista de vídeos se perdem ao se atualizar a página. Entretanto, deverá ser possível adicionar um vídeo pela página de gerenciamento, e acessar a lista de vídeos criados na página de vídeos. Cada página, isto é, login, cadastro, vídeos e gerenciamento deve possuir seu próprio componente.
Não é necessário se preocupar com o Axios ou com o envio de requisições e recebimento de suas respostas, que serão trabalhadas na entrega 2.
Estrutura sugerida para as pastas do projeto:
Login: [email protected] Senha: public
Este documento tem como objetivo especificar o backend do projeto dev.academy do USPCodeLab. Será utilizada a arquitetura REST junto com o protocolo HTTP.
Material de apoio:Tutorial básico de Node/Express
- Criar conta em firebase.google.com
- Ir em console, console.firebase.google.com/
- Criar um projeto com um nome sem caracteres especiais
- Não ativar o google analytics
Ir para o terminal
- No mesmo nível da pasta Frontend, criar uma pasta Backend
mkdir Backend
cd Backend
npx firebase-tools
npx firebase-tools login
n
Em seguida, logar na conta do google (Abrirá um pop-up no navegador) e concordar com permissões. A mensagem deverá ser retornada no terminal:
Waiting for authentication...
✔ Success! Logged in as [email protected]
Voltar para o terminal.
npx firebase-tools init
O terminal retornará:
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
/home/USUARIOS/9793502/Backend
? Which Firebase CLI features do you want to set up for this folder? Press Space
to select features, then Enter to confirm your choices. (Press <space> to selec
t, <a> to toggle all, <i> to invert selection)
◯ Database: Deploy Firebase Realtime Database Rules
◯ Firestore: Deploy rules and create indexes for Firestore
❯◯ Functions: Configure and deploy Cloud Functions
◯ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
Selecionar com a barra de espaço apenas functions. Apertar enter.
? Please select an option: (Use arrow keys)
❯ Use an existing project
Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
Com o enter, selecionar use an existing project
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: (Use arrow keys)
❯ myproject-2e373 (myProject)
Com o enter, escolher seu projeto.
? What language would you like to use to write Cloud Functions? (Use arrow keys)
❯ JavaScript
TypeScript
Escolher javascript. Escolher y para o linter.
? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? Yes
✔ Wrote functions/package.json
✔ Wrote functions/.eslintrc.json
✔ Wrote functions/index.js
✔ Wrote functions/.gitignore
? Do you want to install dependencies with npm now? (Y/n)
Escolha Y.
Foram criados vários arquivos:
- firebaserc - Arquivo de especificações.Não será manipulado.
- gitignore - Arquivo que define quais arquivos e pastas serão ignorados pelo git. Não será manipulado.
- ./functions - Pasta principal do Projeto:
- .gitignore
- package.json - Configurações do projeto firebase. Contém dependencias, scripts e o nome do projeto. Não será Manipulado
- eslintrc.json - Configurações do Linter. Não será manipulado
- index.js - Arquivo principal
index.js contém inicialmente:
const functions = require('firebase-functions');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
Descomente o código:
const functions = require('firebase-functions');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
Em seguida, na pasta Backend, utilize o comando:
npx firebase-tools deploy --only functions
Isto irá demorar.
O console retornará o link do projeto na última linha:
✔ functions[helloWorld(us-central1)]: Successful create operation.
Function URL (helloWorld): https://us-central1-myproject-2e373.cloudfunctions.net/helloWorld
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/myproject-2e373/overview
Abra o primeiro link no navegador. Hello from Firebase!
será retornado!
No projeto, utilizaremos o Express, biblioteca de node responsável por responder requisições HTTP.
Primeiramente, instale o express na pasta functions:
cd functions
npm install express
Em index.js:
- Importar o express no começo do arquivo:
const express = require('express');
- Inicializar o expres em uma variável app:
const app = express();
- Substituir o callback de functions.https.onRequest por app:
exports.helloWorld = functions.https.onRequest(app);
- Criar a função desejada:
app.get("/oi", (req,res) => {
res.send("Bom dia do angra");
})
Index final:
const functions = require('firebase-functions');
const express = require('express');
const app = express();
app.get("/oi", (req,res) => {
res.send("Bom dia do angra");
})
exports.helloWorld = functions.https.onRequest(app);
O backend pode ser testado localmente, o que usualmente é mais rápido, ou deployado, o que permite seu uso por qualquer um com acesso à url.
npx firebase-tools serve
Neste caso, será retornado:
=== Serving from '/home/USUARIOS/9793502/Backend'...
⚠ Your requested "node" version "8" doesn't match your global version "10"
✔ functions: Emulator started at http://localhost:5000
i functions: Watching "/home/USUARIOS/9793502/Backend/functions" for Cloud Functions...
✔ functions[helloWorld]: http function initialized (http://localhost:5000/myproject-2e373/us-central1/helloWorld).
i functions: Beginning execution of "helloWorld"
i functions: Finished "helloWorld" in ~1s
Neste caso, o root de teste será http://localhost:5000/myproject-2e373/us-central1/helloWorld.
A resposta à requisições get dadas no endpoint /oi podem ser acessadas, portanto, em http://localhost:5000/myproject-2e373/us-central1/helloWorld/oi
npx firebase-tools deploy
Observações:
- exports da função deve ser última coisa do código.
- O link das funções de seu projeto estão disponíveis em https://console.firebase.google.com/project/NOME_DO_SEU_PROJETO/functions/list
- Usualmente, cria-se apenas uma função e controla-se os recursos da aplicação pelos endpoints de argumento em
app.algo('/endpoint')
- O "caminho root" é o que aparece na lista em 2.. No caso, https://us-central1-myproject-2e373.cloudfunctions.net/helloWorld.
- Para utilizar o recurso em "/oi", portanto, deve-se acessar a URL https://us-central1-myproject-2e373.cloudfunctions.net/helloWorld/oi
REST (Representational state transfer) é uma arquitetura para organizar APIs (como um backend). Ela descreve o significado semântico de cada parâmetro de uma requisição HTTP. Um HTTP Request tem os seguintes parâmetros:
-
Método: descrevem a ação do seu Request. Os mais populares métodos são:
- GET: Ação de ler dados.
- POST: Ação de escrever dados.
- DELETE: Ação de deletar dados.
- PATCH: Ação de modificar dados já existentes.
-
URI, Path ou Endpoint: é um identificador de recurso. Descreve qual o recurso que o Request está acessando. Exemplo de um Path: “/users/admin.json”
-
Headers: Informações adicionais sobre o Request. Normalmente descrevem coisas como permissões, tipo do conteúdo, etc…
-
Body: Corpo da requisição. O corpo contém as principais informações da requisição. Em REST, o conteúdo deve ser no formato JSON.
Cada requisição deve ter uma resposta (tambem no formato HTTP). Uma resposta tem os seguintes parâmetros:
-
Status Code: O Status Code de uma resposta indica se o Request foi bem sucedido ou não. Alguns status code famosos são:
- 200: OK! Deu tudo certo!
- 404: Não encontrado. O recurso ao qual o Request se refere não pode ser encontrado.
- 500: Erro interno do servidor. O servidor fez alguma besteira.
-
Headers: Análogos aos headers de requisições, porêm são dados adicionais à resposta.
-
body: Tambem análogo ao corpo de requisições. É onde os dados respondidos devem ser enviados, no formato JSON.
Em REST, cada requisição deve ser referente a um recurso do servidor (especificado pelo endpoint). O método da requisição deve descrever a ação a ser tomada neste recurso (GET - ler, POST - criar novo, DELETE - deletar, PATCH - editar). O corpo contem os dados necessarios para a ação desejada (exemplo: informações de um usuario para um login).
A resposta da requisição requer apenas que o Status Code seja semântico.
Index:
Index:
Contém os seguintes dados referentes aos usuários:
- Senha Criptografada
Método: POST
Endpoint: /users
Não requer autenticação
Corpo da requisição:
{
email: string,
password: string
}
Corpo da resposta quando sucesso (200): Vazio
Corpo da resposta quando email invalido (400):
{
message: "Email is not valid"
}
Corpo da resposta quando senha invalida (400):
{
message: "Password is not valid"
}
Corpo da resposta quando email já existe (400):
{
message: "Email is already in use"
}
Método: POST
Endpoint: /users/login
Não requer autenticação
Corpo da requisição:
{
email: string,
password: string
}
Corpo da resposta quando sucesso (200):
{
token: string
}
Corpo da resposta quando credenciais incorretas (400):
{
message: "invalid email/password"
}
Método: GET
Endpoint: /users
Não requer autenticação
Corpo da requisição: Vazio
Corpo da resposta:
{
users: {email: string}[]
}
Método: DELETE
Endpoint: /users/:id
requer autenticação (necessita um token)
Corpo da requisição: Vazio
Corpo da resposta quando sucesso(200): Vazio
Corpo da resposta quando não se está logado (400)
{
message: "You must be logged in"
}
Corpo da resposta caso o usuario não exista (400)
{
message: "User not found"
}
O usuario só pode ser deletado por si mesmo
Contem os seguintes dados de vídeos:
- timestamp
- email do usuário criador
- url do video
- nome do video
- descrição do video
- ID do video
Método: POST
Endpoint: /videos
Requer autenticação
Corpo da requisição:
{
timestamp: number,
ownerEmail: string,
url: string,
title: string,
description: string
}
Corpo da resposta quando sucessor (200):
{
videoId: string
}
Corpo da resposta quando não se esta logado (400):
{
message: "You must be logged in"
}
Corpo da resposta quando alguma informação do video esta incorreta (400):
{
message: "invalid video info"
}
Método: GET
Endpoint: /videos
Não requer autenticação
Corpo da requisição: Vazio
Corpo da resposta:
{
videos: {
timestamp: number,
ownerEmail: string,
url: string,
title: string,
description: string,
id: string
}[]
}
Método: DELETE
Endpoint: /videos/:id
requer autenticação
Corpo da requisição: Vazio
Corpo da resposta quando sucesso (200): Vazio
Corpo da resposta quando não se está logado:
{
message: "You must be logged in"
}
Corpo da resposta quando o video não é do usuario logado (400):
{
message: "Not enough permissions"
}
Corpo da resposta quando o video não existe (400):
{
message: "video not found"
}
O vídeo só pode ser deletado pelo seu criador