1. O que você vai criar
Neste codelab, você vai criar um blog de viagens com um mapa colaborativo em tempo real usando a versão mais recente da nossa biblioteca Angular: AngularFire. O web app final vai consistir em um blog de viagens em que você pode enviar imagens de cada lugar que visitou.
O AngularFire será usado para criar o app da Web, o Pacote de emuladores para testes locais, o Authentication para acompanhar os dados do usuário, o Firestore e o Storage para manter dados e mídia, com tecnologia do Cloud Functions e, por fim, o Firebase Hosting para implantar o app.
O que você vai aprender
- Como desenvolver com produtos do Firebase localmente com o Pacote de emuladores
- Como melhorar seu app da Web com o AngularFire
- Como manter os dados no Firestore
- Como manter mídia no Storage
- Como implantar seu app no Firebase Hosting
- Como usar o Cloud Functions para interagir com seus bancos de dados e APIs
Pré-requisitos
- Node.js versão 10 ou mais recente
- Uma Conta do Google para a criação e o gerenciamento do seu projeto do Firebase
- A versão 11.14.2 ou mais recente da CLI do Firebase
- Um navegador da sua escolha, como o Chrome
- Noções básicas de Angular e JavaScript
2. Acessar o exemplo de código
Clone o repositório do GitHub (link em inglês) do codelab na linha de comando:
git clone https://github.com/firebase/codelab-friendlychat-web
Se preferir, baixe o repositório como um arquivo ZIP.
O repositório do GitHub contém projetos de exemplo para várias plataformas.
Este codelab usa apenas o repositório webframework:
- 📁 webframework: o código inicial que você vai usar como base neste codelab.
Instalar dependências
Depois de clonar, instale as dependências na raiz e na pasta functions
antes de criar o app da Web.
cd webframework && npm install
cd functions && npm install
Instalar a CLI do Firebase
Instale a CLI do Firebase usando este comando em um terminal:
npm install -g firebase-tools
Verifique se a versão da CLI do Firebase é maior que 11.14.2 usando:
firebase --version
Se a versão for anterior à 11.14.2, atualize usando:
npm update firebase-tools
3. Criar e configurar um projeto do Firebase
Criar um projeto do Firebase
- Faça login no console do Firebase usando sua Conta do Google.
- Clique no botão para criar um projeto e insira um nome (por exemplo,
FriendlyChat
).
- Clique em Continuar.
- Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
- (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
- Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
- Clique em Criar projeto, aguarde o provisionamento e clique em Continuar.
Adicionar um app da Web do Firebase ao projeto
- Clique no ícone da Web para criar um app da Web do Firebase.
- Na próxima etapa, você vai encontrar um objeto de configuração. Copie o conteúdo desse objeto no arquivo
environments/environment.ts
.
Configurar produtos do Firebase
O aplicativo que vamos criar usa produtos do Firebase disponíveis para apps da Web:
- Firebase Authentication: usado para permitir que os usuários acessem seu app facilmente.
- Cloud Firestore: é usado para salvar dados estruturados na nuvem e receber notificações instantâneas quando os dados são alterados.
- Cloud Storage para Firebase: é usado para salvar arquivos na nuvem.
- Firebase Hosting: é usado para hospedar e exibir seus recursos.
- Funções para interagir com APIs internas e externas.
Alguns desses produtos precisam de configurações especiais ou precisam ser ativados usando o console do Firebase.
Ative o login do Google para o Firebase Authentication
Para permitir que os usuários façam login no app da Web com as Contas do Google, vamos usar o método de login do Google.
Para ativar o Login do Google:
- No console do Firebase, localize a seção Build no painel à esquerda.
- Clique em Autenticação e na guia Método de login (ou clique aqui para acessar diretamente).
- Ative o provedor de login do Google e clique em Salvar.
- Defina o nome público do app como <your-project-name> e escolha um E-mail de suporte do projeto no menu suspenso.
Ativar o Cloud Firestore
- Na seção Build do Console do Firebase, clique em Firestore Database.
- Clique em Criar banco de dados no painel do Cloud Firestore.
- Defina o local em que os dados do Cloud Firestore são armazenados. Você pode deixar essa opção como padrão ou escolher uma região próxima a você.
Ativar o Cloud Storage
O app da Web usa o Cloud Storage para Firebase para armazenar, fazer upload e compartilhar fotos.
- Na seção Build do console do Firebase, clique em Storage.
- Se não houver um botão Começar, significa que o Cloud Storage já está
ativado, e não é necessário seguir as etapas abaixo.
- Clique em Começar.
- Leia o aviso sobre as regras de segurança do seu projeto do Firebase e clique em Próxima.
- O local do Cloud Storage é pré-selecionado com a mesma região escolhida para o banco de dados do Cloud Firestore. Clique em Concluído para terminar a configuração.
Com as regras de segurança padrão, qualquer usuário autenticado pode gravar qualquer coisa no Cloud Storage. Vamos tornar nosso armazenamento mais seguro mais adiante neste codelab.
4. Conectar ao seu projeto do Firebase
A interface de linha de comando (CLI) do Firebase permite usar o Firebase Hosting para disponibilizar seu app da Web localmente e implantá-lo no seu projeto do Firebase.
Confira se a linha de comando está acessando o diretório webframework
local do seu app.
Conecte o código do app da Web ao seu projeto do Firebase. Primeiro, faça login na CLI do Firebase na linha de comando:
firebase login
Em seguida, execute o comando a seguir para criar um alias de projeto. Substitua $YOUR_PROJECT_ID
pelo ID do seu projeto do Firebase.
firebase use $YOUR_PROJECT_ID
Adicionar o AngularFire
Para adicionar o AngularFire ao app, execute o comando:
ng add @angular/fire
Em seguida, siga as instruções da linha de comando e selecione os recursos que existem no seu projeto do Firebase.
Inicializar o Firebase
Para inicializar o projeto do Firebase, execute:
firebase init
Em seguida, seguindo os comandos da linha de comando, selecione os recursos e emuladores usados no seu projeto do Firebase.
Iniciar os emuladores
No diretório webframework
, execute o seguinte comando para iniciar os emuladores:
firebase emulators:start
Depois de um tempo, você vai ver algo assim:
$ firebase emulators:start
i emulators: Starting emulators: auth, functions, firestore, hosting, functions
i firestore: Firestore Emulator logging to firestore-debug.log
i hosting: Serving hosting files from: public
✔ hosting: Local server: http://localhost:5000
i ui: Emulator UI logging to ui-debug.log
i functions: Watching "/functions" for Cloud Functions...
✔ functions[updateMap]: firestore function initialized.
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://localhost:4000 │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Hosting │ localhost:5000 │ n/a │
└────────────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at localhost:4400
Other reserved ports: 4500
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
Quando a mensagem ✔All emulators ready!
aparecer, os emuladores estarão prontos para uso.
Você vai ver a interface do usuário do app de viagens, que (ainda) não está funcionando:
Agora vamos criar!
5. Conectar o app da Web aos emuladores
Com base na tabela nos registros do emulador, o emulador do Cloud Firestore detecta atividade na porta 8080 e o emulador do Authentication detecta atividade na porta 9099.
Abrir a EmulatorUI
No navegador da Web, acesse http://127.0.0.1:4000/. A interface do Pacote de emuladores vai aparecer.
Encaminhar o app para usar os emuladores
Em src/app/app.module.ts
, adicione o seguinte código à lista de importações de AppModule
:
@NgModule({
declarations: [...],
imports: [
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
const auth = getAuth();
if (location.hostname === 'localhost') {
connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
}
return auth;
}),
provideFirestore(() => {
const firestore = getFirestore();
if (location.hostname === 'localhost') {
connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
}
return firestore;
}),
provideFunctions(() => {
const functions = getFunctions();
if (location.hostname === 'localhost') {
connectFunctionsEmulator(functions, '127.0.0.1', 5001);
}
return functions;
}),
provideStorage(() => {
const storage = getStorage();
if (location.hostname === 'localhost') {
connectStorageEmulator(storage, '127.0.0.1', 5001);
}
return storage;
}),
...
]
O app agora está configurado para usar emuladores locais, permitindo que testes e desenvolvimento sejam feitos localmente.
6. Adicionar autenticação
Agora que os emuladores estão configurados para o app, podemos adicionar recursos de autenticação para garantir que cada usuário faça login antes de postar mensagens.
Para isso, podemos importar funções signin
diretamente do AngularFire e rastrear o estado de autenticação do usuário com a função authState
. Modifique as funções da página de login para que ela verifique o estado de autenticação do usuário ao carregar.
Como injetar a autenticação do AngularFire
Em src/app/pages/login-page/login-page.component.ts
, importe Auth
de @angular/fire/auth
e injete-o no LoginPageComponent
. Provedores de autenticação, como o Google, e funções comosignin
e signout
também podem ser importados diretamente do mesmo pacote e usados no app.
import { Auth, GoogleAuthProvider, signInWithPopup, signOut, user } from '@angular/fire/auth';
export class LoginPageComponent implements OnInit {
private auth: Auth = inject(Auth);
private provider = new GoogleAuthProvider();
user$ = user(this.auth);
constructor() {}
ngOnInit(): void {}
login() {
signInWithPopup(this.auth, this.provider).then((result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
return credential;
})
}
logout() {
signOut(this.auth).then(() => {
console.log('signed out');}).catch((error) => {
console.log('sign out error: ' + error);
})
}
}
Agora a página de login está funcional. Tente fazer login e confira os resultados no emulador de autenticação.
7. Como configurar o Firestore
Nesta etapa, você vai adicionar a funcionalidade para postar e atualizar postagens de blogs de viagens armazenadas no Firestore.
Assim como o Authentication, as funções do Firestore vêm pré-empacotadas do AngularFire. Cada documento pertence a uma coleção, e cada documento também pode ter coleções aninhadas. É necessário saber o path
do documento no Firestore para criar e atualizar uma postagem de blog de viagens.
Implementar o TravelService
Como muitas páginas diferentes precisarão ler e atualizar documentos do Firestore no web app, podemos implementar as funções em src/app/services/travel.service.ts
para evitar a injeção repetida das mesmas funções do AngularFire em todas as páginas.
Comece injetando Auth
, semelhante à etapa anterior, e Firestore
no nosso serviço. Também é útil definir um objeto user$
observável que detecta o status de autenticação atual.
import { doc, docData, DocumentReference, Firestore, getDoc, setDoc, updateDoc, collection, addDoc, deleteDoc, collectionData, Timestamp } from "@angular/fire/firestore";
export class TravelService {
firestore: Firestore = inject(Firestore);
auth: Auth = inject(Auth);
user$ = authState(this.auth).pipe(filter(user => user !== null), map(user => user!));
router: Router = inject(Router);
Adicionar uma postagem sobre viagem
As postagens de viagens vão existir como documentos armazenados no Firestore. Como os documentos precisam estar em coleções, a coleção que contém todas as postagens de viagens será chamada de travels
. Assim, o caminho de qualquer postagem de viagem será travels/
Usando a função addDoc
do AngularFire, um objeto pode ser inserido em uma coleção:
async addEmptyTravel(userId: String) {
...
addDoc(collection(this.firestore, 'travels'), travelData).then((travelRef) => {
collection(this.firestore, `travels/${travelRef.id}/stops`);
setDoc(travelRef, {... travelData, id: travelRef.id})
this.router.navigate(['edit', `${travelRef.id}`]);
return travelRef;
})
}
Atualizar e excluir dados
Com o uid de qualquer postagem de viagem, é possível deduzir o caminho do documento armazenado no Firestore, que pode ser lido, atualizado ou excluído usando as funções updateFoc
e deleteDoc
do AngularFire:
async updateData(path: string, data: Partial<Travel | Stop>) {
await updateDoc(doc(this.firestore, path), data)
}
async deleteData(path: string) {
const ref = doc(this.firestore, path);
await deleteDoc(ref)
}
Como ler dados como um observable
Como as postagens e paradas de viagem podem ser modificadas após a criação, é mais útil receber objetos de documento como observáveis para se inscrever em qualquer mudança feita. Essa funcionalidade é oferecida pelas funções docData
e collectionData
de @angular/fire/firestore
.
getDocData(path: string) {
return docData(doc(this.firestore, path), {idField: 'id'}) as Observable<Travel | Stop>
}
getCollectionData(path: string) {
return collectionData(collection(this.firestore, path), {idField: 'id'}) as Observable<Travel[] | Stop[]>
}
Adicionar paradas a uma postagem de viagem
Agora que as operações de postagem de viagem estão configuradas, é hora de considerar as paradas, que vão existir em uma subcoleção de uma postagem de viagem, assim: travels/
Isso é quase idêntico à criação de uma postagem de viagem. Por isso, tente implementar por conta própria ou confira a implementação abaixo:
async addStop(travelId: string) {
...
const ref = await addDoc(collection(this.firestore, `travels/${travelId}/stops`), stopData)
setDoc(ref, {...stopData, id: ref.id})
}
Muito bem! As funções do Firestore foram implementadas no serviço de viagens. Agora você pode vê-las em ação.
Como usar funções do Firestore no app
Navegue até src/app/pages/my-travels/my-travels.component.ts
e injete TravelService
para usar as funções dele.
travelService = inject(TravelService);
travelsData$: Observable<Travel[]>;
stopsList$!: Observable<Stop[]>;
constructor() {
this.travelsData$ = this.travelService.getCollectionData(`travels`) as Observable<Travel[]>
}
TravelService
é chamado no construtor para receber uma matriz observável de todas as viagens.
Se você precisar apenas das viagens do usuário atual, use a função query
.
Outros métodos para garantir a segurança incluem a implementação de regras de segurança ou o uso do Cloud Functions com o Firestore, conforme explicado nas etapas opcionais abaixo.
Em seguida, basta chamar as funções implementadas em TravelService
.
async createTravel(userId: String) {
this.travelService.addEmptyTravel(userId);
}
deleteTravel(travelId: String) {
this.travelService.deleteData(`travels/${travelId}`)
}
Agora a página "Minhas viagens" deve estar funcionando. Confira o que acontece no emulador do Firestore quando você cria uma nova postagem de viagem.
Em seguida, repita para as funções de atualização em /src/app/pages/edit-travels/edit-travels.component.ts
:
travelService: TravelService = inject(TravelService)
travelId = this.activatedRoute.snapshot.paramMap.get('travelId');
travelData$: Observable<Travel>;
stopsData$: Observable<Stop[]>;
constructor() {
this.travelData$ = this.travelService.getDocData(`travels/${this.travelId}`) as Observable<Travel>
this.stopsData$ = this.travelService.getCollectionData(`travels/${this.travelId}/stops`) as Observable<Stop[]>
}
updateCurrentTravel(travel: Partial<Travel>) {
this.travelService.updateData(`travels${this.travelId}`, travel)
}
updateCurrentStop(stop: Partial<Stop>) {
stop.type = stop.type?.toString();
this.travelService.updateData(`travels${this.travelId}/stops/${stop.id}`, stop)
}
addStop() {
if (!this.travelId) return;
this.travelService.addStop(this.travelId);
}
deleteStop(stopId: string) {
if (!this.travelId || !stopId) {
return;
}
this.travelService.deleteData(`travels${this.travelId}/stops/${stopId}`)
this.stopsData$ = this.travelService.getCollectionData(`travels${this.travelId}/stops`) as Observable<Stop[]>
}
8. Como configurar o armazenamento
Agora você vai implementar o Storage para armazenar imagens e outros tipos de mídia.
O Cloud Firestore é mais adequado para armazenar dados estruturados, como objetos JSON. O Cloud Storage foi projetado para armazenar arquivos ou blobs. Neste app, você vai usá-lo para permitir que os usuários compartilhem fotos de viagens.
Da mesma forma que no Firestore, o armazenamento e a atualização de arquivos com o Storage exigem um identificador exclusivo para cada arquivo.
Vamos implementar as funções em TraveService
:
Como enviar um arquivo
Navegue até src/app/services/travel.service.ts
e injete o armazenamento do AngularFire:
export class TravelService {
firestore: Firestore = inject(Firestore);
auth: Auth = inject(Auth);
storage: Storage = inject(Storage);
Implemente a função de upload:
async uploadToStorage(path: string, input: HTMLInputElement, contentType: any) {
if (!input.files) return null
const files: FileList = input.files;
for (let i = 0; i < files.length; i++) {
const file = files.item(i);
if (file) {
const imagePath = `${path}/${file.name}`
const storageRef = ref(this.storage, imagePath);
await uploadBytesResumable(storageRef, file, contentType);
return await getDownloadURL(storageRef);
}
}
return null;
}
A principal diferença entre acessar documentos do Firestore e arquivos do Cloud Storage é que, embora ambos sigam caminhos estruturados em pastas, a combinação de URL e caminho base é obtida pelo getDownloadURL
, que pode ser armazenado e usado em um arquivo
.
Como usar a função no app
Navegue até src/app/components/edit-stop/edit-stop.component.ts
e chame a função de upload usando:
async uploadFile(file: HTMLInputElement, stop: Partial<Stop>) {
const path = `/travels/${this.travelId}/stops/${stop.id}`
const url = await this.travelService.uploadToStorage(path, file, {contentType: 'image/png'});
stop.image = url ? url : '';
this.travelService.updateData(path, stop);
}
Quando a imagem é enviada, o arquivo de mídia é enviado para o armazenamento, e o URL é armazenado de acordo com o documento no Firestore.
9. Como implantar o aplicativo
Agora você já pode implantar o aplicativo.
Copie as configurações firebase
de src/environments/environment.ts
para src/environments/environment.prod.ts
e execute:
firebase deploy
Você vai conferir algo parecido com:
✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.
=== Deploying to 'friendly-travels-b6a4b'...
i deploying storage, firestore, hosting
i firebase.storage: checking storage.rules for compilation errors...
✔ firebase.storage: rules file storage.rules compiled successfully
i firestore: reading indexes from firestore.indexes.json...
i cloud.firestore: checking firestore.rules for compilation errors...
✔ cloud.firestore: rules file firestore.rules compiled successfully
i storage: latest version of storage.rules already up to date, skipping upload...
i firestore: deploying indexes...
i firestore: latest version of firestore.rules already up to date, skipping upload...
✔ firestore: deployed indexes in firestore.indexes.json successfully for (default) database
i hosting[friendly-travels-b6a4b]: beginning deploy...
i hosting[friendly-travels-b6a4b]: found 6 files in .firebase/friendly-travels-b6a4b/hosting
✔ hosting[friendly-travels-b6a4b]: file upload complete
✔ storage: released rules storage.rules to firebase.storage
✔ firestore: released rules firestore.rules to cloud.firestore
i hosting[friendly-travels-b6a4b]: finalizing version...
✔ hosting[friendly-travels-b6a4b]: version finalized
i hosting[friendly-travels-b6a4b]: releasing new version...
✔ hosting[friendly-travels-b6a4b]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/friendly-travels-b6a4b/overview
Hosting URL: https://friendly-travels-b6a4b.web.app
10. Parabéns!
Agora seu aplicativo está completo e implantado no Firebase Hosting. Todos os dados e análises agora estarão acessíveis no Console do Firebase.
Para mais recursos relacionados ao AngularFire, Functions e regras de segurança, não se esqueça de conferir as etapas opcionais abaixo, além de outros Codelabs do Firebase.
11. Opcional: proteção de autenticação do AngularFire
Além do Firebase Authentication, o AngularFire também oferece proteção baseada em autenticação nas rotas para que os usuários com acesso insuficiente possam ser redirecionados. Isso ajuda a proteger o app contra usuários que acessam dados protegidos.
Em src/app/app-routing.module.ts
, importe
import {AuthGuard, redirectLoggedInTo, redirectUnauthorizedTo} from '@angular/fire/auth-guard'
Em seguida, você pode definir funções para quando e onde os usuários devem ser redirecionados em determinadas páginas:
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['signin']);
const redirectLoggedInToTravels = () => redirectLoggedInTo(['my-travels']);
Em seguida, basta adicioná-los às rotas:
const routes: Routes = [
{path: '', component: LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe: redirectLoggedInToTravels}},
{path: 'signin', component: LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe: redirectLoggedInToTravels}},
{path: 'my-travels', component: MyTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe: redirectUnauthorizedToLogin}},
{path: 'edit/:travelId', component: EditTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe: redirectUnauthorizedToLogin}},
];
12. Opcional: regras de segurança
O Firestore e o Cloud Storage usam regras de segurança (firestore.rules
e security.rules
, respectivamente) para aplicar a segurança e validar os dados.
No momento, os dados do Firestore e do Storage têm acesso aberto para leitura e gravação, mas você não quer que as pessoas mudem as postagens de outras pessoas. É possível usar regras de segurança para restringir o acesso a coleções e documentos.
Regras do Firestore
Para permitir que apenas usuários autenticados vejam postagens de viagens, acesse o arquivo firestore.rules
e adicione:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/travels {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId;
}
}
As regras de segurança também podem ser usadas para validar dados:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/posts {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId;
&& "author" in request.resource.data
&& "text" in request.resource.data
&& "timestamp" in request.resource.data;
}
}
Regras do Storage
Da mesma forma, podemos usar regras de segurança para aplicar o acesso a bancos de dados de armazenamento em storage.rules
. Também é possível usar funções para verificações mais complexas:
rules_version = '2';
function isImageBelowMaxSize(maxSizeMB) {
return request.resource.size < maxSizeMB * 1024 * 1024
&& request.resource.contentType.matches('image/.*');
}
service firebase.storage {
match /b/{bucket}/o {
match /{userId}/{postId}/{filename} {
allow write: if request.auth != null
&& request.auth.uid == userId && isImageBelowMaxSize(5);
allow read;
}
}
}