@svadmin/nestjs-query 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@svadmin/nestjs-query",
3
+ "version": "0.3.0",
4
+ "description": "NestJS Query DataProvider for svadmin",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": ["src/**/*"],
8
+ "main": "src/index.ts",
9
+ "types": "src/index.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./src/index.ts",
13
+ "default": "./src/index.ts"
14
+ }
15
+ },
16
+ "peerDependencies": {
17
+ "@svadmin/core": "^0.3.0"
18
+ },
19
+ "license": "MIT"
20
+ }
@@ -0,0 +1,108 @@
1
+ import type {
2
+ DataProvider, GetListParams, GetListResult, GetOneParams, GetOneResult,
3
+ CreateParams, CreateResult, UpdateParams, UpdateResult, DeleteParams, DeleteResult,
4
+ GetManyParams, GetManyResult, CustomParams, CustomResult, Sort, Filter
5
+ } from '@svadmin/core';
6
+
7
+ // Nestjs-query uses GraphQL with auto-generated queries
8
+ function mapOperator(op: string): string {
9
+ const map: Record<string, string> = {
10
+ eq: 'eq', ne: 'neq', lt: 'lt', gt: 'gt', lte: 'lte', gte: 'gte',
11
+ contains: 'like', in: 'in', null: 'is',
12
+ };
13
+ return map[op] ?? 'eq';
14
+ }
15
+
16
+ function buildFilter(filters?: Filter[]): Record<string, unknown> {
17
+ if (!filters?.length) return {};
18
+ const where: Record<string, unknown> = {};
19
+ for (const f of filters) {
20
+ if ('field' in f) {
21
+ where[f.field as string] = { [mapOperator(f.operator as string)]: f.operator === 'contains' ? `%${f.value}%` : f.value };
22
+ }
23
+ }
24
+ return where;
25
+ }
26
+
27
+ function buildSorting(sorters?: Sort[]): Array<{ field: string; direction: string }> {
28
+ return (sorters ?? []).map((s: Sort) => ({ field: s.field, direction: s.order === 'asc' ? 'ASC' : 'DESC' }));
29
+ }
30
+
31
+ async function gql(endpoint: string, query: string, variables: Record<string, unknown> = {}, headers: Record<string, string> = {}) {
32
+ const res = await fetch(endpoint, {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json', ...headers },
35
+ body: JSON.stringify({ query, variables }),
36
+ });
37
+ const json = await res.json();
38
+ if (json.errors) throw new Error(json.errors.map((e: any) => e.message).join('\n'));
39
+ return json.data;
40
+ }
41
+
42
+ export function createNestjsQueryDataProvider(endpoint: string, headers: Record<string, string> = {}): DataProvider {
43
+ return {
44
+ getApiUrl: () => endpoint,
45
+
46
+ async getList<T>({ resource, pagination, sorters, filters, meta }: GetListParams): Promise<GetListResult<T>> {
47
+ const { current = 1, pageSize = 10 } = pagination ?? {};
48
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
49
+ const query = `query($paging: CursorPaging, $filter: ${resource}Filter, $sorting: [${resource}Sort!]) {
50
+ ${resource}(paging: $paging, filter: $filter, sorting: $sorting) {
51
+ totalCount
52
+ nodes { ${fields} }
53
+ }
54
+ }`;
55
+ const data = await gql(endpoint, query, {
56
+ paging: { first: pageSize, offset: (current - 1) * pageSize },
57
+ filter: buildFilter(filters),
58
+ sorting: buildSorting(sorters),
59
+ }, headers);
60
+ return { data: data[resource]?.nodes ?? [], total: data[resource]?.totalCount ?? 0 };
61
+ },
62
+
63
+ async getOne<T>({ resource, id, meta }: GetOneParams): Promise<GetOneResult<T>> {
64
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
65
+ const singular = resource.replace(/s$/, '');
66
+ const query = `query($id: ID!) { ${singular}(id: $id) { ${fields} } }`;
67
+ const data = await gql(endpoint, query, { id }, headers);
68
+ return { data: data[singular] as T };
69
+ },
70
+
71
+ async create<T>({ resource, variables, meta }: CreateParams): Promise<CreateResult<T>> {
72
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
73
+ const singular = resource.replace(/s$/, '');
74
+ const query = `mutation($input: CreateOne${singular}Input!) { createOne${singular}(input: $input) { ${fields} } }`;
75
+ const data = await gql(endpoint, query, { input: { [singular.toLowerCase()]: variables } }, headers);
76
+ return { data: data[`createOne${singular}`] as T };
77
+ },
78
+
79
+ async update<T>({ resource, id, variables, meta }: UpdateParams): Promise<UpdateResult<T>> {
80
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
81
+ const singular = resource.replace(/s$/, '');
82
+ const query = `mutation($input: UpdateOne${singular}Input!) { updateOne${singular}(input: $input) { ${fields} } }`;
83
+ const data = await gql(endpoint, query, { input: { id, update: variables } }, headers);
84
+ return { data: data[`updateOne${singular}`] as T };
85
+ },
86
+
87
+ async deleteOne<T>({ resource, id, meta }: DeleteParams): Promise<DeleteResult<T>> {
88
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
89
+ const singular = resource.replace(/s$/, '');
90
+ const query = `mutation($input: DeleteOne${singular}Input!) { deleteOne${singular}(input: $input) { ${fields} } }`;
91
+ const data = await gql(endpoint, query, { input: { id } }, headers);
92
+ return { data: data[`deleteOne${singular}`] as T };
93
+ },
94
+
95
+ async getMany<T>({ resource, ids, meta }: GetManyParams): Promise<GetManyResult<T>> {
96
+ const fields = (meta?.fields as string[])?.join('\n') || 'id';
97
+ const query = `query($filter: ${resource}Filter) { ${resource}(filter: $filter) { nodes { ${fields} } } }`;
98
+ const data = await gql(endpoint, query, { filter: { id: { in: ids } } }, headers);
99
+ return { data: data[resource]?.nodes ?? [] };
100
+ },
101
+
102
+ async custom<T>({ url, method, payload, headers: h }: CustomParams): Promise<CustomResult<T>> {
103
+ const res = await fetch(url, { method: method.toUpperCase(), headers: { 'Content-Type': 'application/json', ...headers, ...h }, body: payload ? JSON.stringify(payload) : undefined });
104
+ if (!res.ok) throw new Error(`Custom request failed: ${res.status}`);
105
+ return { data: (await res.json()) as T };
106
+ },
107
+ };
108
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { createNestjsQueryDataProvider } from './data-provider';