Skip to content

Commit 24ba1ea

Browse files
Refactor Supabase types and enhance API URL building for improved query handling
1 parent a1caa66 commit 24ba1ea

File tree

10 files changed

+419
-42
lines changed

10 files changed

+419
-42
lines changed

src/app/admin/_services/admin-api.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { inject, Injectable } from '@angular/core';
22
import { map, Observable } from 'rxjs';
3-
import { Post, PostInsert, PostUpdate, Tag } from '../../supabase-types';
43
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
54
import { SupabaseService } from '../../services/supabase.service';
65
import { environment } from '../../../environments/environment';
6+
import { Post, PostInsert, PostUpdate, Tag } from '../../types/supabase';
77

88
@Injectable()
99
export class AdminApiService {
@@ -111,7 +111,7 @@ export class AdminApiService {
111111
const existingTagIds = (existingPostTags || [])
112112
.map((pt) => pt.tag_id)
113113
.sort();
114-
const newTagIds = tags.map((tag) => tag.id).sort();
114+
const newTagIds = tags.map((tag: Tag) => tag.id).sort();
115115

116116
// Check if tags have actually changed using JSON comparison for better accuracy
117117
const tagsChanged =

src/app/app.config.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
2-
APP_INITIALIZER,
32
ApplicationConfig,
43
provideZoneChangeDetection,
4+
inject,
5+
provideAppInitializer,
56
} from '@angular/core';
67
import { provideRouter, withComponentInputBinding } from '@angular/router';
78

@@ -24,13 +25,11 @@ export const appConfig: ApplicationConfig = {
2425
provideZoneChangeDetection({ eventCoalescing: true }),
2526
provideRouter(routes, withComponentInputBinding()),
2627
provideClientHydration(withEventReplay()),
28+
provideAppInitializer(() => {
29+
const supabaseService = inject(SupabaseService);
30+
return supabaseInitializer(supabaseService)();
31+
}),
2732

28-
{
29-
provide: APP_INITIALIZER,
30-
useFactory: supabaseInitializer,
31-
deps: [SupabaseService],
32-
multi: true,
33-
},
3433
provideHighlightOptions({
3534
coreLibraryLoader: () => import('highlight.js/lib/core'),
3635
languages: {

src/app/auth/_guards/authAdminGuard.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export const authAdminGuard: CanMatchFn = (): boolean => {
1414
return false;
1515
}
1616
};
17+

src/app/helpers/api-url-builder.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { ColumnName, RowOf, TableName } from '../types/supabase-helper';
2+
3+
//https://postgrest.org/en/stable/references/api/tables_views.html#operators
4+
type Op =
5+
| 'eq'
6+
| 'gt'
7+
| 'gte'
8+
| 'lt'
9+
| 'lte'
10+
| 'neq'
11+
| 'like'
12+
| 'ilike'
13+
| 'match'
14+
| 'imatch'
15+
| 'in'
16+
| 'is'
17+
| 'isdistinct'
18+
| 'fts'
19+
| 'plfts'
20+
| 'phfts'
21+
| 'wfts'
22+
| 'cs'
23+
| 'cd'
24+
| 'ov'
25+
| 'sl'
26+
| 'sr'
27+
| 'nxr'
28+
| 'nxl'
29+
| 'adj'
30+
| 'not'
31+
| 'or'
32+
| 'and'
33+
| 'all'
34+
| 'any';
35+
36+
export class UB<T extends TableName> {
37+
private selects = new Set<string>();
38+
private filters: string[] = [];
39+
private orders: string[] = [];
40+
private lim?: number;
41+
private off?: number;
42+
43+
constructor(private readonly table: T) {}
44+
45+
select(...cols: string[]) {
46+
cols.forEach((c) => this.selects.add(c));
47+
return this;
48+
}
49+
50+
where<K extends ColumnName<T>>(col: K, op: Op, value: RowOf<T>[K]) {
51+
this.filters.push(
52+
`${encodeURIComponent(String(col))}=${op}.${encodeURIComponent(String(value))}`,
53+
);
54+
return this;
55+
}
56+
57+
orderBy<K extends ColumnName<T>>(
58+
col: K,
59+
dir: 'asc' | 'desc' = 'asc',
60+
nulls?: 'first' | 'last',
61+
) {
62+
this.orders.push(`${String(col)}.${dir}${nulls ? `.nulls${nulls}` : ''}`);
63+
return this;
64+
}
65+
66+
range(from: number, to: number) {
67+
this.off = from;
68+
this.lim = to - from + 1;
69+
return this;
70+
}
71+
72+
build(): string {
73+
const p: string[] = [];
74+
if (this.selects.size)
75+
p.push(`select=${encodeURIComponent([...this.selects].join(','))}`);
76+
if (this.filters.length) p.push(...this.filters);
77+
if (this.orders.length)
78+
p.push(`order=${encodeURIComponent(this.orders.join(','))}`);
79+
if (this.lim !== undefined) p.push(`limit=${this.lim}`);
80+
if (this.off !== undefined) p.push(`offset=${this.off}`);
81+
return `${this.table}?${p.join('&')}`;
82+
}
83+
}
84+
85+
export const createApiUrl = <T extends TableName>(table: T) => new UB<T>(table);

src/app/reader/_components/main-page/posts-list/post-card/post-card.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Component, computed, input } from '@angular/core';
1+
import { Component, input } from '@angular/core';
22
import { LabelComponent } from '../label/label.component';
33
import { RouterLink } from '@angular/router';
4-
import { Post } from '../../../../../supabase-types';
4+
import { Post } from '../../../../../types/supabase';
55

66
@Component({
77
selector: 'app-post-card',

src/app/reader/_services/reader-api.service.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ import { Comment, Post, Profile, Tag, PostTag } from '../../types/supabase';
44
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
55
import { map, Observable } from 'rxjs';
66
import { environment } from '../../../environments/environment';
7+
import { createApiUrl } from '../../helpers/api-url-builder';
78

89
@Injectable({ providedIn: 'root' })
910
export class ReaderApiService {
1011
supabaseService = inject(SupabaseService);
1112
http = inject(HttpClient);
1213
private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`;
1314
private readonly apiKey = environment.supabaseKey;
15+
private headers = new HttpHeaders({
16+
apikey: this.apiKey,
17+
Authorization: `Bearer ${this.apiKey}`,
18+
Accept: 'application/json',
19+
});
1420

1521
getPost(id: string): Observable<Post> {
1622
const selectQuery = `
@@ -60,27 +66,20 @@ export class ReaderApiService {
6066
.eq('post_id', postId);
6167
}
6268

63-
getPosts(): Observable<Post[] | null> {
64-
const selectQuery = `
65-
*,
66-
author:profiles(id,username,avatar_url),
67-
post_tags(tags(id,name,color,icon))
68-
`
69-
.replace(/\s+/g, ' ')
70-
.trim();
71-
72-
const params = new HttpParams()
73-
.set('select', selectQuery)
74-
.set('is_draft', 'eq.false')
75-
.set('order', 'created_at.desc');
69+
getPosts(): Observable<Post[]> {
70+
const query = createApiUrl('posts')
71+
.select(
72+
'*',
73+
'author:profiles(id,username,avatar_url)',
74+
'post_tags(tags(id,name,color,icon))',
75+
)
76+
.where('is_draft', 'eq', false)
77+
.orderBy('created_at', 'desc')
78+
.build();
7679

77-
const headers = new HttpHeaders({
78-
apikey: this.apiKey,
79-
Authorization: `Bearer ${this.apiKey}`,
80-
Accept: 'application/json',
80+
return this.http.get<Post[]>(`${this.baseUrl}/${query}`, {
81+
headers: this.headers,
8182
});
82-
83-
return this.http.get<Post[]>(`${this.baseUrl}posts`, { headers, params });
8483
}
8584

8685
async getProfiles(): Promise<Profile[] | null> {

src/app/services/supabase.service.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import { environment } from '../../environments/environment';
1313
providedIn: 'root',
1414
})
1515
export class SupabaseService {
16+
public session: AuthSession | null = null;
1617
private supabase: SupabaseClient;
1718
private readonly ngZone = inject(NgZone);
18-
public session: AuthSession | null = null;
1919

2020
constructor() {
2121
this.supabase = this.ngZone.runOutsideAngular(() =>
@@ -24,9 +24,6 @@ export class SupabaseService {
2424
}
2525

2626
getSession(): AuthSession | null {
27-
this.supabase.auth.getSession().then(({ data }) => {
28-
this.session = data.session;
29-
});
3027
return this.session;
3128
}
3229

0 commit comments

Comments
 (0)