Update User Account callable function example
The following is a walkthrough of creating the Update User Account function example
Example change request
Overview
Users need to be able to update their accounts with a Github URL.
Acceptance criteria
Login
as any userNavigate
to the user account view/account
Observe
view includes the following:Update Github URL: <input>
Button to update the user's Github URL
Technical requirements
Account
template updates to include new designAccount
component update to interact with the stateAccount
state management updated to interact with APIonUpdateAccount
HTTPS callable function to update the/users/{{uid}}
document with new Github URL property
Type of function
For this update, we will be creating an HTTPS callable function that allows users to update their account information directly from the application.
Additional types of functions can be reviewed here: Cloud Functions
Create onUpdateAccount function
In the apps\full-stack-typescript-api\src\functions
folder, create the following:
functions
- account
- index.ts
Update
apps\full-stack-typescript-api\src\functions\account\index.ts
to be:
import { User } from '@full-stack-typescript/models';
import * as admin from 'firebase-admin';
const {
firestoreInstance,
// eslint-disable-next-line @typescript-eslint/no-var-requires
} = require('../../utils/admin');
export async function onUpdateAccount(data: User, context) {
const batch = firestoreInstance.batch(); // Get a new write batch
const serverTimestamp = admin.firestore.Timestamp.now();
const uid = context.auth.uid;
const userDoc = firestoreInstance.collection('users').doc(uid); // Get paths
batch.update(userDoc, {
...data,
updatedAt: serverTimestamp
}, { merge: true });
return batch.commit();
};
The new function receives a data
payload of type User
and merges the new data to the users document in our database.
Update
apps\full-stack-typescript-api\src\main.ts
to include the new function.
import * as authFunctions from './functions/auth';
...
// Account
export const onUpdateAccount = functions.https.onCall((data, context) => {
return accountFunctions.onUpdateAccount(data, context);
});
The main.ts
file is our primary entry point for our functions and needs to be updated so that our new function is included when the project is built.
Build the functions
Run the following command to build the functions application:
nx build full-stack-typescript-api
Deploy functions
Run the following command to deploy new and updated functions:
firebase deploy --only functions
Update the frontend application for new API interaction
Now that we've created a secure method for interacting with the database, we need to update the frontend application to integrate with the new function.
Update Account state management
Update actions
Update
libs\features\account\src\lib\+state\account.actions.ts
to include the new actions:
updateAccount
updateAccountFailure
updateAccountSuccess
...
export const updateAccount = createAction(
'[Account] Update account',
props<{ account: User }>()
);
export const updateAccountFailure = createAction(
'[Account] Update account Failure',
props<{ error: any }>()
);
export const updateAccountSuccess = createAction(
'[Account] Update account Success'
);
Update reducer
Update
libs\features\account\src\lib\+state\account.reducer.ts
to include:
const accountReducer = createReducer(
initialState,
...
on(accountActions.loadAccountFailure, accountActions.updateAccountFailure, (state, { error }) => ({
...state,
error,
isLoaded: false,
isLoading: false
})),
...
on(accountActions.updateAccount, (state) => ({
...state,
isLoading: true
})),
on(accountActions.updateAccountSuccess, (state) => ({
...state,
error: null,
isLoaded: false
}))
);
Update facade
Update
libs\features\account\src\lib\+state\account.facade.ts
to include:
updateAccount(account: User) {
this.store.dispatch(updateAccount({ account }));
}
Update effects
Update
libs\features\account\src\lib\+state\account.effects.ts
to include:
updateAccount$ = createEffect(() =>
this.actions$.pipe(
ofType(accountActions.updateAccount),
exhaustMap(({ account }) =>
this.accountService.updateAccount(account).pipe(
map(() => {
return accountActions.updateAccountSuccess();
}),
catchError((error) => of(accountActions.updateAccountFailure({ error })))
)
)
)
);
Update AccountService
Update
libs\services\account\src\lib\account.service.ts
to include:
import { AngularFireFunctions } from '@angular/fire/compat/functions';
constructor(private fns: AngularFireFunctions, afs: AngularFirestore) {
super(afs);
}
updateAccount(account: User): Observable<User> {
const callable = this.fns.httpsCallable('onUpdateAccount');
return callable(account) as Observable<User>;
}
Update AccountModule for forms integration
Update
libs\features\account\src\lib\account.module.ts
to include the FormsModule
:
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
...
FormsModule,
...
],
})
export class AccountModule {}
Update AccountComponent class
Update
libs\features\account\src\lib\account.component.ts
to include:
githubURL = '';
onUpdateAccount() {
this.accountFacade.updateAccount({ githubURL: this.githubURL });
}
Update AccountComponent template
Update
libs\features\account\src\lib\account.component.html
to include:
<div>
Update Github URL: <input type="text" [(ngModel)]="githubURL">
<button (click)="onUpdateAccount()">Update Account</button>
</div>
Further improvements
There is now a secure way for users to update their account information with the database. The onUpdateAccount
function and Account
state accepts a User
payload which can include all of the attributes of User
.
The AccountComponent
can be improved to enable updating of all User
properties.
Last updated