Este projeto faz parte da continuação do curso de GraphQL.
Clique aqui🔗 para acessar o repositório com a introdução do curso onde foi desenvolvido um exemplo de cliente / servidor que é a base deste projeto.
A implementação deste projeto foi dividido em backend e frontend:
- backend - apollo server
- frontend - react
No backend foi desenvolvida toda a infraestrutura de dados em GraphQL manipulando um arquivo JSON.
No frontend foi desenvolvida a interface para receber e editar os dados do backend.
Recursos atuais:
- Criar, editar e remover clientes e demandas - backend
- Listar clientes - backend e frontend
Futuros recursos:
- Criar a regra de negócio entre clientes e demandas.
◽ Acessar🔗 o código fonte pelo GitHub.
◽ Download🔗 do código fonte no formato zip.
◽ Clonar o repositório ultilizando Git Bash + o comando:
git clone https://github.com/brseghese/hc3-dev-demands.git
Após clonar ou fazer o download do projeto digitar no terminal:
pnpm i --filter @dev-demands/server
pnpm i --filter @dev-demands/web
Instruções para executar o projeto clique aqui🔗
Clique para Navegar 🔽
O projeto base - exemplo de cliente / servidor - foi desenvolvido em um único pacote.
Iremos transformar o projeto em um Monorepo (mais de um pacote).
Organizando as Pastas
- packages
- web
- cliente
- server
- servidor
- web
Deletando "package-lock.json" usaremos o lock-file do pnpm.
Deletando "node_modules" será gerado novamente com o pnpm.
npm i -g pnpm
pnpm init
Nomeando o pacote como @dev-demands/root.
O @ indica scopo de pacote - @dev-demands será todos os pacotes que teremos.
O root indica o domínio raiz.
Criando um arquivo "pnpm-workspaces.yaml" - contém a indicação dos nossos pacotes.
packages:
- "packages/**"
O "yaml" é similar ao json, ele é mais simples de escrever e específico para arquivo de configuração.
Nomeando "package.json" do server para @dev-demands/server.
Nomeando "package.json" do client para @dev-demands/web.
pnpm i --filter @dev-demands/server
O pacote instalado gera o node_modules na pasta server e um pnpm-lock.yaml.
pnpm --filter @dev-demands/server run start
pnpx create-react-app packages/web
O pnpx vem instalado junto com o pnpm e executa uma função externa.
Deletando "package-lock.json" usaremos o lock-file do pnpm.
pnpm i --filter @dev-demands/web
pnpm i react-router-dom --filter @dev-demands/web
Codificando a interface web.
pnpm --filter @dev-demands/web run start
pnpm i express --filter @dev-demands/server
O Express é uma biblioteca para facilitar as requisições com as APIs.
Codificando o express no "main.js" e deixando a API pronta.
pnpm --filter @dev-demands/server run start
O express abstrai muito mais coisas do servidor e é muito mais prático para trabalhar.
Codificando o client para fazer o authenticate.
Executando o server na porta 8000 e o client na porta 3000.
Tratando a requisição para o authenticate.
Para resolver precisamos configurar o server para lidar com as requisições.
Lidando com os cabeçalhos específicos que os Browsers pedem para validar se os métodos são válidos e se o host (origin) que estamos usando para fazer as requisições são válidas.
Instalando a biblioteca "cors" resolvemos o CORS.
pnpm i cors --filter @dev-demands/server
- Recebe parâmetros e envia dados usando
JSON
como formato - Todas as operações são abstraídas dentro dos métodos HTTP (GET, POST, PUT, PATCH, DELETE e OPTIONS)
- GET - para buscar dados
- POST - para criar novas instâncias de dados
- PUT - para alterar o dado todo
- PATCH - para alterar determinado atributo do dado
- DELETE - para remover uma entidade
- OPTIONS - utilizado pelo Browser para checar opções dentro de um server
- As rotas, ou endpoints, são baseados nas entidades das aplicações.
- Ex: /users/:userID
Endpoints / Rotas - cada entidade (abstração da regra de negócio / domínios estruturados na aplicação) será um Endpoint diferente.
Ex: Endpoint de Usuário, de Empresa, de Pagamento, etc...
Exemplos de requisição RESTful
POST /users
{
"name": "Bruno Cecilio Seghese",
...
}
PATCH /users/1
{
"name": "Bruno Seghese",
}
As APIs RESTful são limitadas pelos protocolos que a definem, pois não é possível lidar com as relações entre as entidades sendo necessária a implementação manual deles.
Essas limitações resultam 3 situações que o GraphQL se propõe a resolver:
- Cascata de Requisições - Request Waterfall
- Dados insuficientes nas APIs - Under-Fetching
- Dados mais que suficientes nas APIs - Over-Fetching
Resumindo os protocolos Http são protocolos de mensagens e não de regras de negócio.
Apollo Server é uma plataforma / servidor para Node.js que trabalha com GraphQL.
Ele provê uma excelente interface para o trabalho.
Nesse projeto usaremos o Apollo Server Express que é uma implementação do Apollo Server Oficial.
pnpm i apollo-server-express --filter @dev-demands/server
Codificando o Apollo Server em "main.js".
Instalando o Nodemon
pnpm i -D nodemon --filter @dev-demands/server
O Nodemon reinicia o servidor a cada atualizaçõa.
O -D é porque ele é uma dependência de desenvolvimento, ou seja, não é uma dependência obrigatória para rodar nosso servidor.
Configurando Nodemon
"dev": "nodemon -r esm .",
pnpm --filter @dev-demands/server run dev
O TypeDefs
é uma propriedade do apollo server que define o tipo de respostas da API, podemos dizer que significa o que vai ser retornado.
O valor que vamos passar para o typeDefs será um gql
(função que trabalha com templeta string).
A sintaxe que vamos usar dentro das crases do gql é uma sintaxe de GraphQL.
const server = new ApolloServer({
typeDefs: gql`
...GraphQL...
`,
});
Os graphos são esquemas (possuem dados) e é a partir desse esquema que as queries são executadas e consequentemente validadas, tanto entrada quanto saída.
Podemos fazer as relações entre os graphos.
É preciso prover pelo menos um type query para poder operar os dados.
Type Query
é especificamente utilizado para definir como são feitas as consultas.
Resolvers
são basicamente o como as expectativas colocadas nos typeDefs serão resolvidas, devolvendo o dado solicitado.
O GraphQL possue uma documentação viva e dinâmica.
Na API que estamos executando colocando /graphql
na URL, uma página com toda documentação e outras funcionalidades serão disponibilizada.
Um boa maneira de manter a estrutura grapho é separando em camadas / pastas e definindo cada entidade em pasta e arquivo com seus respectivos typeDefs e Resolvers, podendo assim escalar de forma estruturada.
Para fazermos a união dos typeDefs e resolvers criamos os arquivos "typeDefs.js" e "resolvers.js".
O extend
permite extender o type query e adicionar outras queries com mais facilidade.
O type query global não pode ser vazio, então implementamos ele com um _root: String.
const typeDefs = gql`
type Query {
_root: String
}
`;
A ordem é obrigátoria, para o extend funcionar o extend global deve estar implementado.
const typeDefs = gql`
type Query {
_root: String
}
${clientTypeDefs}
${demandTypeDefs}
`;
Exportações Explícitas (Named Exports) (Zero ou mais exports por módulo).
Exportações Padrão (Default Exports) (Uma por módulo).
Usaremos Named Exports em typeDefs e resolvers.
Quando a propriedade e a constante tem o mesmo nome, podemos usar uma shorthand (propriedade abreviadas) do JavaScript.
import typeDefs from "./graphql/typeDefs";
const server = new ApolloServer({
typeDefs,
});
Quando importamos duas constantes iguais precisamos fazer um rename.
import { typeDefs as clientTypeDefs } from "./Client/Client";
import { typeDefs as demandTypeDefs } from "./Demand/Demand";
Precisamos configura o body parse nos Middleware do nosso server, porque antes tínhamos o express.json() e agora se mandarmos um json para o server ele não irá interpretar.
Então passamos um bodyParseConfig: true
server.applyMiddleware({
app,
cors: {
origin: "http://localhost:3000",
},
bodyParserConfig: true,
});
As Query é uma das três principais operações em GraphQL, elas praticamente assistem os dados e os retornam quando solicitados.
O GraphQL não diz respeito a banco de dados, ele é a camada de ligação entre o front e o back.
Gerando dados fakes no site mockaroo para consumir esses dados.
Inserindo os dados fakes em um arquivo "client.json".
Criando "server/io/Database/createRepository.js" e desenvolvendo a ligação entre o arquivo JSON e o GraphQL.
Buscando um cliente pelo id:
query GET_CLIENT($clientID: ID!) {
client(id: $clientID) {
id
name
email
disabled
}
}
// Query Variables - parâmetro para consulta
{
"clientID": "10b34d77-a60d-4916-8303-964f1e1261a4"
}
query GET_CLIENTS {
clients {
totalItems
items {
name
email
}
}
}
As Mutations é uma das três principais operações em GraphQL, elas deletam, alteram e incluem dados.
Observação: o Nodemon está assistindo os dados do "server/data/client.json" e vamos modificar isso para que ele não assista mais os dados.
Em "server/package.json":
"nodemonConfig": {
"ignore": [
"src/data/*"
]
O type mutation global não pode ser vazio, então implementamos ele com um _root: String.
const typeDefs = gql`
type Mutation {
_root: String
}
`;
Podemos extender a mutation com o global implementado.
Criando o input e extendendo a mutation no typeDefs.
O resolvers de query e mutations são iguais, têm os mesmos parâmetros:
- parent
- args
- context
- info
Para criar um novo cliente usaremos somente o parâmetro args.
Para gerar o id vamos utilizar uma biblioteca chamada "uuid".
pnpm i uuid --filter @dev-demands/server
Implementado a mutation no "client.js" e "resolvers.js" para inserir clientes.
mutation CREATE_CLIENT {
createClient(input:{
name: "Bruno Seghese",
email: "brsegh@gmail.com"
}) {
id
name
email
disabled
}
}
Implementado a mutation no "client.js" e "resolvers.js" para alterar clientes.
mutation UPDATE_CLIENT {
updateClient(
input: {
id: "e0b6e391-9191-469a-9322-b0491191870c"
name: "Eduardo Seghese"
email: "dudu@gmail.com"
}
) {
id
name
email
disabled
}
}
Implementado a mutation no "client.js" e "resolvers.js" para deletar clientes.
mutation DELETE_CLIENT {
deleteClient(id: "4a2a2588-f67b-42ac-aa48-39d7b37fae96") {
id
name
email
disabled
}
}
pnpm i apollo-link apollo-link-context apollo-link-error apollo-link-http --filter @dev-demands/web
Implementando "web/src/plugins/apollo/link.js"
pnpm i apollo-client graphql graphql-tag react-apollo --filter @dev-demands/web
pnpm i apollo-cache-inmemory --filter @dev-demands/web
Implementando "web/src/plugins/apollo/client.js"
Instalando plugin Apollo no Browser.
server
pnpm --filter @dev-demands/server run dev
web
pnpm --filter @dev-demands/web run start
Abrindo a página HOME - devtools Apollo - executar Query & Mutations
Esse projeto é licenciado pela MIT License. Veja aqui mais detalhes.
Clique aqui🔗 e acesse meu portfólio! 💼 (em construção...)
Desenvolvido com ❤️ por Bruno Seghese