In this tutorial we are going to build a simple login using Angular for the frontend application and FastAPI for the backend application.
Angular & FastAPI
To start things off, let’s first create our Angular app for the frontend. After we have finish creating the necessary thing in our frontend app, then only will we move to our backend.
Run below command. Make sure you have Node js installed.
npm install -g @angular/cli
ng new my-app
cd my-app
npm start
With that, you will have you frontend application now running. But its the default application. We will have to create our own login component and services.

Open your project folder in the editor of your choice. I will be using Visual Studio.

To make things simple, we would not be creating the layout component and other stuff, instead we only have to create two components, one for Login and another one for Profile, for when we have logged in.
In your command prompt, make sure you cd
into your app
folder.
cd src/app
ng g c main/login
ng g c main/profile
You now have 2 newly created components in app/main
folder with both in their respective folder.
Open you app-routing.module.ts
and add the following.
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LoginComponent } from './main/login/login.component'; import { ProfileComponent } from './main/profile/profile.component'; const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: 'profile', component: ProfileComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
You should have something like this.

Go to your browser and try to go each route, /login
and /profile
.
At this point, both routes are accessible, but what we want is to have the profile component to only be accessible if the user is logged in.
Angular AuthGuard
To modify a page/route/component accessibility we can use AuthGuard.
First, let’s create a service for our AuthGuard class. Run the following.
ng g service services/auth-guard
And with that, you should have something like this.

Now, open up your AuthGuard and paste the following.
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, Route } from '@angular/router'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root' }) export class AuthGuardService { constructor( private _authService: AuthService, private _router: Router ) { } canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { if (this._authService.getToken()) { return true; } // navigate to login page this._router.navigate(['/login']); // you can save redirect url so after authing we can move them back to the page they requested return false; } }
You probably have already noticed that we do not have the AuthService class yet. Let’s go ahead and create that first.
ng g service services/auth
Open up the auth-service.ts
and paste the following.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() {
}
getUserDetails() {
return localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : null;
}
setDataInLocalStorage(variableName, data) {
localStorage.setItem(variableName, data);
}
getToken() {
return localStorage.getItem('token');
}
clearStorage() {
localStorage.clear();
}
}
Now, go back to your app-routing.module.ts
and paste the following.
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LoginComponent } from './main/login/login.component'; import { ProfileComponent } from './main/profile/profile.component'; import { AuthGuardService } from './services/auth-guard.service'; const routes: Routes = [ { path: '', redirectTo: 'login', pathMatch: 'full' }, { path: 'login', component: LoginComponent }, { path: 'profile', component: ProfileComponent, canActivate:[AuthGuardService] }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Go to your browser and test out both routes, login
and profile
.
You will notice that upon trying to go to profile
, you will get redirect to login
page. If you still can go to profile
, make sure there is no existing token in your browser local storage which you have set before.
Explanation
What we did in the above was creating two separate services, AuthGuard
and Auth
. The AuthGuard
service is used to guard the route. How it works is that whenever a User tries to go to a route, the AuthGuard checks whether if the User has a token set in his/her browser.
If you open the Auth
service, you can see we have 4 different methods that deal with the localStorage
.
You might be asking when does the token get set then? Well, the token will get set after a successful login.
During a login, we will send the User’s username and password to the backend to check with the database, if it exists, we will send back a token to the frontend and the token will be set into the localStorage
.
Not only that, after successful login, we can include the token in the request header every time we make an API call to a guarded endpoint.
Sounds cool? Lets code it.
Login Page
Let’s clean up our site looks a bit. Go to app.component.html
and delete everything. Replace it with below.
<router-outlet></router-outlet>
I will not be styling the page though, I will leave that to you. Go crazy with your page’s look and style.
You should have something like this.

Open your login.component.html
and paste the following.
<div>
<form [formGroup] = 'form' (ngSubmit) = 'login()'>
<label>Username</label>
<input type = 'text' formControlName = 'username' />
<label>Password</label>
<input type = 'password' formControlName = 'password' />
<div>
<button type ='submit' [disabled] = '!this.form.valid'>
Login
</button>
</div>
</form>
</div>
Open your login.component.ts
and paste the following.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
form: FormGroup
constructor(
public fb: FormBuilder
) { }
ngOnInit(): void {
this.form = this.fb.group({
username: ['', Validators.required],
password:['', Validators.required]
});
}
login(){
let b = this.form.value
console.log(b)
}
}
Make sure you import the ReactiveFormsModule
and FormsModule
in your app.module.ts
or else you will get errors.
Test out your login now and upon submit you should a log in your console.
Now that the login box is working, the next part is making the API call to our backend.
Angular FastAPI : API Service
We will need to create 2 new services, one to make API calls and another one as an interceptor to modify our request header.
Run the following command.
ng g service services/api
ng g service services/interceptor-service
Open your api.service
and paste the following.
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ApiService {
private REST_API_SERVER = "http://localhost:8000/";
constructor(private httpClient: HttpClient) { }
getTypeRequest(url) {
return this.httpClient.get(this.REST_API_SERVER+url).pipe(map(res => {
return res;
}));
}
postTypeRequest(url, payload) {
return this.httpClient.post(this.REST_API_SERVER+url, payload).pipe(map(res => {
return res;
}));
}
putTypeRequest(url, payload) {
return this.httpClient.put(this.REST_API_SERVER+url, payload).pipe(map(res => {
return res;
}))
}
}
Notice that the REST_API_SERVER
value is localhost:8000/
. This value corresponds to our backend api.
The above code basically shows that we have 3 functions for each type of request we are going to make, the GET
, POST
and PUT
.
Now open your interceptor-service
and paste the following.
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpRequest, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable, Subject, of, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';
import { tap, catchError } from 'rxjs/operators';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class InterceptorService {
constructor(
private router: Router,
private _auth: AuthService
) {
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!request.headers.has('Content-Type')) {
request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
}
request = request.clone({ headers: request.headers.set('Accept', 'application/json') }).clone({
setHeaders: {
Authorization: `Bearer ${this._auth.getToken()}`
}
});
return next.handle(request)
}
}
Open your app.module.ts
and paste the following.
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 './main/login/login.component';
import { ProfileComponent } from './main/profile/profile.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { InterceptorService } from './services/interceptor-service.service'
@NgModule({
declarations: [
AppComponent,
LoginComponent,
ProfileComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: InterceptorService,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Make sure your components and the interceptor service are imported as well as the ReactiveFormsModule, FormsModule
Open your login.component.html
and paste the following.
<div>
<form [formGroup] = 'form' (ngSubmit) = 'login()'>
<label>Username</label>
<input type = 'text' formControlName = 'username' />
<label>Password</label>
<input type = 'password' formControlName = 'password' />
<div>
<button type ='submit' [disabled] = '!this.form.valid'>
Login
</button>
</div>
</form>
</div>
Now open your login.component.ts
and paste the following.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/services/auth.service';
import {ApiService} from '../../services/api.service'
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
form: FormGroup
constructor(
private _api : ApiService,
private _auth: AuthService,
private router: Router,
public fb: FormBuilder
) { }
ngOnInit(): void {
this.form = this.fb.group({
username: ['', Validators.required],
password:['', Validators.required]
});
}
login(){
let b = this.form.value
console.log(b)
this._api.postTypeRequest('login', b).subscribe((res: any) => {
console.log(res)
if(res.access_token){
this._auth.setDataInLocalStorage('token', res.access_token)
this.router.navigate(['profile'])
}
}, err => {
console.log(err)
});
}
}
You should have something like this.

For your profile.component.html
, paste the following.
<p>Login Successful!</p>
For your profile.component.ts
, paste the following.
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/services/auth.service';
import {ApiService} from '../../services/api.service'
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.scss']
})
export class ProfileComponent implements OnInit {
constructor(
private _api : ApiService,
private _auth: AuthService,
) { }
ngOnInit(): void {
this.test_jwt()
}
test_jwt(){
this._api.getTypeRequest('test-jwt').subscribe((res: any) => {
console.log(res)
}, err => {
console.log(err)
});
}
}
Upon successful login, you will be redirected to the profile page and the test_jwt()
will automatically run to show you that the JWT is working and you can access the JWT protected endpoint.
It’s time to build our backend.
Angular FastAPI Backend
Let’s create our backend now. Make sure you have Python installed, I am using Anaconda but it works the same.
mkdir backend
cd backend
python -m venv C:\backend\venv
venv\Scripts\activate
pip install fastapi
pip install uvicorn
This will create a virtual env for your backend and also install FastAPI and uvicorrn.
Create a new file name main.py
. Open it and paste the following.
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World, Rasyue"}
To run your backend application, run the following.
uvicorn main:app --reload
Go to localhost:8000
to test it out.

FastAPI Login Endpoint and JWT
First thing first, install the jwt.
pip install fastapi-jwt-auth
Open your main.py
and paste the following.
from typing import Optional from fastapi import FastAPI, HTTPException, Depends, Request from fastapi.responses import JSONResponse from fastapi_jwt_auth import AuthJWT from fastapi_jwt_auth.exceptions import AuthJWTException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel app = FastAPI() origins = [ "http://localhost", "http://localhost:4200", ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class User(BaseModel): username: str password: str class Settings(BaseModel): authjwt_secret_key: str = "my_jwt_secret" @AuthJWT.load_config def get_config(): return Settings() @app.exception_handler(AuthJWTException) def authjwt_exception_handler(request: Request, exc: AuthJWTException): return JSONResponse( status_code=exc.status_code, content={"detail": exc.message} ) @app.get("/") def read_root(): return {"Hello": "World Rasyue"} @app.post('/login') def login(user: User, Authorize: AuthJWT = Depends()): #user.username #user.password # this is the part where we will check the user credentials with our database record #but since we are not going to use any db, straight away we will just create the token and send it back # subject identifier for who this token is for example id or username from database access_token = Authorize.create_access_token(subject=user.username) return {"access_token": access_token} @app.get('/test-jwt') def user(Authorize: AuthJWT = Depends()): Authorize.jwt_required() return {"user": 123124124, 'data': 'jwt test works'} #current_user = Authorize.get_jwt_subject() #return {"user": current_user, 'data': 'jwt test works'}
And that is it for our backend. Remember to run with the command uvicorn main:app --reload
Now go back to your frontend and test the login system.
The End..
With that, we have successfully create a login system for our app using Angular for the frontend and FastAPI for the backend.
Also, if you are the type that learn better from watching videos, try this React and FastAPI Login
Stay tuned for more.