Firebase Database não é bagunça!

Em:

Sim, temos regras e validações e vc irá se surpreender

nao

Psiu! Não conte a ninguém, mas até “foreign keys” tem, maluco né?!

A primeira impressão que se tem de uma base de dados não relacional como o Firebase, é de que ela simplesmente aceita tudo… na realidade aceita rsrs, mas podemos limitar colocando regras e é isso que iremos ver nesse artigo =D

A principal vantagem, que eu vejo numa base nosql, é não ter schema \o/, passei grande parte da minha vida escrevendo tabelas, tipando cada coluna, definindo PKs e FKs…. saudades #SQN!! kkk Vamos lembrar de como era, apenas para termos uma comparação Vamos criar uma tabela de estado, e uma tabela de pessoa, com suas devidas PKs e FKs

     CREATE TABLE Estado (
       EstadoId int NOT NULL PRIMARY KEY,
       Sigla varchar(2) NOT NULL
    );

    CREATE TABLE Pessoa (
       PessoaId int NOT NULL PRIMARY KEY,
       Nome varchar(50) NOT NULL,
       SobreNome varchar(50),
       email  varchar(30) NOT NULL,
       Idade int,
       Cidade varchar(50),
       EstadoId int
    );

    ALTER TABLE Pessoa
    ADD FOREIGN KEY (EstadoId) REFERENCES Estado(EstadoId);

Ficaram saudosistas né? rsrs Vamos fazer algo parecido com json, bom, como não temos como a definição iremos colocar os dados também

"estados" : {
"AC" : true,
"AL" : true,
"AM" : true,
"AP" : true,
"BA" : true,
"CE" : true,
"DF" : true,
"ES" : true,
"GO" : true,
"MA" : true,
"MG" : true,
"MS" : true,
"MT" : true,
"PA" : true,
"PB" : true,
"PE" : true,
"PI" : true,
"PR" : true,
"RJ" : true,
"RN" : true,
"RO" : true,
"RR" : true,
"RS" : true,
"SC" : true,
"SE" : true,
"SP" : true,
"TO" : true
}


"pessoas": [
    {
      "nome": "Evelyn",
      "sobrenome": "Mendes",
      "email": "[email protected]",
      "idade": 35,
      "cidade": "Porto Alegre",
      "estado": "RS"
     }
  ]

Diferente né? Mas até ai são apenas dois json que não tem relação nenhnuma com o outro, e que no caso aceitam qualquer valor em suas chaves, um exemplo: Vamos imaginar que alguém ao invés de colocar 35, um número inteiro, coloque qualquer coisa no valor dessa chave “idade”: “trinta e cinco” eu acho que não ficaria legal e prejudicaria muito uma pesquisa. Para resolver isso iremos usar as regras do banco de dados do firebase

screen-shot-2017-09-25-at-00-07-00

Ao entrar nas regras vc verá algo definido bem simples

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Aonde diz que toda a base de dados pode ser lida e gravada livremente por qualquer usuário. Mas isso não é nada legal, quem sabe a gente deixa apenas que leiam, mas não que escrevam? E como fazemos isso? O Firebase tem server variables, uma delas é a auth, que traz o usuário logado e com essa variável podemos fazer uma simples modificação.

{
  "rules": {
    ".read": true,
    ".write": "auth != null"
  }
}

Pronto, agora somente usuários logados podem escrever na base de dados =D Dai vc pensa, e dai, grandes coisas, quero mais, muiiito mais!!!! Sim, temos mais! Agora vamos voltar a situação da idade e validar para apenas receber inteiros. Regras novamente

{
  "rules": {
    ".read": true,
    ".write": "auth !=null",

    "usuarios": {
      "$uid": {
        ".read": true,
        ".write": "auth.uid == $uid",
        ".validate": " newData.child('idade').isNumber()"
      }
    }
  }
}

Perceba a condição:

".validate": " newData.child('idade').isNumber()"

Ela validará se somente números serão inputados para a chave idade

Outra coisa que vc deve ter notado é o $uid, ele é um curinga (wild card), digo, nesse caso ele representa o id do usuário que gravou o registro, por isso que em .write, exite “auth.uid == $uid”, somente o dono do registro poderá escrever nele, o auth, como eu já disse é um server variable Existem 6 server variables, vamos a elas

Server Variables do Firebase

auth É o usuário autenticado

data São os dados existentes no caminho, exemplo, pessoa/idade

newData São os dados postados no caminho, esse variável só está disponível em .write e .validate pq somente nesses momentos que novos dados serão postados

now É uma marcação de data e hora de tempo real do servidor

root Levará de volta a raiz do banco de dados

$ Curinga, como usamos em $uid para pegar o id do usuário do registro


Voltando a situação de validações, podemos definir tb que os campos textos somente receberão strings e terão um tamanho determinado, vamos ver como fica no campo nome que além de tudo não pode ser nulo?

{
    "rules": {
        ".read": true,
        ".write": "auth !=null",

        "usuarios": {
        "$uid": {
            ".read": true,
            ".write": "auth.uid == $uid",
            ".validate": "newData.child('idade').isNumber() &&
                            newData.child('nome').exists() &&
                            newData.child('nome').isString() && 
                            newData.child('nome').val().length < 51 "
            }
        }
    }
}

Podemos repetir isso para os demais campos

{
    "rules": {
        ".read": true,
        ".write": "auth !=null",

        "usuarios": {
        "$uid": {
            ".read": true,
            ".write": "auth.uid == $uid",
            ".validate": "newData.child('idade').isNumber() &&
                            newData.child('nome').exists() &&
                            newData.child('nome').isString() && 
                            newData.child('nome').val().length < 51 &&
                            newData.child('sobrenome').isString() && 
                            newData.child('sobrenome').val().length < 51 &&
                            newData.child('cidade').isString() && 
                            newData.child('cidade').val().length < 51"                     
            }
        }
    }
}

A partir daqui a brincadeira começa a ficar mais interessante. Vcs viram que tem um campo de email não é? Quem sabe a gente não valida se é um endereço de email válido?????? Vamos, vamos?!?! =D Para isso usaremos as famigeradas expressões regulares uhuhuh

{
    "rules": {
        ".read": true,
        ".write": "auth !=null",

        "usuarios": {
        "$uid": {
            ".read": true,
            ".write": "auth.uid == $uid",
            ".validate": "newData.child('idade').isNumber() &&
                            newData.child('nome').exists() &&
                            newData.child('nome').isString() && 
                            newData.child('nome').val().length < 51 &&
                            newData.child('sobrenome').isString() && 
                            newData.child('sobrenome').val().length < 51 &&
                            newData.child('cidade').isString() && 
                            newData.child('cidade').val().length < 51 &&
                            newData.child('email').isString() && 
                            newData.child('email').val().matches(/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\\.[A-Z]{2,4}$/i)"
             }
          }
       }
    }  

Viram que lindo????? Pequeno ponto de observação, regExp para emails nem sempre conseguem suprir todas as necessidades, portanto cautela ao usar.


E o estado??? Até agora apenas temos aquela lista json separada lá, mas sei que ele tb deve entrar no nosso objeto pessoa, mas só se existir na tabela estados… hummm vc quer dizer uma FK? Nãooo, quero apenas validar se existe o valor, coisa simples, nada de mais, mas como fazemos isso? Iremos acessar a tabela estados usando a server variable root, e com ela verificar se o dado inputado está contido nela. Vamos lá!? Antes de colocarmos as regras é necessário dizer que o estado pode ser uma tabela que não deverá ser modificada de fora da base de dados, pois seus dados quase nunca são alterados, o que se pode fazer é deixar .read e .write como false, assim somente entrando no console>realtimedatabase vc poder alterar os valores, adicionar e excluir.

{
    "rules": {
        ".read": true,
        ".write": "auth !=null",
        "estados" :{
            ".read": false,
           ".write": false,
          },
        "usuarios": {
        "$uid": {
            ".read": true,
            ".write": "auth.uid == $uid",
            ".validate": "newData.child('idade').isNumber() &&
                            newData.child('nome').exists() &&
                            newData.child('nome').isString() && 
                            newData.child('nome').val().length < 51 &&
                            newData.child('sobrenome').isString() && 
                            newData.child('sobrenome').val().length < 51 &&
                            newData.child('cidade').isString() && 
                            newData.child('cidade').val().length < 51 &&
                            newData.child('email').isString() && 
                            newData.child('email').val().matches(/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\\.[A-Z]{2,4}$/i) && 
                            root.child('estados').child(newData.child('estado').val()).exists()"
             }
          }
       }
    }       

Olhem como é fácil entender o que ele faz, ele vai até a tabela estados

root.child('estados')

referencia os filhos passando o valor que veio objeto newData

.child(newData.child('estado').val())

e por fim pergunta se esse valor existe na coleção

.exists()

Não é simples??

root.child('estados').child(newData.child('estado').val()).exists()

Para entender melhor

root.child('estados').child('RS').exists()

Existe o estado!!!!!!!!!!

"estados" : {
   "RS" : true,
}

E como vou saber se isso tudo está certo? Sim, eu sei que é um pouco complexo e subjetivo em alguns momentos, é por isso que se usa o simulador das regras.

Exemplo de gravação com tudo ok

screen-shot-2017-09-25-at-02-30-15

Exemplo de gravação com idade errada, como string

screen-shot-2017-09-25-at-02-33-05

Também podemos testar a leitura dos dados sem autenticar screen-shot-2017-09-25-at-02-34-23


O Firebase database é uma ferramenta muito poderosa, esses exemplo do artigo chegam a ser simples para tanta coisa que ele pode oferecer. Aos poucos vamos falando mais sobre tudo que o BAAS (backend as a service) firebase pode nos oferecer.

Espero muito que tenham gostado Obrigada!!! <3

Atualização Firebase Firestore

4/10/2017 11:48

Ontem lançaram a nova base de dados nosql do firebase, o firestore, dai resolvi dar uma pequena amostra de como funcionam as validações dentro dele, um pouco diferente, mas o conceito é o mesmo. O legal, tem funções nas validações 🙂 O não legal, não é mais json e não tem simulador, pelo menos até agora dentro console web não tinha 🙁


service cloud.firestore {
  match /databases/{database}/documents {
    // match /{document=**} {
    //   allow read, write: if true;
    //}
    match /contatos/{contatos}{
        allow read;
          allow write:  if request.resource.telefone is number && 
                        isAuthenticated() &&
                        isEmail(request.resource.email) &&
                        request.resource.size() == 6 &&
                        exists(/databases/$(database)/documents/estados/$(request.resource.estado));
    }
  }
    function isAuthenticated() {
      return request.auth != null;
    }
    function isEmail(email){
      return  email.matches('\\b[a-zA-Z0-9._%+-][email protected][a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b');
    }   
}
  • Teste de Aceitação com React, Jest e Nightmare

    O Jest é um framework de testes com baterias incluídas criado pelo Facebook. Ele é rápido, cheio de recursos e integrado perfeitamente com [Babel][2] (outra importante ferramenta do nosso processo de build hoje em dia). Jest cria uma experiência excepcional para testes unitários.

  • Interação com Hardware usando JavaScript

    É isso mesmo que você leu! Já ouvimos falar que o JavaScript “roda em tudo”, certo? Existem diversos exemplos que provam que tal afirmação está correta, e hoje irei mostrar um deles. Inclusive, se contássemos o que anda acontecendo atualmente, faríamos alguém dos anos 90 rir muito. Primeiramente, existem frameworks e plataformas diferentes: O Cylon.js, […]

  • Weekly #191 – ELM e Inglês no RSJS, Firefox 53 e Chrome 58 e melhoria no Atom

    Mais palestras do RSJS liberadas, novidades sobre navegadores, melhoria no Atom e muito mais!

Patrocinadores BrazilJS

Gold

Silver

Bronze

Apoio

BrazilJS® é uma iniciativa NASC.     Hosted by Getup