Skip to content

Commit 3015ee0

Browse files
committed
feat(types): support views
1 parent 378c00c commit 3015ee0

File tree

5 files changed

+98
-29
lines changed

5 files changed

+98
-29
lines changed

src/PostgrestClient.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,20 @@ export default class PostgrestClient<
4545
}
4646

4747
/**
48-
* Perform a table operation.
48+
* Perform a query on a table or a view.
4949
*
50-
* @param table The table name to operate on.
50+
* @param relation The table or view name to query.
5151
*/
5252
from<
5353
TableName extends string & keyof Schema['Tables'],
5454
Table extends Schema['Tables'][TableName]
55-
>(table: TableName): PostgrestQueryBuilder<Table>
56-
from(table: string): PostgrestQueryBuilder<any>
57-
from(table: string): PostgrestQueryBuilder<any> {
58-
const url = new URL(`${this.url}/${table}`)
55+
>(relation: TableName): PostgrestQueryBuilder<Table>
56+
from<ViewName extends string & keyof Schema['Views'], View extends Schema['Views'][ViewName]>(
57+
relation: ViewName
58+
): PostgrestQueryBuilder<View>
59+
from(relation: string): PostgrestQueryBuilder<any>
60+
from(relation: string): PostgrestQueryBuilder<any> {
61+
const url = new URL(`${this.url}/${relation}`)
5962
return new PostgrestQueryBuilder<any>(url, {
6063
headers: { ...this.headers },
6164
schema: this.schema,

src/PostgrestQueryBuilder.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import PostgrestBuilder from './PostgrestBuilder'
22
import PostgrestFilterBuilder from './PostgrestFilterBuilder'
33
import { GetResult } from './select-query-parser'
4-
import { Fetch, GenericTable } from './types'
4+
import { Fetch, GenericTable, GenericView } from './types'
55

6-
export default class PostgrestQueryBuilder<Table extends GenericTable> {
6+
export default class PostgrestQueryBuilder<Relation extends GenericTable | GenericView> {
77
url: URL
88
headers: Record<string, string>
99
schema?: string
@@ -35,7 +35,7 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
3535
*/
3636
select<
3737
Query extends string = '*',
38-
Result = GetResult<Table['Row'], Query extends '*' ? '*' : Query>
38+
Result = GetResult<Relation['Row'], Query extends '*' ? '*' : Query>
3939
>(
4040
columns?: Query,
4141
{
@@ -47,7 +47,7 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
4747
/** Count algorithm to use to count rows in a table. */
4848
count?: 'exact' | 'planned' | 'estimated'
4949
} = {}
50-
): PostgrestFilterBuilder<Table['Row'], Result> {
50+
): PostgrestFilterBuilder<Relation['Row'], Result> {
5151
const method = head ? 'HEAD' : 'GET'
5252
// Remove whitespaces except when quoted
5353
let quoted = false
@@ -83,15 +83,15 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
8383
*
8484
* @param values The values to insert.
8585
*/
86-
insert<Row extends Table['Insert']>(
86+
insert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
8787
values: Row | Row[],
8888
{
8989
count,
9090
}: {
9191
/** Count algorithm to use to count rows in a table. */
9292
count?: 'exact' | 'planned' | 'estimated'
9393
} = {}
94-
): PostgrestFilterBuilder<Table['Row'], undefined> {
94+
): PostgrestFilterBuilder<Relation['Row'], undefined> {
9595
const method = 'POST'
9696

9797
const prefersHeaders = []
@@ -128,7 +128,7 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
128128
*
129129
* @param values The values to insert.
130130
*/
131-
upsert<Row extends Table['Insert']>(
131+
upsert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
132132
values: Row | Row[],
133133
{
134134
onConflict,
@@ -142,7 +142,7 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
142142
/** Specifies if duplicate rows should be ignored and not inserted. */
143143
ignoreDuplicates?: boolean
144144
} = {}
145-
): PostgrestFilterBuilder<Table['Row'], undefined> {
145+
): PostgrestFilterBuilder<Relation['Row'], undefined> {
146146
const method = 'POST'
147147

148148
const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`]
@@ -173,15 +173,15 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
173173
*
174174
* @param values The values to update.
175175
*/
176-
update<Row extends Table['Update']>(
176+
update<Row extends Relation extends { Update: unknown } ? Relation['Update'] : never>(
177177
values: Row,
178178
{
179179
count,
180180
}: {
181181
/** Count algorithm to use to count rows in a table. */
182182
count?: 'exact' | 'planned' | 'estimated'
183183
} = {}
184-
): PostgrestFilterBuilder<Table['Row'], undefined> {
184+
): PostgrestFilterBuilder<Relation['Row'], undefined> {
185185
const method = 'PATCH'
186186
const prefersHeaders = []
187187
const body = values
@@ -212,7 +212,7 @@ export default class PostgrestQueryBuilder<Table extends GenericTable> {
212212
}: {
213213
/** Count algorithm to use to count rows in a table. */
214214
count?: 'exact' | 'planned' | 'estimated'
215-
} = {}): PostgrestFilterBuilder<Table['Row'], undefined> {
215+
} = {}): PostgrestFilterBuilder<Relation['Row'], undefined> {
216216
const method = 'DELETE'
217217
const prefersHeaders = []
218218
if (count) {

src/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,25 @@ export type GenericTable = {
5050
Update: Record<string, unknown>
5151
}
5252

53+
export type GenericUpdatableView = {
54+
Row: Record<string, unknown>
55+
Insert: Record<string, unknown>
56+
Update: Record<string, unknown>
57+
}
58+
59+
export type GenericNonUpdatableView = {
60+
Row: Record<string, unknown>
61+
}
62+
63+
export type GenericView = GenericUpdatableView | GenericNonUpdatableView
64+
5365
export type GenericFunction = {
5466
Args: Record<string, unknown>
5567
Returns: unknown
5668
}
5769

5870
export type GenericSchema = {
5971
Tables: Record<string, GenericTable>
72+
Views: Record<string, GenericView>
6073
Functions: Record<string, GenericFunction>
6174
}

test/db/00-schema.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ create table public.shops (
6666
, shop_geom extensions.geometry(POINT, 4326)
6767
);
6868

69+
create view public.non_updatable_view as
70+
select username from public.users limit 1;
71+
72+
create view public.updatable_view as
73+
select username, 1 as non_updatable_column from public.users;
74+
6975
-- SECOND SCHEMA USERS
7076
CREATE TYPE personal.user_status AS ENUM ('ONLINE', 'OFFLINE');
7177
CREATE TABLE personal.users(

test/types.ts

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,75 @@ export interface Database {
88
username: string
99
data: Json | null
1010
age_range: unknown | null
11-
status: 'ONLINE' | 'OFFLINE' | null
11+
status: Database['public']['Enums']['user_status'] | null
1212
}
1313
Insert: {
1414
username: string
1515
data?: Json | null
1616
age_range?: unknown | null
17-
status?: 'ONLINE' | 'OFFLINE' | null
17+
status?: Database['public']['Enums']['user_status'] | null
1818
}
1919
Update: {
2020
username?: string
2121
data?: Json | null
2222
age_range?: unknown | null
23-
status?: 'ONLINE' | 'OFFLINE' | null
23+
status?: Database['public']['Enums']['user_status'] | null
2424
}
2525
}
2626
}
27+
Views: {
28+
[_ in never]: never
29+
}
2730
Functions: {
2831
get_status: {
2932
Args: { name_param: string }
30-
Returns: 'ONLINE' | 'OFFLINE'
33+
Returns: Database['public']['Enums']['user_status']
3134
}
3235
}
36+
Enums: {
37+
user_status: 'ONLINE' | 'OFFLINE'
38+
}
3339
}
3440
public: {
3541
Tables: {
42+
shops: {
43+
Row: {
44+
id: number
45+
address: string | null
46+
shop_geom: unknown | null
47+
}
48+
Insert: {
49+
id: number
50+
address?: string | null
51+
shop_geom?: unknown | null
52+
}
53+
Update: {
54+
id?: number
55+
address?: string | null
56+
shop_geom?: unknown | null
57+
}
58+
}
3659
users: {
3760
Row: {
3861
username: string
3962
data: Json | null
4063
age_range: unknown | null
4164
catchphrase: unknown | null
42-
status: 'ONLINE' | 'OFFLINE' | null
65+
status: Database['public']['Enums']['user_status'] | null
4366
}
4467
Insert: {
4568
username: string
4669
data?: Json | null
4770
age_range?: unknown | null
4871
catchphrase?: unknown | null
49-
status?: 'ONLINE' | 'OFFLINE' | null
72+
status?: Database['public']['Enums']['user_status'] | null
5073
}
5174
Update: {
5275
username?: string
5376
data?: Json | null
5477
age_range?: unknown | null
5578
catchphrase?: unknown | null
56-
status?: 'ONLINE' | 'OFFLINE' | null
79+
status?: Database['public']['Enums']['user_status'] | null
5780
}
5881
}
5982
channels: {
@@ -97,23 +120,47 @@ export interface Database {
97120
}
98121
}
99122
}
123+
Views: {
124+
non_updatable_view: {
125+
Row: {
126+
username: string | null
127+
}
128+
}
129+
updatable_view: {
130+
Row: {
131+
username: string | null
132+
non_updatable_column: number | null
133+
}
134+
Insert: {
135+
username?: string | null
136+
non_updatable_column?: never
137+
}
138+
Update: {
139+
username?: string | null
140+
non_updatable_column?: never
141+
}
142+
}
143+
}
100144
Functions: {
101145
get_status: {
102146
Args: { name_param: string }
103-
Returns: 'ONLINE' | 'OFFLINE'
147+
Returns: Database['public']['Enums']['user_status']
104148
}
105149
get_username_and_status: {
106150
Args: { name_param: string }
107151
Returns: Record<string, unknown>[]
108152
}
153+
offline_user: {
154+
Args: { name_param: string }
155+
Returns: Database['public']['Enums']['user_status']
156+
}
109157
void_func: {
110158
Args: Record<PropertyKey, never>
111159
Returns: undefined
112160
}
113-
offline_user: {
114-
Args: { name_param: string }
115-
Returns: 'ONLINE' | 'OFFLINE'
116-
}
161+
}
162+
Enums: {
163+
user_status: 'ONLINE' | 'OFFLINE'
117164
}
118165
}
119166
}

0 commit comments

Comments
 (0)