The ProfileService provides methods for managing user profiles in Supabase, including profile data CRUD operations and avatar image management.
Import
import { ProfileService } from '@jet/services/profile/profile.service';
import { Profile } from '@jet/interfaces/profile.interface';
Usage
Fetch User Profile
import { inject, OnInit } from '@angular/core';
import { ProfileService } from '@jet/services/profile/profile.service';
import { Profile } from '@jet/interfaces/profile.interface';
export class ProfileComponent implements OnInit {
readonly #profileService = inject(ProfileService);
profile: Profile | null = null;
async ngOnInit() {
try {
const { data } = await this.#profileService.selectProfile();
this.profile = data;
} catch (error) {
console.error('Failed to load profile:', error);
}
}
}
Update Profile
async updateProfile(username: string, fullName: string) {
try {
const { data } = await this.#profileService.updateAndSelectProfile({
username,
full_name: fullName
});
console.log('Profile updated:', data);
} catch (error) {
console.error('Failed to update profile:', error);
}
}
Upload Avatar
async onFileSelected(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
const { data, error } = await this.#profileService.uploadAvatar(file);
if (error) {
console.error('Upload failed:', error);
return;
}
// Get public URL for the uploaded avatar
const publicUrl = this.#profileService.getAvatarPublicUrl(data.path);
// Update profile with new avatar URL
await this.#profileService.updateAndSelectProfile({
avatar_url: publicUrl
});
}
Delete Avatar
async removeAvatar() {
if (!this.profile?.avatar_url) return;
const { error } = await this.#profileService.deleteAvatar(
this.profile.avatar_url
);
if (error) {
console.error('Failed to delete avatar:', error);
return;
}
// Clear avatar URL in profile
await this.#profileService.updateAndSelectProfile({
avatar_url: null
});
}
Methods
selectProfile
Fetches the current user’s profile from Supabase.
return
Promise<PostgrestSingleResponse<Profile>>
Promise that resolves with the user’s profile data or throws an error
Source: /home/daytona/workspace/source/src/app/services/profile/profile.service.ts:42
public selectProfile() {
return this.#supabaseClient
.from(SupabaseTable.Profiles)
.select()
.eq('user_id', this.#user()?.id)
.single()
.throwOnError();
}
Example:
const { data, error } = await this.profileService.selectProfile();
if (data) {
console.log('Username:', data.username);
console.log('Full name:', data.full_name);
console.log('Avatar URL:', data.avatar_url);
}
updateAndSelectProfile
Updates the current user’s profile and returns the updated data.
Object containing the profile fields to updatepartialProfile.avatar_url
URL to user’s avatar image
return
Promise<PostgrestSingleResponse<Profile>>
Promise that resolves with the updated profile data or throws an error
Source: /home/daytona/workspace/source/src/app/services/profile/profile.service.ts:51
public updateAndSelectProfile(partialProfile: Partial<Profile>) {
return this.#supabaseClient
.from(SupabaseTable.Profiles)
.update(partialProfile)
.eq('user_id', this.#user()?.id)
.select()
.single()
.throwOnError();
}
Example:
// Update username
const { data } = await this.profileService.updateAndSelectProfile({
username: 'newusername'
});
// Update multiple fields
const { data } = await this.profileService.updateAndSelectProfile({
username: 'john_doe',
full_name: 'John Doe',
avatar_url: 'https://example.com/avatar.jpg'
});
uploadAvatar
Uploads an avatar image file to Supabase Storage.
return
Promise<FileUploadResponse>
Promise that resolves with upload data including the file path, or an errordata
{ fullPath: string; id: string; path: string } | null
Upload metadata including the storage path
Error object if upload failed
Source: /home/daytona/workspace/source/src/app/services/profile/profile.service.ts:61
public uploadAvatar(file: File): Promise<...> {
const fileExtension = file.name.split('.').pop();
const timestamp = Date.now();
const path = `${this.#userService.user()?.id}/avatar-${timestamp}.${fileExtension}`;
return this.#supabaseClient.storage
.from(SupabaseStorage.ProfileAvatars)
.upload(path, file);
}
Path format: {user_id}/avatar-{timestamp}.{extension}
Example:
const file = event.target.files[0];
const { data, error } = await this.profileService.uploadAvatar(file);
if (data) {
console.log('File uploaded to:', data.path);
console.log('Full path:', data.fullPath);
// Get public URL
const url = this.profileService.getAvatarPublicUrl(data.path);
}
getAvatarPublicUrl
Generates a public URL for an avatar stored in Supabase Storage.
The storage path of the avatar file
The public URL to access the avatar image
Source: /home/daytona/workspace/source/src/app/services/profile/profile.service.ts:34
public getAvatarPublicUrl(path: string): string {
const { data } = this.#supabaseClient.storage
.from(SupabaseStorage.ProfileAvatars)
.getPublicUrl(path);
return data.publicUrl;
}
Example:
const path = 'user123/avatar-1234567890.jpg';
const publicUrl = this.profileService.getAvatarPublicUrl(path);
// Returns: 'https://...supabase.co/storage/v1/object/public/profile-avatars/user123/avatar-1234567890.jpg'
deleteAvatar
Deletes an avatar file from Supabase Storage.
The public URL of the avatar to delete
return
Promise<FileDeleteResponse>
Promise that resolves with deletion result or errorArray of deleted file objects
Error object if deletion failed
Source: /home/daytona/workspace/source/src/app/services/profile/profile.service.ts:25
public deleteAvatar(publicUrl: string): Promise<...> {
const fileName = publicUrl.split('/').pop();
const path = `${this.#userService.user()?.id}/${fileName}`;
return this.#supabaseClient.storage
.from(SupabaseStorage.ProfileAvatars)
.remove([path]);
}
Example:
const avatarUrl = 'https://.../avatar-123.jpg';
const { data, error } = await this.profileService.deleteAvatar(avatarUrl);
if (error) {
console.error('Failed to delete:', error);
} else {
console.log('Deleted files:', data);
}
Type Definitions
Profile Interface
export interface Profile {
avatar_url: null | string;
created_at: string;
full_name: null | string;
updated_at: null | string;
user_id: User['id'];
username: string;
}
Source: /home/daytona/workspace/source/src/app/interfaces/profile.interface.ts:3
Configuration
Supabase Setup
The service requires Supabase client injection:
import { SUPABASE_CLIENT } from '@jet/injection-tokens/supabase-client.injection-token';
import { createClient } from '@supabase/supabase-js';
const supabaseClient = createClient(
environment.supabaseUrl,
environment.supabaseAnonKey
);
export const appConfig: ApplicationConfig = {
providers: [
{ provide: SUPABASE_CLIENT, useValue: supabaseClient },
ProfileService // Must be explicitly provided
]
};
Storage Bucket
The service uses the profile-avatars storage bucket:
enum SupabaseStorage {
ProfileAvatars = 'profile-avatars'
}
Ensure this bucket exists in your Supabase project with appropriate permissions.
Best Practices
- Handle errors - Always wrap service calls in try/catch blocks
- Validate files - Check file type and size before uploading
- Clean up old avatars - Delete previous avatar when uploading a new one
- Use loading states - Show spinners during async operations
- Update UI reactively - Update profile display after successful operations
Common Patterns
Complete Avatar Update Flow
async updateAvatar(file: File) {
try {
// Delete old avatar if exists
if (this.currentProfile?.avatar_url) {
await this.profileService.deleteAvatar(this.currentProfile.avatar_url);
}
// Upload new avatar
const { data, error } = await this.profileService.uploadAvatar(file);
if (error) throw error;
// Get public URL
const publicUrl = this.profileService.getAvatarPublicUrl(data.path);
// Update profile
const result = await this.profileService.updateAndSelectProfile({
avatar_url: publicUrl
});
this.currentProfile = result.data;
this.alertService.showAlert('Avatar updated successfully');
} catch (error) {
this.alertService.showErrorAlert('Failed to update avatar');
}
}
export class ProfileFormComponent implements OnInit {
readonly #profileService = inject(ProfileService);
readonly #alertService = inject(AlertService);
profileForm = new FormGroup({
username: new FormControl('', Validators.required),
fullName: new FormControl('')
});
async ngOnInit() {
const { data } = await this.#profileService.selectProfile();
if (data) {
this.profileForm.patchValue({
username: data.username,
fullName: data.full_name
});
}
}
async onSubmit() {
if (!this.profileForm.valid) return;
try {
await this.#profileService.updateAndSelectProfile({
username: this.profileForm.value.username!,
full_name: this.profileForm.value.fullName
});
this.#alertService.showAlert('Profile saved');
} catch (error) {
this.#alertService.showErrorAlert('Failed to save profile');
}
}
}
Dependencies
The ProfileService depends on:
- SUPABASE_CLIENT - Supabase client instance
- LoggerService - For service initialization logging
- UserService - For current user information
This service is not provided at the root level. You must provide it in your component or module providers.