prisma-generator-express 1.19.0 → 1.20.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 +4 -6
- package/src/bin.ts +2 -0
- package/src/constants.ts +1 -0
- package/src/generators/generateImportPrismaStatement.ts +78 -0
- package/src/generators/generateQueryBuilderHelper.ts +138 -0
- package/src/generators/generateRouter.ts +352 -0
- package/src/generators/generateUnifiedDocs.ts +168 -0
- package/src/generators/generateUnifiedHandler.ts +469 -0
- package/src/generators/generateUnifiedScalarUI.ts +1409 -0
- package/src/index.ts +100 -0
- package/src/utils/copyFiles.ts +134 -0
- package/src/utils/strings.ts +7 -0
- package/src/utils/writeFileSafely.ts +83 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-generator-express",
|
|
3
3
|
"description": "Prisma generator for Hono CRUD API with OpenAPI documentation",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.20.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -71,11 +71,9 @@
|
|
|
71
71
|
],
|
|
72
72
|
"files": [
|
|
73
73
|
"dist/**/*",
|
|
74
|
-
"src
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"README.md",
|
|
78
|
-
"LICENSE"
|
|
74
|
+
"src/**/*",
|
|
75
|
+
"../../README.md",
|
|
76
|
+
"../../LICENSE"
|
|
79
77
|
],
|
|
80
78
|
"release": {
|
|
81
79
|
"branches": [
|
package/src/bin.ts
ADDED
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const GENERATOR_NAME = 'prisma-generator-express'
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GeneratorOptions } from '@prisma/generator-helper'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
function findClientGenerator(options: GeneratorOptions) {
|
|
5
|
+
const byName = options.otherGenerators.find((gen) => gen.name === 'client')
|
|
6
|
+
if (byName) return byName
|
|
7
|
+
|
|
8
|
+
const byProvider = options.otherGenerators.find(
|
|
9
|
+
(gen) =>
|
|
10
|
+
gen.provider.value === 'prisma-client-js' ||
|
|
11
|
+
gen.provider.value === '@prisma/client' ||
|
|
12
|
+
gen.provider.value === 'prisma-client',
|
|
13
|
+
)
|
|
14
|
+
if (byProvider) return byProvider
|
|
15
|
+
|
|
16
|
+
const withOutput = options.otherGenerators.find(
|
|
17
|
+
(gen) =>
|
|
18
|
+
gen.output?.value?.includes('prisma') ||
|
|
19
|
+
gen.output?.value?.includes('client'),
|
|
20
|
+
)
|
|
21
|
+
return withOutput || null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getRelativeImportPath(
|
|
25
|
+
fromDir: string,
|
|
26
|
+
clientOutputPath: string,
|
|
27
|
+
): string {
|
|
28
|
+
let relativeImportPath = path.relative(fromDir, clientOutputPath)
|
|
29
|
+
relativeImportPath = relativeImportPath.split(path.sep).join(path.posix.sep)
|
|
30
|
+
if (!relativeImportPath.startsWith('.')) {
|
|
31
|
+
relativeImportPath = './' + relativeImportPath
|
|
32
|
+
}
|
|
33
|
+
return relativeImportPath
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function generateImportPrismaStatement(
|
|
37
|
+
generatorOptions: GeneratorOptions,
|
|
38
|
+
): string {
|
|
39
|
+
const clientGenerator = findClientGenerator(generatorOptions)
|
|
40
|
+
|
|
41
|
+
if (!clientGenerator || !clientGenerator.output?.value) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'Prisma client generator not found. Ensure a generator with provider "prisma-client-js" exists in your schema.',
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const outputValue = generatorOptions.generator.output?.value
|
|
48
|
+
if (!outputValue) {
|
|
49
|
+
throw new Error('Generator output path not defined.')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const subDir = path.join(outputValue, '_relative')
|
|
53
|
+
const outputPath = getRelativeImportPath(subDir, clientGenerator.output.value)
|
|
54
|
+
|
|
55
|
+
return `import { Prisma, PrismaClient } from '${outputPath}';\n`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getRelativeClientPath(
|
|
59
|
+
generatorOptions: GeneratorOptions,
|
|
60
|
+
modelName: string,
|
|
61
|
+
): string {
|
|
62
|
+
const clientGenerator = findClientGenerator(generatorOptions)
|
|
63
|
+
|
|
64
|
+
if (!clientGenerator || !clientGenerator.output?.value) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Prisma client generator not found. Ensure a generator with provider "prisma-client-js" exists in your schema.',
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const outputValue = generatorOptions.generator.output?.value
|
|
71
|
+
if (!outputValue) {
|
|
72
|
+
throw new Error('Generator output path not defined.')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const routerDirPath = path.join(outputValue, modelName)
|
|
76
|
+
|
|
77
|
+
return getRelativeImportPath(routerDirPath, clientGenerator.output.value)
|
|
78
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { GeneratorOptions } from '@prisma/generator-helper'
|
|
2
|
+
|
|
3
|
+
export function generateQueryBuilderHelper(options: GeneratorOptions): string {
|
|
4
|
+
const schemaPath = options.schemaPath
|
|
5
|
+
? JSON.stringify(options.schemaPath)
|
|
6
|
+
: "require('path').resolve(process.cwd(), 'prisma/schema.prisma')"
|
|
7
|
+
|
|
8
|
+
return `import { spawn } from 'child_process'
|
|
9
|
+
import { resolve, join, dirname } from 'path'
|
|
10
|
+
import { existsSync, readFileSync } from 'fs'
|
|
11
|
+
import { createRequire } from 'module'
|
|
12
|
+
import type { ChildProcess } from 'child_process'
|
|
13
|
+
|
|
14
|
+
let _process: ChildProcess | null = null
|
|
15
|
+
let _stopping = false
|
|
16
|
+
let _cleanupRegistered = false
|
|
17
|
+
|
|
18
|
+
export interface QueryBuilderOptions {
|
|
19
|
+
port?: number
|
|
20
|
+
host?: string
|
|
21
|
+
schemaPath?: string
|
|
22
|
+
databaseUrl?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findCliPath(): string | null {
|
|
26
|
+
try {
|
|
27
|
+
const req = createRequire(resolve(process.cwd(), 'package.json'))
|
|
28
|
+
const pkgJsonPath = req.resolve('prisma-query-builder-ui/package.json')
|
|
29
|
+
const pkgDir = dirname(pkgJsonPath)
|
|
30
|
+
const cliPath = join(pkgDir, 'bin', 'cli.js')
|
|
31
|
+
if (existsSync(cliPath)) return cliPath
|
|
32
|
+
} catch {}
|
|
33
|
+
|
|
34
|
+
let dir = process.cwd()
|
|
35
|
+
const root = resolve(dir, '/')
|
|
36
|
+
while (dir !== root) {
|
|
37
|
+
const candidate = join(dir, 'node_modules', 'prisma-query-builder-ui', 'bin', 'cli.js')
|
|
38
|
+
if (existsSync(candidate)) return candidate
|
|
39
|
+
dir = dirname(dir)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function startQueryBuilder(options: QueryBuilderOptions = {}): void {
|
|
46
|
+
if (_process) return
|
|
47
|
+
|
|
48
|
+
const env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
49
|
+
|
|
50
|
+
if (env.NODE_ENV === 'production') return
|
|
51
|
+
|
|
52
|
+
const cliPath = findCliPath()
|
|
53
|
+
if (!cliPath) {
|
|
54
|
+
console.warn('[query-builder] prisma-query-builder-ui not found. Install: npm install prisma-query-builder-ui')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const port = options.port || 5173
|
|
59
|
+
const host = options.host || 'localhost'
|
|
60
|
+
const schemaPath = options.schemaPath || ${schemaPath}
|
|
61
|
+
const databaseUrl = options.databaseUrl || env.DATABASE_URL || ''
|
|
62
|
+
|
|
63
|
+
if (!existsSync(schemaPath)) {
|
|
64
|
+
console.error('[query-builder] Schema file not found: ' + schemaPath)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let schemaContent: string
|
|
69
|
+
try {
|
|
70
|
+
schemaContent = readFileSync(schemaPath, 'utf-8')
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error('[query-builder] Failed to read schema:', err)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const schemaCwd = dirname(resolve(schemaPath))
|
|
77
|
+
|
|
78
|
+
_process = spawn(process.execPath, [cliPath], {
|
|
79
|
+
stdio: 'inherit',
|
|
80
|
+
env: {
|
|
81
|
+
...env,
|
|
82
|
+
PORT: String(port),
|
|
83
|
+
HOST: host,
|
|
84
|
+
PRISMA_QUERY_BUILDER_MODE: 'embedded',
|
|
85
|
+
DISABLE_PERSISTENCE: 'true',
|
|
86
|
+
PRISMA_QUERY_BUILDER_SCHEMA_CONTENT: schemaContent,
|
|
87
|
+
PRISMA_QUERY_BUILDER_CWD: schemaCwd,
|
|
88
|
+
DATABASE_URL: databaseUrl,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
_process.on('error', (err) => {
|
|
93
|
+
console.error('[query-builder] Failed to start:', err.message)
|
|
94
|
+
_process = null
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
_process.on('exit', (code) => {
|
|
98
|
+
const wasStopping = _stopping
|
|
99
|
+
_stopping = false
|
|
100
|
+
_process = null
|
|
101
|
+
if (!wasStopping && code !== 0) {
|
|
102
|
+
console.warn('[query-builder] Process exited with code ' + code)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (!_cleanupRegistered) {
|
|
107
|
+
_cleanupRegistered = true
|
|
108
|
+
|
|
109
|
+
process.on('exit', () => {
|
|
110
|
+
stopQueryBuilder()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const handleSigint = () => {
|
|
114
|
+
stopQueryBuilder()
|
|
115
|
+
process.removeListener('SIGINT', handleSigint)
|
|
116
|
+
process.kill(process.pid, 'SIGINT')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const handleSigterm = () => {
|
|
120
|
+
stopQueryBuilder()
|
|
121
|
+
process.removeListener('SIGTERM', handleSigterm)
|
|
122
|
+
process.kill(process.pid, 'SIGTERM')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
process.on('SIGINT', handleSigint)
|
|
126
|
+
process.on('SIGTERM', handleSigterm)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('[query-builder] Starting on http://' + host + ':' + port)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function stopQueryBuilder(): void {
|
|
133
|
+
if (!_process || _process.killed) return
|
|
134
|
+
_stopping = true
|
|
135
|
+
_process.kill()
|
|
136
|
+
}
|
|
137
|
+
`
|
|
138
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { DMMF } from '@prisma/generator-helper'
|
|
2
|
+
|
|
3
|
+
export function generateRouterFunction({
|
|
4
|
+
model,
|
|
5
|
+
enums,
|
|
6
|
+
relativeClientPath,
|
|
7
|
+
}: {
|
|
8
|
+
model: DMMF.Model
|
|
9
|
+
enums: DMMF.DatamodelEnum[]
|
|
10
|
+
relativeClientPath: string
|
|
11
|
+
}): string {
|
|
12
|
+
const modelName = model.name
|
|
13
|
+
const modelNameLower = modelName.toLowerCase()
|
|
14
|
+
const routerFunctionName = `${modelName}Router`
|
|
15
|
+
|
|
16
|
+
const fieldsMeta = model.fields.map((f) => ({
|
|
17
|
+
name: f.name,
|
|
18
|
+
kind: f.kind,
|
|
19
|
+
type: f.type,
|
|
20
|
+
isList: f.isList,
|
|
21
|
+
isRequired: f.isRequired,
|
|
22
|
+
hasDefaultValue: f.hasDefaultValue,
|
|
23
|
+
isUpdatedAt: f.isUpdatedAt ?? false,
|
|
24
|
+
documentation: f.documentation,
|
|
25
|
+
relationFromFields: f.relationFromFields,
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
const referencedEnumTypes = new Set(
|
|
29
|
+
model.fields.filter((f) => f.kind === 'enum').map((f) => f.type),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const enumsMeta = enums
|
|
33
|
+
.filter((e) => referencedEnumTypes.has(e.name))
|
|
34
|
+
.map((e) => ({
|
|
35
|
+
name: e.name,
|
|
36
|
+
values: e.values.map((v) => ({ name: v.name })),
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
return `import express, { Request, Response, NextFunction, RequestHandler } from 'express'
|
|
40
|
+
import type { PrismaClient } from '${relativeClientPath}'
|
|
41
|
+
import {
|
|
42
|
+
${modelName}FindUnique,
|
|
43
|
+
${modelName}FindUniqueOrThrow,
|
|
44
|
+
${modelName}FindFirst,
|
|
45
|
+
${modelName}FindFirstOrThrow,
|
|
46
|
+
${modelName}FindMany,
|
|
47
|
+
${modelName}FindManyPaginated,
|
|
48
|
+
${modelName}Create,
|
|
49
|
+
${modelName}CreateMany,
|
|
50
|
+
${modelName}CreateManyAndReturn,
|
|
51
|
+
${modelName}Update,
|
|
52
|
+
${modelName}UpdateMany,
|
|
53
|
+
${modelName}UpdateManyAndReturn,
|
|
54
|
+
${modelName}Upsert,
|
|
55
|
+
${modelName}Delete,
|
|
56
|
+
${modelName}DeleteMany,
|
|
57
|
+
${modelName}Aggregate,
|
|
58
|
+
${modelName}Count,
|
|
59
|
+
${modelName}GroupBy
|
|
60
|
+
} from './${modelName}Handlers'
|
|
61
|
+
import type { RouteConfig } from '../routeConfig'
|
|
62
|
+
import { parseQueryParams } from '../parseQueryParams'
|
|
63
|
+
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
64
|
+
|
|
65
|
+
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
66
|
+
|
|
67
|
+
const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
|
|
68
|
+
|
|
69
|
+
const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
|
|
70
|
+
|
|
71
|
+
const defaultOpConfig = {
|
|
72
|
+
before: [] as RequestHandler[],
|
|
73
|
+
after: [] as RequestHandler[],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizePrefix(p: string): string {
|
|
77
|
+
if (!p) return ''
|
|
78
|
+
let result = p
|
|
79
|
+
if (!result.startsWith('/')) result = '/' + result
|
|
80
|
+
while (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1)
|
|
81
|
+
if (result === '/') return ''
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function transformResult(value: unknown): unknown {
|
|
86
|
+
if (value === null || value === undefined) return value
|
|
87
|
+
if (typeof value === 'bigint') return value.toString()
|
|
88
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
89
|
+
return value.toString('base64')
|
|
90
|
+
}
|
|
91
|
+
if (value instanceof Uint8Array) {
|
|
92
|
+
let binary = ''
|
|
93
|
+
for (let i = 0; i < value.length; i++) binary += String.fromCharCode(value[i])
|
|
94
|
+
return btoa(binary)
|
|
95
|
+
}
|
|
96
|
+
if (value instanceof Date) return value
|
|
97
|
+
if (Array.isArray(value)) return value.map(transformResult)
|
|
98
|
+
if (typeof value === 'object') {
|
|
99
|
+
const proto = Object.getPrototypeOf(value)
|
|
100
|
+
if (proto !== Object.prototype && proto !== null) return value
|
|
101
|
+
const out: Record<string, unknown> = {}
|
|
102
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
103
|
+
out[k] = transformResult(v)
|
|
104
|
+
}
|
|
105
|
+
return out
|
|
106
|
+
}
|
|
107
|
+
return value
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isQueryBuilderEnabled(config: RouteConfig): boolean {
|
|
111
|
+
if (config.queryBuilder === false) return false
|
|
112
|
+
if (typeof config.queryBuilder === 'object' && config.queryBuilder.enabled === false) return false
|
|
113
|
+
if (_env.NODE_ENV === 'production') return false
|
|
114
|
+
return true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getQueryBuilderConfig(config: RouteConfig) {
|
|
118
|
+
if (config.queryBuilder === false) return null
|
|
119
|
+
if (typeof config.queryBuilder === 'object') return config.queryBuilder
|
|
120
|
+
return {}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function ${routerFunctionName}(config: RouteConfig = {}) {
|
|
124
|
+
const router = express.Router()
|
|
125
|
+
|
|
126
|
+
router.use(express.json())
|
|
127
|
+
|
|
128
|
+
const customPrefix = normalizePrefix(config.customUrlPrefix || '')
|
|
129
|
+
const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
|
|
130
|
+
const basePath = customPrefix + modelPrefix
|
|
131
|
+
|
|
132
|
+
const openApiDisabled = config.disableOpenApi === true
|
|
133
|
+
|| (config.disableOpenApi !== false && (
|
|
134
|
+
_env.DISABLE_OPENAPI === 'true'
|
|
135
|
+
|| _env.NODE_ENV === 'production'
|
|
136
|
+
))
|
|
137
|
+
|
|
138
|
+
const qbEnabled = isQueryBuilderEnabled(config)
|
|
139
|
+
|
|
140
|
+
if (qbEnabled) {
|
|
141
|
+
const qbConfig = getQueryBuilderConfig(config)
|
|
142
|
+
if (qbConfig) {
|
|
143
|
+
import('../queryBuilder').then(mod => mod.startQueryBuilder(qbConfig)).catch(() => {})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parseQuery: RequestHandler = (req, res, next) => {
|
|
148
|
+
const rawQuery = req.query
|
|
149
|
+
if (rawQuery && Object.keys(rawQuery).length > 0) {
|
|
150
|
+
res.locals.parsedQuery = parseQueryParams(rawQuery as Record<string, unknown>)
|
|
151
|
+
}
|
|
152
|
+
next()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const setShape = (opConfig: any): RequestHandler => {
|
|
156
|
+
return (req, res, next) => {
|
|
157
|
+
res.locals.routeConfig = config
|
|
158
|
+
if (opConfig.shape) {
|
|
159
|
+
res.locals.guardShape = opConfig.shape
|
|
160
|
+
const caller = config.guard?.resolveVariant?.(req)
|
|
161
|
+
?? req.get(config.guard?.variantHeader || 'x-api-variant')
|
|
162
|
+
?? undefined
|
|
163
|
+
if (caller) {
|
|
164
|
+
res.locals.guardCaller = caller
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
next()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const respond: RequestHandler = (_req, res) => {
|
|
172
|
+
const data = res.locals.data
|
|
173
|
+
if (data === undefined) {
|
|
174
|
+
return res.status(500).json({ message: 'No data set by handler' })
|
|
175
|
+
}
|
|
176
|
+
return res.json(transformResult(data))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const respondCreated: RequestHandler = (_req, res) => {
|
|
180
|
+
const data = res.locals.data
|
|
181
|
+
if (data === undefined) {
|
|
182
|
+
return res.status(500).json({ message: 'No data set by handler' })
|
|
183
|
+
}
|
|
184
|
+
return res.status(201).json(transformResult(data))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!openApiDisabled) {
|
|
188
|
+
const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
|
|
189
|
+
const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
|
|
190
|
+
|
|
191
|
+
router.get(openapiJsonPath, (_req, res) => {
|
|
192
|
+
const spec = buildModelOpenApi(
|
|
193
|
+
'${modelName}',
|
|
194
|
+
MODEL_FIELDS as any,
|
|
195
|
+
MODEL_ENUMS as any,
|
|
196
|
+
config,
|
|
197
|
+
{ format: 'json' }
|
|
198
|
+
)
|
|
199
|
+
res.json(spec)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
router.get(openapiYamlPath, (_req, res) => {
|
|
203
|
+
const spec = buildModelOpenApi(
|
|
204
|
+
'${modelName}',
|
|
205
|
+
MODEL_FIELDS as any,
|
|
206
|
+
MODEL_ENUMS as any,
|
|
207
|
+
config,
|
|
208
|
+
{ format: 'yaml' }
|
|
209
|
+
)
|
|
210
|
+
res.type('application/yaml').send(spec as string)
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (config.enableAll || config.findFirst) {
|
|
215
|
+
const opConfig = config.findFirst || defaultOpConfig
|
|
216
|
+
const { before = [], after = [] } = opConfig
|
|
217
|
+
const path = basePath ? \`\${basePath}/first\` : '/first'
|
|
218
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindFirst as RequestHandler, ...after, respond)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (config.enableAll || config.findFirstOrThrow) {
|
|
222
|
+
const opConfig = config.findFirstOrThrow || defaultOpConfig
|
|
223
|
+
const { before = [], after = [] } = opConfig
|
|
224
|
+
const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
|
|
225
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindFirstOrThrow as RequestHandler, ...after, respond)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (config.enableAll || config.findManyPaginated) {
|
|
229
|
+
const opConfig = config.findManyPaginated || defaultOpConfig
|
|
230
|
+
const { before = [], after = [] } = opConfig
|
|
231
|
+
const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
|
|
232
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindManyPaginated as RequestHandler, ...after, respond)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (config.enableAll || config.aggregate) {
|
|
236
|
+
const opConfig = config.aggregate || defaultOpConfig
|
|
237
|
+
const { before = [], after = [] } = opConfig
|
|
238
|
+
const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
|
|
239
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}Aggregate as RequestHandler, ...after, respond)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (config.enableAll || config.count) {
|
|
243
|
+
const opConfig = config.count || defaultOpConfig
|
|
244
|
+
const { before = [], after = [] } = opConfig
|
|
245
|
+
const path = basePath ? \`\${basePath}/count\` : '/count'
|
|
246
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}Count as RequestHandler, ...after, respond)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (config.enableAll || config.groupBy) {
|
|
250
|
+
const opConfig = config.groupBy || defaultOpConfig
|
|
251
|
+
const { before = [], after = [] } = opConfig
|
|
252
|
+
const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
|
|
253
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}GroupBy as RequestHandler, ...after, respond)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (config.enableAll || config.findUniqueOrThrow) {
|
|
257
|
+
const opConfig = config.findUniqueOrThrow || defaultOpConfig
|
|
258
|
+
const { before = [], after = [] } = opConfig
|
|
259
|
+
const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
|
|
260
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindUniqueOrThrow as RequestHandler, ...after, respond)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (config.enableAll || config.findUnique) {
|
|
264
|
+
const opConfig = config.findUnique || defaultOpConfig
|
|
265
|
+
const { before = [], after = [] } = opConfig
|
|
266
|
+
const path = basePath ? \`\${basePath}/unique\` : '/unique'
|
|
267
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindUnique as RequestHandler, ...after, respond)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (config.enableAll || config.findMany) {
|
|
271
|
+
const opConfig = config.findMany || defaultOpConfig
|
|
272
|
+
const { before = [], after = [] } = opConfig
|
|
273
|
+
const path = basePath || '/'
|
|
274
|
+
router.get(path, parseQuery, setShape(opConfig), ...before, ${modelName}FindMany as RequestHandler, ...after, respond)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (config.enableAll || config.createManyAndReturn) {
|
|
278
|
+
const opConfig = config.createManyAndReturn || defaultOpConfig
|
|
279
|
+
const { before = [], after = [] } = opConfig
|
|
280
|
+
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
281
|
+
router.post(path, setShape(opConfig), ...before, ${modelName}CreateManyAndReturn as RequestHandler, ...after, respondCreated)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (config.enableAll || config.createMany) {
|
|
285
|
+
const opConfig = config.createMany || defaultOpConfig
|
|
286
|
+
const { before = [], after = [] } = opConfig
|
|
287
|
+
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
288
|
+
router.post(path, setShape(opConfig), ...before, ${modelName}CreateMany as RequestHandler, ...after, respondCreated)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (config.enableAll || config.create) {
|
|
292
|
+
const opConfig = config.create || defaultOpConfig
|
|
293
|
+
const { before = [], after = [] } = opConfig
|
|
294
|
+
const path = basePath || '/'
|
|
295
|
+
router.post(path, setShape(opConfig), ...before, ${modelName}Create as RequestHandler, ...after, respondCreated)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (config.enableAll || config.updateManyAndReturn) {
|
|
299
|
+
const opConfig = config.updateManyAndReturn || defaultOpConfig
|
|
300
|
+
const { before = [], after = [] } = opConfig
|
|
301
|
+
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
302
|
+
router.put(path, setShape(opConfig), ...before, ${modelName}UpdateManyAndReturn as RequestHandler, ...after, respond)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (config.enableAll || config.updateMany) {
|
|
306
|
+
const opConfig = config.updateMany || defaultOpConfig
|
|
307
|
+
const { before = [], after = [] } = opConfig
|
|
308
|
+
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
309
|
+
router.put(path, setShape(opConfig), ...before, ${modelName}UpdateMany as RequestHandler, ...after, respond)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (config.enableAll || config.update) {
|
|
313
|
+
const opConfig = config.update || defaultOpConfig
|
|
314
|
+
const { before = [], after = [] } = opConfig
|
|
315
|
+
const path = basePath || '/'
|
|
316
|
+
router.put(path, setShape(opConfig), ...before, ${modelName}Update as RequestHandler, ...after, respond)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (config.enableAll || config.upsert) {
|
|
320
|
+
const opConfig = config.upsert || defaultOpConfig
|
|
321
|
+
const { before = [], after = [] } = opConfig
|
|
322
|
+
const path = basePath || '/'
|
|
323
|
+
router.patch(path, setShape(opConfig), ...before, ${modelName}Upsert as RequestHandler, ...after, respond)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (config.enableAll || config.deleteMany) {
|
|
327
|
+
const opConfig = config.deleteMany || defaultOpConfig
|
|
328
|
+
const { before = [], after = [] } = opConfig
|
|
329
|
+
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
330
|
+
router.delete(path, setShape(opConfig), ...before, ${modelName}DeleteMany as RequestHandler, ...after, respond)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (config.enableAll || config.delete) {
|
|
334
|
+
const opConfig = config.delete || defaultOpConfig
|
|
335
|
+
const { before = [], after = [] } = opConfig
|
|
336
|
+
const path = basePath || '/'
|
|
337
|
+
router.delete(path, setShape(opConfig), ...before, ${modelName}Delete as RequestHandler, ...after, respond)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
router.use((err: any, _req: Request, res: Response, next: NextFunction) => {
|
|
341
|
+
const status = typeof err.status === 'number' ? err.status : 500
|
|
342
|
+
const message = err.message || 'Internal server error'
|
|
343
|
+
if (!res.headersSent) {
|
|
344
|
+
return res.status(status).json({ message })
|
|
345
|
+
}
|
|
346
|
+
next(err)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
return router
|
|
350
|
+
}
|
|
351
|
+
`
|
|
352
|
+
}
|