VAGAS DE EMPREGO

balta.io balta.io
  • Cursos
  • Carreiras
  • Para sua Empresa
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog

Seja Premium
balta.io

  • Cursos
  • Carreiras
  • Para sua Empresa
  • Agenda
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog
  • Player
Seja Premium

Entre ou Cadastre-se

  • Home
  • Artigos
  • Login/Logout, Proteção de rotas e envio de tokens com Angular


👉 Temos uma versão mais atualizada deste artigo no nosso novo Blog

Login/Logout, Proteção de rotas e envio de tokens com Angular

Sumário

  1. Introdução
  2. Primeiros passos
  3. Criando nossos componentes
  4. Tela de login
  5. Protegendo nossas rotas
  6. Enviando o token do usuário nas requisições
  7. Finalizando

Introdução

Fazer login/logout, muitas vezes, parece ser algo muito simples, porém, ao nos depararmos com a necessidade do mesmo, vemos que não é tão simples assim, ainda mais para quem não tem tanta intimidade com storage, route guards e token interceptors. Neste artigo, irei explicar de forma simples e prática, como realizar um login/logout completo e funcional utilizando Angular.

Primeiros passos

Os primeiros passos é entender o que utilizaremos e o porquê utilizaremos:

Storage: Será o responsável por armazenar os dados do usuário logado localmente, para que possamos utilizá-los quando necessário (IdUsuario, Token, Nome, etc). Se possível, utilize criptografia para armazenar esses dados, pois eles serão visíveis.

Route guards ou guarda de rotas: Será o responsável por verificar se o usuário que está acessando determinada rota, está logado ou não, e assim, redirecioná-lo caso seja necessário.

Token interceptor: Será o responsável por interceptar nossas chamadas https que serão realizadas para alguma API, e que a mesma necessite do token do usuário. Exemplo: o endereço base da nossa API é https://localhost:5001/v1. Sempre que houver uma chamada para essa API, nosso token interceptor irá interceptar essa chamada e adicionar no header o token do usuário. Com isso, você não precisará colocar o token em todas as chamadas que irá fazer para essa API.

Bora lá!

Irei partir do princípio que você já saiba criar um projeto em Angular e tenha os conhecimentos prévios no framework. E caso não saiba, a documentação é muito rica, basta consultar.

Com o projeto já criado, irei utilizar a seguinte estrutura:

Estilizando o projeto

Para estilizar minhas páginas, formulários, botões, etc, irei utilizar o Angular Material, porém, você pode utilizar o framework de sua preferência.

Caso queira utilizar o mesmo que eu, basta digitar no seu terminal: ng add @angular/material Em seguida escolher um tema e dar y (YES) para sempre rsrs.

Iremos importar os módulos que vamos utilizar do Material em nosso módulo principal app.module.ts, e aproveitando a viagem, iremos importar os módulos necessários para nossos formulários reativos e nossas chamadas HTTPs, que ficará assim:

Criando nossos componentes

Agora iremos criar três páginas/componentes, que serão: a Home (que o usuário só poderá acessar caso já esteja logado), a Login (que o usuário só poderá acessar caso não esteja logado) e a Principal (que geralmente utilizamos como layout base, com header e footer).

Utilizaremos os seguintes comandos para criar nossos components diretamente na nossa pasta pages:

ng g c pages/login

ng g c pages/home

ng g c pages/compartilhado/principal

Os arquivos .spec.ts poderão ser apagados, pois servem para testar a aplicação, e não iremos usá-los no momento.

Com esses três componentes criados, iremos adicionar suas respectivas rotas no arquivo app-routing-module.ts, que ficará assim, por enquanto:

Perceba que utilizamos uma jogadinha nas rotas, onde a Home e as demais que forem surgir e que também necessitem do layout padrão (header, menu) e login do usuário, serão rotas filhas do component principal.

Porém, para que tudo funcione como desejamos, será necessário realizar as seguintes alterações:

Primeiramente, apague tudo no arquivo app.component.html e deixe apenas o router-outlet, que como bem sabemos, é o responsável por acessar nossas rotas:

Agora precisamos inserir nosso HTML e CSS que será utilizado como layout padrão. Abaixo irei mostrar como ficou o meu, porém, você pode brincar à vontade com o seu, caso não queira fazer igual:

principal.component.html

<p>
  <mat-toolbar color="primary">
    <span>Exemplo login</span>
    <span class="espacamento-toolbar"></span>
    <button mat-icon-button>
      <mat-icon>logout</mat-icon>
    </button>
  </mat-toolbar>
</p>

<mat-card-content>
  <router-outlet></router-outlet>
</mat-card-content>

principal.component.scss

.espacamento-toolbar {
  flex: 1 1 auto;
}

Observe que existe outro router-outlet no componente principal, e será justamente nesse ponto que as rotas filhas do componente principal serão carregadas. Para visualizarmos melhor, agora já podemos testar como está ficando nossa aplicação, utilize o comando ng serve --o

E olha só que bacana o que já temos na nossa rota raíz, o componente principal e seu filho home...

Tela de login

Agora, iremos para nossa página de login. Assim como na tela acima, irei passar rapidamente pelos arquivos e ressaltando apenas os pontos relevantes para este artigo. Sinta-se livre para desenhar a tela como desejar. Mas antes que eu me esqueça, iremos primeiro criar nosso usuario.service.ts que será onde teremos nossos métodos relacionados ao usuário.

Para criar seu service, utilize o comando ng g service services/usuario. E já aproveitando a viagem, iremos criar nossa interface de Usuário, para tiparmos nossos dados. Na pasta interfaces, crie o arquive IUsuario.ts, que ficará assim:

export interface IUsuario{
  id: string,
  email: string;
  senha: string
}

No arquivo usuario.service.ts, já irei criar todos os métodos que irei precisar para logar e deslogar nosso usuário:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
import { IUsuario } from '../interfaces/IUsuario';
const apiUrlUsuario = environment.apiUrl + "Usuario";
@Injectable({
  providedIn: 'root'
})
export class UsuarioService {
constructor(private httpClient: HttpClient,
            private router: Router) { }
  logar(usuario: IUsuario) : Observable<any> {
    /*return this.httpClient.post<any>(apiUrlUsuario + "/login", usuario).pipe(
      tap((resposta) => {
        if(!resposta.sucesso) return;
        localStorage.setItem('token', btoa(JSON.stringify(resposta['token'])));
        localStorage.setItem('usuario', btoa(JSON.stringify(resposta['usuario'])));
        this.router.navigate(['']);
      }));*/
      return this.mockUsuarioLogin(usuario).pipe(tap((resposta) => {
        if(!resposta.sucesso) return;
        localStorage.setItem('token', btoa(JSON.stringify("TokenQueSeriaGeradoPelaAPI")));
        localStorage.setItem('usuario', btoa(JSON.stringify(usuario)));
        this.router.navigate(['']);
      }));
  }
  private mockUsuarioLogin(usuario: IUsuario): Observable<any> {
    var retornoMock: any = [];
    if(usuario.email === "hello@balta.io" && usuario.senha == "123"){
      retornoMock.sucesso = true;
      retornoMock.usuario = usuario;
      retornoMock.token = "TokenQueSeriaGeradoPelaAPI";
      return of(retornoMock);
    }
    retornoMock.sucesso = false;
    retornoMock.usuario = usuario;
    return of(retornoMock);
  }
  deslogar() {
      localStorage.clear();
      this.router.navigate(['login']);
  }
  get obterUsuarioLogado(): IUsuario {
    return localStorage.getItem('usuario')
      ? JSON.parse(atob(localStorage.getItem('usuario')))
      : null;
  }
  get obterIdUsuarioLogado(): string {
    return localStorage.getItem('usuario')
      ? (JSON.parse(atob(localStorage.getItem('usuario'))) as IUsuario).id
      : null;
  }
  get obterTokenUsuario(): string {
    return localStorage.getItem('token')
      ? JSON.parse(atob(localStorage.getItem('token')))
      : null;
  }
  get logado(): boolean {
    return localStorage.getItem('token') ? true : false;
  }
}

Repare no nosso método logar(). Como não temos uma API para autenticar nosso usuário, irei mockar o retorno do método logar, para conseguirmos simular.

logar(usuario: IUsuario) : Observable<any> {
    /*return this.httpClient.post<any>(apiUrlUsuario + "/login", usuario).pipe(
      tap((resposta) => {
        if(!resposta.sucesso) return;
        localStorage.setItem('token', btoa(JSON.stringify(resposta['token'])));
        localStorage.setItem('usuario', btoa(JSON.stringify(resposta['usuario'])));
        this.router.navigate(['']);
      }));*/
      return this.mockUsuarioLogin(usuario).pipe(tap((resposta) => {
        if(!resposta.sucesso) return;
        localStorage.setItem('token', btoa(JSON.stringify("TokenQueSeriaGeradoPelaAPI")));
        localStorage.setItem('usuario', btoa(JSON.stringify(usuario)));
        this.router.navigate(['']);
      }));
  }
  private mockUsuarioLogin(usuario: IUsuario): Observable<any> {
    var retornoMock: any = [];
    if(usuario.email === "hello@balta.io" && usuario.senha == "123"){
      retornoMock.sucesso = true;
      retornoMock.usuario = usuario;
      retornoMock.token = "TokenQueSeriaGeradoPelaAPI";
      return of(retornoMock);
    }
    retornoMock.sucesso = false;
    retornoMock.usuario = usuario;
    return of(retornoMock);
  }

O que está acontecendo aqui é, por parâmetro irá chegar nosso usuário (email e senha) que chegarão do formulário de login (que iremos fazê-lo logo em seguida), no caso do nosso mock, se usuário digitado for hello@balta.io e a senha 123 iremos autenticar esse usuário, salvar o token e suas informações de usuário no storage e redirecioná-lo para nossa página home. Para salvar os dados no storage, utilizei o btoa e atob, que apenas irão passar os dados para base 64, para não ficarem tão visíveis no navegador.

E ressaltando que, a forma que você irá tratar o retorno do seu método logar, irá depender muito de como a API está lhe retornando os dados, porém, em grande parte das vezes, são retornados dessa forma.

Em seguida, precisamos fazer nosso formulário de login e fazê-lo funcionar com nosso usuario.service.

Para validação do formulário, utilizei Reactive Forms, que ficou dessa maneira:

login.component.html

<mat-card-content class="container">
  <mat-card class="card-login">
    <mat-card-title>Login</mat-card-title>
    <form [formGroup]="formLogin" novalidate>
      <mat-card-content class="conteudo-login">
        <mat-form-field appearance="outline" class="form-field-full">
          <mat-label>Email</mat-label>
          <input type="email" matInput formControlName="email" placeholder="Ex. perukas@example.com">
          <mat-error *ngIf="formLogin.get('email').hasError('email') && formLogin.get('email').touched">
            Por favor informe um email válido
          </mat-error>
          <mat-error *ngIf="formLogin.get('email').hasError('required') && formLogin.get('email').touched">
            Email é <strong>obrigatório</strong>
          </mat-error>
        </mat-form-field>
        <mat-form-field appearance="outline" class="form-field-full">
          <mat-label>Senha</mat-label>
          <input type="password" matInput formControlName="senha">
          <mat-error *ngIf="formLogin.get('senha').invalid && formLogin.get('senha').touched">Senha é
            <strong>obrigatória</strong>
          </mat-error>
        </mat-form-field>
      </mat-card-content>
    </form>
    <mat-card-actions align="end">
      <button mat-button color="primary" class="full" align="end" (click)="logar()">Entrar</button>
    </mat-card-actions>
  </mat-card>
</mat-card-content>

login.component.scss

.container{
  display: flex;
  height: 100%;
  justify-content: center;
  align-items: center;
}
.conteudo-login{
  display: flex;
  flex-direction: column;
}
.card-login{
  width: 300px;
  box-shadow: 10px 5px 5px rgba(0, 0, 0, 0.4);
}

login.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { IUsuario } from '../../interfaces/IUsuario';
import { UsuarioService } from '../../services/usuario.service';
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  formLogin: FormGroup;
  constructor(private formBuilder: FormBuilder,
              private usuarioService: UsuarioService,
              private snackBar: MatSnackBar) { }
  ngOnInit(): void {
    this.criarForm();
  }
  criarForm(){
    this.formLogin = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      senha: ['', [Validators.required]]
    });
  }
  logar(){
    if(this.formLogin.invalid) return;
    var usuario = this.formLogin.getRawValue() as IUsuario;
    this.usuarioService.logar(usuario).subscribe((response) => {
        if(!response.sucesso){
          this.snackBar.open('Falha na autenticação', 'Usuário ou senha incorretos.', {
            duration: 3000
          });
        }
    })
  }
}

E olha só que legal o que temos até agora:

Protegendo nossas rotas

Bom, realizada as etapas acima, agora precisamos proteger nossas rotas. Se não, qual seria o sentido de ter um login, se o usuário pode digitar na url "http://localhost:4200/" e acessar sem login, não é mesmo?!

Para proteger nossas rotas, iremos criar dois guardas de rotas. Um será para verificar se o usuário já está logado e direcioná-lo para nossa home, e o outro será para verificar se ele não está logado e direcioná-lo para nosso login.

Para criar os dois guardas de rotas, utilize os seguintes comandos:

Os dois serão CanActivate

ng g guard services/guards/usuario-nao-autenticado

ng g guard services/guards/usuario-autenticado

E os nossos arquivos ficarão assim:

usuario-autenticado.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { UsuarioService } from '../usuario.service';
@Injectable({
  providedIn: 'root'
})
export class UsuarioAutenticadoGuard implements CanActivate{
    constructor(
      private usuarioService: UsuarioService,
      private router: Router) { }
    canActivate(){
      if (this.usuarioService.logado) {
        return true;
      }
      this.router.navigate(['login']);
      return false;
    }
}

usuario-nao-autenticado.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { UsuarioService } from '../usuario.service';
@Injectable({
  providedIn: 'root'
})
export class UsuarioNaoAutenticadoGuard implements CanActivate{
    constructor(
      private usuarioService: UsuarioService,
      private router: Router) { }
    canActivate(){
      if (this.usuarioService.logado) {
        this.router.navigate(['']);
        return false;
      }
      return true;
    }
}

Agora, basta colocar nossos guardas de rotas no arquivo app-routing.module.ts, que ficará assim as rotas:

const routes: Routes = [
  { path: 'login', component: LoginComponent, canActivate: [UsuarioNaoAutenticadoGuard]},
  {
    path: '', component: PrincipalComponent, canActivate: [UsuarioAutenticadoGuard],
    children: [
      { path: '', component: HomeComponent }
    ],
  },
];

Para conseguirmos realizar o teste completo, precisamos adicionar a função de logout no nosso menu principal. Irei adicionar o método deslogar no evento click do meu botão logout:

principal.component.html

<button mat-icon-button (click)="deslogar()">
	<mat-icon>logout</mat-icon>
</button>

E no arquivo principal.component.ts, basicamente, irei injetar meu usuarioService e chamar o método deslogar(), que irá limpar o storage e redirecionar o usuário para o login:

import { Component, OnInit } from '@angular/core';
import { UsuarioService } from '../../../services/usuario.service';
@Component({
  selector: 'app-principal',
  templateUrl: './principal.component.html',
  styleUrls: ['./principal.component.scss']
})
export class PrincipalComponent implements OnInit {
  constructor(private usuarioService: UsuarioService) { }
  ngOnInit(): void {
  }
  deslogar(){
    this.usuarioService.deslogar();
  }
}

Para testar tudo isso, você pode tentar acessar a rota raíz sem estar logado e/ou tentar acessar a rota login estando logado.

Feito isso, temos um login totalmente funcional!!!!!!! \õ/

Enviando o token do usuário nas requisições

Como já sabemos, o Angular interage muito bem com comunicações via API, porém, sempre que nos comunicamos via API, precisamos enviar o token do usuário, que comprova que o usuário já está autenticado em determinada API e pode realizar as requisições de acordo com suas permissões. Para isso, iremos utilizar os interceptors. Utilize o comando ng g interceptor services/interceptors/token para gerá-lo. Nosso token.interceptor.ts, ficará assim:

token.interceptor.ts

import { environment } from 'src/environments/environment';
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { UsuarioService } from '../usuario.service';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs/internal/observable/throwError';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
    constructor(private usuarioService : UsuarioService) {}
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const token = this.usuarioService.obterTokenUsuario;
        const requestUrl: Array<any> = request.url.split('/');
        const apiUrl: Array<any> = environment.apiUrl.split('/');
        
        if (token && requestUrl[2] === apiUrl[2]) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${token}`,
                    token: `${token}`
                }
            });
            return next.handle(request).pipe(catchError(error => {
                if (error instanceof HttpErrorResponse && error.status === 401)
                  this.usuarioService.deslogar(false);
                else
                  return throwError(error.message);
            }));
        }
        else {
            return next.handle(request);
        }
    }
}

A grosso modo, o que está acontecendo no código acima é:

  1. Estamos obtendo o token do usuário que está no nosso storage;

  2. Estamos obtendo a requisição que está sendo executada;

  3. Estamos obtendo a apiUrl que está em nosso environment;

  4. É verificado se a requisição que está sendo realizada, é de fato para a API que temos em nosso environment;

  5. Caso for, adicionamos o token no header da requisição;

  6. Caso não for, prosseguimos a requisição normal;

  7. Ao terminar a requisição, verificamos se houve algum erro.

  8. Caso ocorra o erro 401 Unauthorized no retorno da requisição, ou seja, nosso usuário não está mais autenticado na API, iremos deslogar o usuário. Afinal, sem o token não conseguiremos mais fazer requisições para as rotas que necessitam de autenticação.

Para fechar nosso token-interceptor por completo, precisamos adicioná-lo nos providers no app.module.ts, que ficará assim:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './pages/login/login.component';
import { HomeComponent } from './pages/home/home.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { PrincipalComponent } from './pages/compartilhado/principal/principal.component';
import { MatCardModule } from '@angular/material/card';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule } from '@angular/material/icon';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { TokenInterceptor } from './services/interceptors/token.interceptor';
@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    HomeComponent,
    PrincipalComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    HttpClientModule,
    ReactiveFormsModule,
    FormsModule,
    MatCardModule,
    MatInputModule,
    MatButtonModule,
    MatSidenavModule,
    MatToolbarModule,
    MatIconModule,
    MatSnackBarModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Finalizando

Bom, por ora é isso pessoal, temos um login totalmente funcional de ponta a ponta!

Espero que com esse artigo, eu tenha contribuído com suas maiores dúvidas em relação ao tema e até a próxima. Muito obrigado pela leitura até aqui!

Código disponibilizado no GitHub → https://github.com/GustavoRodrigues94/AngularLoginExemplo

Populares

Priority Queue

Priority Queue ou fila prioritária é um tipo de lista inclusa no C# 10 que permite que seus itens...


Implicit Operators no C#

Implicit Operators permitem adicionar comportamentos de conversão a objetos Built In ou complexos...


ASP.NET 5 – Autenticação e Autorização com Bearer e JWT

Este artigo atualmente utiliza a versão 5.0.0-rc.1 do ASP.NET/.NET, o que significa que ainda não...


Clean Code - Guia e Exemplos

Saiba como manter seu código limpo (Clean Code) seguindo algumas práticas sugeridas pelo Robert C...


Git e GitHub - Instalação, Configuração e Primeiros Passos

Git é um sistema de controle de versões distribuídas, enquanto GitHub é uma plataforma que tem o ...


Compartilhe este artigo



Conheça o autor

Gustavo Rodrigues da Silveira

Gustavo Rodrigues da Silveira

Desenvolvedor Fullstack

Desenvolvedor fullstack, com conhecimento/experiência em Angular e C#.NET. Com foco sempre na melhor experiência/solução para o usuário. Sempre levando em consideração as boas práticas (SOLID & Clean Code & Unit Test)





3.156

Aulas disponíveis

294

horas de conteúdo

76.953

Alunos matriculados

53.576

Certificados emitidos





Comece de graça agora mesmo!

Temos mais de 21 cursos totalmente de graça e todos com certificado de conclusão.

Começar


Prefere algo mais Premium?

Conheça nossos planos



Premium anual

Compra única, parcelada em até
12x no cartão de crédito


12x R$

99

,79

=R$ 1.197,44
  • 1 ano de acesso
  • Acesso à todo conteúdo
  • Emissão de Certificado
  • Tira Dúvidas Online
  • 68 cursos disponíveis
  • 10 carreiras disponíveis
  • 161 temas de tecnologia
  • Conteúdo novo todo mês
  • Encontros Premium

Começar agora

Política de privacidade



Precisa de ajuda?

Dúvidas frequentes



  • Posso começar de graça?

    Sim! Basta criar sua conta gratuita no balta.io e começar seus estudos. Nós contamos com diversos cursos TOTALMENTE gratuitos e com certificado de conclusão.

  • Vou ter que pagar algo?

    Nós temos cursos gratuitos e pagos, porém você não precisa informar nenhum dado de pagamento para começar seus estudos gratuitamente conosco. Os cursos gratuitos são completos e com certificado de conclusão, você não paga nada por eles.

    Porém, caso queira algo mais Premium , você terá acesso à diversos benefícios que vão te ajudar ainda mais em sua carreira.

  • Por onde devo começar?

    Siga SEMPRE as nossas Carreiras , elas vão te orientar em todos os sentidos. Os cursos já estão organizados em categorias e carreiras para facilitar seu aprendizado.
    Nossa sugestão para aprendizado é começar pelo Backend e seguindo para Frontend e Mobile.

    • Backend
    • Frontend
    • Mobile

  • Os cursos ensinam tudo que preciso?

    Nenhum curso no mundo vai te ensinar tudo, desculpa ser sincero! Os cursos são uma base, eles fornecem por volta de 30% do que você precisa aprender, o resto é com você, com dedicação e MUITA prática.

  • O que eu devo estudar?

    Java ou .NET? Angular ou React? Xamarin ou Flutter? A resposta é simples e direta: "Você já sabe o básico?"

    Se você ainda não sabe BEM o básico, ou seja, os fundamentos, OOP, SOLID, Clean Code, está perdendo tempo estudando Frameworks ou até coisas mais avançadas como Docker. Foque nos seus objetivos primeiro.
    Agora se você está indeciso sobre qual Framework estudar, a boa notícia é que o mercado neste momento está bem aquecido e você tem várias oportunidade. Desta forma o que levaríamos em conta para tomar esta decisão seria:

    • Já sei o básico
    • O Framework/Tecnologia tem mercado onde eu estou (região)
    • O Framework/Tecnologia é utilizado em uma empresa onde quero atual
    • O Framework/Tecnologia resolve meu problema
    • Eu gosto de utilizar o Framework/Tecnologia

  • Estou pronto para estudar no balta.io?

    Com certeza! O primeiro passo é começar e você pode fazer isto agora mesmo!

    Começar de graça

Ainda tem dúvidas?





Assine nosso Newsletter

Receba em primeira mão todas as nossas novidades.

Cadastrar


balta.io

Sobre

  • Como funciona?
  • Seja Premium
  • Agenda
  • Blog
  • Todos os cursos

Cursos

  • Frontend
  • Backend
  • Mobile
  • Fullstack

Suporte

  • Termos de uso
  • Privacidade
  • Cancelamento
  • Central de ajuda

Redes Sociais

  • Telegram
  • Facebook
  • Instagram
  • YouTube
  • Twitch
  • LinkedIn
  • Discord