@anthonylzq/simba.js 6.2.2 → 7.0.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.
@@ -1,2444 +0,0 @@
1
- const { platform } = require('os')
2
- const { promisify } = require('util')
3
- const exec = promisify(require('child_process').exec)
4
- const writeFile = require('../../utils/writeFile')
5
-
6
- /**
7
- * @param {Object} args
8
- * @param {String} args.projectName
9
- */
10
- const expressRestNetwork = async ({ projectName }) => {
11
- const createFoldersCommand = `mkdir ${projectName}/src/network/routes/utils`
12
-
13
- if (platform() === 'win32')
14
- await exec(createFoldersCommand.replaceAll('/', '\\'))
15
- else await exec(createFoldersCommand)
16
-
17
- const network = {
18
- response: {
19
- content: `interface ResponseProps {
20
- error: boolean
21
- message: unknown
22
- res: CustomResponse
23
- status: number
24
- }
25
-
26
- const response = ({ error, message, res, status }: ResponseProps): void => {
27
- res.status(status).send({ error, message })
28
- }
29
-
30
- export { response }
31
- `,
32
- file: `${projectName}/src/network/response.ts`
33
- },
34
- router: {
35
- content: `import { Application, Response, Request, Router, NextFunction } from 'express'
36
- import swaggerUi from 'swagger-ui-express'
37
- import httpErrors from 'http-errors'
38
-
39
- import { response } from './response'
40
- import { Home, User } from './routes'
41
- import { docs } from 'utils'
42
-
43
- const routers = [User]
44
- const applyRoutes = (app: Application): void => {
45
- app.use('/', Home)
46
- app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(docs))
47
- routers.forEach((router: Router): Application => app.use('/api', router))
48
-
49
- // Handling 404 error
50
- app.use((req, res, next) => {
51
- next(new httpErrors.NotFound('This route does not exists'))
52
- })
53
- app.use(
54
- (
55
- error: httpErrors.HttpError,
56
- req: Request,
57
- res: Response,
58
- next: NextFunction
59
- ) => {
60
- response({
61
- error: true,
62
- message: error.message,
63
- res,
64
- status: error.status
65
- })
66
- next()
67
- }
68
- )
69
- }
70
-
71
- export { applyRoutes }
72
- `,
73
- file: `${projectName}/src/network/router.ts`
74
- },
75
- server: {
76
- content: `import { Server as HttpServer } from 'http'
77
- import express from 'express'
78
- import cors from 'cors'
79
- import pino, { HttpLogger } from 'express-pino-logger'
80
-
81
- import { dbConnection } from 'database'
82
- import { applyRoutes } from './router'
83
-
84
- const PORT = (process.env.PORT as string) || 1996
85
- const ENVIRONMENTS_WITHOUT_PRETTY_PRINT = ['production', 'ci']
86
-
87
- class Server {
88
- #app: express.Application
89
- #log: HttpLogger
90
- #server: HttpServer | undefined
91
- #connection: Awaited<ReturnType<typeof dbConnection>> | undefined
92
-
93
- constructor() {
94
- this.#app = express()
95
- this.#log = pino({
96
- transport: !ENVIRONMENTS_WITHOUT_PRETTY_PRINT.includes(
97
- process.env.NODE_ENV as string
98
- )
99
- ? {
100
- target: 'pino-pretty',
101
- options: {
102
- translateTime: 'HH:MM:ss Z',
103
- ignore: 'pid,hostname'
104
- }
105
- }
106
- : undefined
107
- })
108
- this.#config()
109
- }
110
-
111
- #config() {
112
- this.#app.use(cors())
113
- this.#app.use(express.json())
114
- this.#app.use(express.urlencoded({ extended: false }))
115
- this.#app.use(this.#log)
116
- this.#app.use(
117
- (
118
- req: express.Request,
119
- res: express.Response,
120
- next: express.NextFunction
121
- ) => {
122
- res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
123
- res.header('Access-Control-Allow-Origin', '*')
124
- res.header(
125
- 'Access-Control-Allow-Headers',
126
- 'Authorization, Content-Type'
127
- )
128
- res.header('x-powered-by', 'Simba.js')
129
- next()
130
- }
131
- )
132
- applyRoutes(this.#app)
133
- }
134
-
135
- async #dbConnection() {
136
- this.#connection = await dbConnection(this.#log.logger)
137
- }
138
-
139
- public async start(): Promise<void> {
140
- try {
141
- await this.#dbConnection()
142
- await this.#connection?.connect()
143
- this.#server = this.#app.listen(PORT, () => {
144
- this.#log.logger.info(\`Server running at port \${PORT}\`)
145
- })
146
- } catch (e) {
147
- this.#log.logger.error(e)
148
- }
149
- }
150
-
151
- public async stop(): Promise<void> {
152
- try {
153
- await this.#connection?.disconnect()
154
- this.#server?.close()
155
- } catch (e) {
156
- this.#log.logger.error(e)
157
- }
158
- }
159
- }
160
-
161
- const server = new Server()
162
-
163
- export { server as Server }
164
- `,
165
- file: `${projectName}/src/network/server.ts`
166
- },
167
- routes: {
168
- home: {
169
- content: `import { Response, Request, Router } from 'express'
170
-
171
- import { response } from 'network/response'
172
-
173
- const Home = Router()
174
-
175
- Home.route('').get((req: Request, res: Response) => {
176
- response({
177
- error: false,
178
- message: 'Welcome to your Express Backend!',
179
- res,
180
- status: 200
181
- })
182
- })
183
-
184
- export { Home }
185
- `,
186
- file: `${projectName}/src/network/routes/home.ts`
187
- },
188
- index: {
189
- content: `export * from './home'
190
- export * from './user'
191
- `,
192
- file: `${projectName}/src/network/routes/index.ts`
193
- },
194
- user: {
195
- content: `import { NextFunction, Router } from 'express'
196
-
197
- import { response } from 'network/response'
198
- import { UserService } from 'services'
199
- import { idSchema, storeUserDto, UserWithId } from 'schemas'
200
- import { validatorCompiler } from './utils'
201
-
202
- const User = Router()
203
-
204
- User.route('/users')
205
- .post(
206
- validatorCompiler(storeUserDto, 'body'),
207
- async (
208
- req: CustomRequest,
209
- res: CustomResponse,
210
- next: NextFunction
211
- ): Promise<void> => {
212
- try {
213
- const {
214
- body: { args: user }
215
- } = req
216
- const us = new UserService({ user })
217
- const result = await us.process({ type: 'store' })
218
-
219
- response({ error: false, message: result, res, status: 201 })
220
- } catch (error) {
221
- next(error)
222
- }
223
- }
224
- )
225
- .get(
226
- async (
227
- req: CustomRequest,
228
- res: CustomResponse,
229
- next: NextFunction
230
- ): Promise<void> => {
231
- try {
232
- const us = new UserService()
233
- const result = await us.process({ type: 'getAll' })
234
-
235
- response({ error: false, message: result, res, status: 200 })
236
- } catch (error) {
237
- next(error)
238
- }
239
- }
240
- )
241
- .delete(
242
- async (
243
- req: CustomRequest,
244
- res: CustomResponse,
245
- next: NextFunction
246
- ): Promise<void> => {
247
- try {
248
- const us = new UserService()
249
- const result = await us.process({ type: 'deleteAll' })
250
-
251
- response({ error: false, message: result, res, status: 200 })
252
- } catch (error) {
253
- next(error)
254
- }
255
- }
256
- )
257
-
258
- User.route('/user/:id')
259
- .get(
260
- validatorCompiler(idSchema, 'params'),
261
- async (
262
- req: CustomRequest,
263
- res: CustomResponse,
264
- next: NextFunction
265
- ): Promise<void> => {
266
- try {
267
- const {
268
- params: { id }
269
- } = req
270
- const us = new UserService({ id })
271
- const result = await us.process({ type: 'getOne' })
272
-
273
- response({ error: false, message: result, res, status: 200 })
274
- } catch (error) {
275
- next(error)
276
- }
277
- }
278
- )
279
- .patch(
280
- validatorCompiler(idSchema, 'params'),
281
- validatorCompiler(storeUserDto, 'body'),
282
- async (
283
- req: CustomRequest,
284
- res: CustomResponse,
285
- next: NextFunction
286
- ): Promise<void> => {
287
- try {
288
- const {
289
- body: { args },
290
- params: { id }
291
- } = req
292
- const userWithId = {
293
- id,
294
- ...args
295
- } as UserWithId
296
- const us = new UserService({ userWithId })
297
- const result = await us.process({ type: 'update' })
298
-
299
- response({ error: false, message: result, res, status: 200 })
300
- } catch (error) {
301
- next(error)
302
- }
303
- }
304
- )
305
- .delete(
306
- validatorCompiler(idSchema, 'params'),
307
- async (
308
- req: CustomRequest,
309
- res: CustomResponse,
310
- next: NextFunction
311
- ): Promise<void> => {
312
- try {
313
- const {
314
- params: { id }
315
- } = req
316
- const us = new UserService({ id })
317
- const result = await us.process({ type: 'delete' })
318
-
319
- response({ error: false, message: result, res, status: 200 })
320
- } catch (error) {
321
- next(error)
322
- }
323
- }
324
- )
325
-
326
- export { User }
327
- `,
328
- file: `${projectName}/src/network/routes/user.ts`
329
- },
330
- utils: {
331
- index: {
332
- content: `import { NextFunction } from 'express'
333
- import httpErrors from 'http-errors'
334
- import { TObject, TProperties } from '@sinclair/typebox'
335
- import Ajv from 'ajv'
336
-
337
- const ajv = new Ajv({
338
- removeAdditional: true,
339
- useDefaults: true,
340
- coerceTypes: true,
341
- nullable: true
342
- })
343
-
344
- type Middleware = (
345
- req: CustomRequest,
346
- res: CustomResponse,
347
- next: NextFunction
348
- ) => void
349
-
350
- const validatorCompiler = <T extends TProperties>(
351
- schema: TObject<T>,
352
- value: 'body' | 'params'
353
- ): Middleware => {
354
- return (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
355
- const validate = ajv.compile(schema)
356
- const ok = validate(req[value])
357
-
358
- if (!ok && validate.errors) {
359
- const [error] = validate.errors
360
- const errorMessage = \`\${error.dataPath.replace('.', '')} \${error.message}\`
361
-
362
- return next(new httpErrors.UnprocessableEntity(errorMessage))
363
- }
364
-
365
- next()
366
- }
367
- }
368
-
369
- export { validatorCompiler }
370
- `,
371
- file: `${projectName}/src/network/routes/utils/index.ts`
372
- }
373
- }
374
- }
375
- }
376
-
377
- await Promise.all([
378
- writeFile(network.response.file, network.response.content),
379
- writeFile(network.router.file, network.router.content),
380
- writeFile(network.server.file, network.server.content),
381
- writeFile(network.routes.home.file, network.routes.home.content),
382
- writeFile(network.routes.index.file, network.routes.index.content),
383
- writeFile(network.routes.user.file, network.routes.user.content),
384
- writeFile(
385
- network.routes.utils.index.file,
386
- network.routes.utils.index.content
387
- )
388
- ])
389
- }
390
-
391
- /**
392
- * @param {Object} args
393
- * @param {String} args.projectName
394
- */
395
- const expressGraphQLNetwork = async ({ projectName }) => {
396
- const createFoldersCommand = `mkdir ${projectName}/src/network/routes/utils \
397
- ${projectName}/src/graphQL/models \
398
- ${projectName}/src/graphQL/models/User \
399
- ${projectName}/src/graphQL/models/utils \
400
- ${projectName}/src/graphQL/models/utils/messages`
401
-
402
- if (platform() === 'win32')
403
- await exec(createFoldersCommand.replaceAll('/', '\\'))
404
- else await exec(createFoldersCommand)
405
-
406
- const graphQL = {
407
- index: {
408
- content: "export * from './models'\n",
409
- file: `${projectName}/src/graphQL/index.ts`
410
- },
411
- models: {
412
- User: {
413
- index: {
414
- content: `import { makeExecutableSchema } from '@graphql-tools/schema'
415
-
416
- import { User as UserTD } from './typeDefs'
417
- import { Query } from './queriesResolver'
418
- import { Mutation } from './mutationsResolver'
419
-
420
- const resolvers = {
421
- Query,
422
- Mutation
423
- }
424
-
425
- const User = makeExecutableSchema({
426
- typeDefs: UserTD,
427
- resolvers
428
- })
429
-
430
- export { User }
431
- `,
432
- file: `${projectName}/src/graphQL/models/User/index.ts`
433
- },
434
- mutations: {
435
- content: `import { ApolloError } from 'apollo-server-core'
436
-
437
- import { store, remove, update } from 'database'
438
- import { User, UserDTO, UserWithId } from 'schemas'
439
- import { EFU, MFU, GE, errorHandling } from '../utils'
440
-
441
- const storeUser = async (
442
- { user }: { user: User },
443
- { log }: Context
444
- ): Promise<UserDTO> => {
445
- try {
446
- const result = await store(user)
447
-
448
- return result
449
- } catch (e) {
450
- return errorHandling({
451
- e,
452
- message: GE.INTERNAL_SERVER_ERROR,
453
- code: 'INTERNAL_SERVER_ERROR',
454
- log
455
- })
456
- }
457
- }
458
-
459
- const deleteAllUsers = async ({ log }: Context): Promise<string> => {
460
- try {
461
- const usersDeleted = (await remove()) as number
462
-
463
- if (usersDeleted >= 1) return MFU.ALL_USERS_DELETED
464
-
465
- if (usersDeleted === 0)
466
- throw new ApolloError(EFU.NOTHING_TO_DELETE, 'NOTHING_TO_DELETE')
467
-
468
- throw new ApolloError(GE.INTERNAL_SERVER_ERROR, 'INTERNAL_SERVER_ERROR')
469
- } catch (e) {
470
- return errorHandling({
471
- e,
472
- message: GE.INTERNAL_SERVER_ERROR,
473
- code: 'INTERNAL_SERVER_ERROR',
474
- log
475
- })
476
- }
477
- }
478
-
479
- const updateUser = async (
480
- { user }: { user: UserWithId },
481
- { log }: Context
482
- ): Promise<UserDTO> => {
483
- try {
484
- const updatedUser = await update(user)
485
-
486
- if (!updatedUser) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
487
-
488
- return updatedUser
489
- } catch (e) {
490
- return errorHandling({
491
- e,
492
- message: GE.INTERNAL_SERVER_ERROR,
493
- code: 'INTERNAL_SERVER_ERROR',
494
- log
495
- })
496
- }
497
- }
498
-
499
- const deleteUser = async (
500
- { id }: { id: string },
501
- { log }: Context
502
- ): Promise<string> => {
503
- try {
504
- const deletedUser = await remove(id)
505
-
506
- if (!deletedUser) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
507
-
508
- return MFU.USER_DELETED
509
- } catch (e) {
510
- return errorHandling({
511
- e,
512
- message: GE.INTERNAL_SERVER_ERROR,
513
- code: 'INTERNAL_SERVER_ERROR',
514
- log
515
- })
516
- }
517
- }
518
-
519
- export { storeUser, deleteAllUsers, updateUser, deleteUser }
520
- `,
521
- file: `${projectName}/src/graphQL/models/User/mutations.ts`
522
- },
523
- mutationsResolver: {
524
- content: `import { DefinedError } from 'ajv'
525
- import { ApolloError } from 'apollo-server-core'
526
-
527
- import {
528
- ajv,
529
- idSchema,
530
- User,
531
- user as storeUserSchema,
532
- UserDTO,
533
- UserWithId,
534
- userWithId as updateUserSchema
535
- } from 'schemas'
536
- import { storeUser, updateUser, deleteUser, deleteAllUsers } from './mutations'
537
- import { errorHandling, GE } from '../utils'
538
-
539
- const Mutation = {
540
- storeUser: async (
541
- parent: unknown,
542
- { user }: { user: User },
543
- context: Context
544
- ): Promise<UserDTO> => {
545
- const { log } = context
546
- const validate = ajv.compile(storeUserSchema)
547
-
548
- try {
549
- const ok = validate(user)
550
-
551
- if (!ok)
552
- throw new ApolloError(
553
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
554
- '/',
555
- ''
556
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
557
- 'UNPROCESSABLE_ENTITY'
558
- )
559
-
560
- return await storeUser({ user }, context)
561
- } catch (e) {
562
- log.error(validate.errors)
563
-
564
- return errorHandling({
565
- e,
566
- message: GE.INTERNAL_SERVER_ERROR,
567
- code: 'INTERNAL_SERVER_ERROR',
568
- log
569
- })
570
- }
571
- },
572
- updateUser: async (
573
- parent: unknown,
574
- { user }: { user: UserWithId },
575
- context: Context
576
- ): Promise<UserDTO> => {
577
- const validate = ajv.compile(updateUserSchema)
578
- const { log } = context
579
-
580
- try {
581
- const ok = validate(user)
582
-
583
- if (!ok)
584
- throw new ApolloError(
585
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
586
- '/',
587
- ''
588
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
589
- 'UNPROCESSABLE_ENTITY'
590
- )
591
-
592
- return await updateUser({ user }, context)
593
- } catch (e) {
594
- log.error(validate.errors)
595
-
596
- return errorHandling({
597
- e,
598
- message: GE.INTERNAL_SERVER_ERROR,
599
- code: 'INTERNAL_SERVER_ERROR',
600
- log
601
- })
602
- }
603
- },
604
- deleteUser: async (
605
- parent: unknown,
606
- { id }: { id: string },
607
- context: Context
608
- ): Promise<string> => {
609
- const validate = ajv.compile(idSchema)
610
- const { log } = context
611
-
612
- try {
613
- const ok = validate({ id })
614
-
615
- if (!ok)
616
- throw new ApolloError(
617
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
618
- '/',
619
- ''
620
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
621
- 'UNPROCESSABLE_ENTITY'
622
- )
623
-
624
- return await deleteUser({ id }, context)
625
- } catch (e) {
626
- log.error(validate.errors)
627
-
628
- return errorHandling({
629
- e,
630
- message: GE.INTERNAL_SERVER_ERROR,
631
- code: 'INTERNAL_SERVER_ERROR',
632
- log
633
- })
634
- }
635
- },
636
- deleteAllUsers: async (
637
- parent: unknown,
638
- args: unknown,
639
- context: Context
640
- ): Promise<string> => {
641
- const { log } = context
642
- try {
643
- return await deleteAllUsers(context)
644
- } catch (e) {
645
- return errorHandling({
646
- e,
647
- message: GE.INTERNAL_SERVER_ERROR,
648
- code: 'INTERNAL_SERVER_ERROR',
649
- log
650
- })
651
- }
652
- }
653
- }
654
-
655
- export { Mutation }
656
- `,
657
- file: `${projectName}/src/graphQL/models/User/mutationsResolver.ts`
658
- },
659
- queries: {
660
- content: `import { ApolloError } from 'apollo-server-core'
661
-
662
- import { get } from 'database'
663
- import { UserDTO } from 'schemas'
664
- import { EFU, GE, errorHandling } from '../utils'
665
-
666
- const getUsers = async (
667
- parent: unknown,
668
- args: unknown,
669
- { log }: Context
670
- ): Promise<UserDTO[]> => {
671
- try {
672
- const users = (await get()) as UserDTO[]
673
-
674
- return users
675
- } catch (e) {
676
- return errorHandling({
677
- e,
678
- message: GE.INTERNAL_SERVER_ERROR,
679
- code: 'INTERNAL_SERVER_ERROR',
680
- log
681
- })
682
- }
683
- }
684
-
685
- const getUser = async (
686
- { id }: { id: string },
687
- { log }: Context
688
- ): Promise<UserDTO> => {
689
- try {
690
- const user = (await get(id as string)) as UserDTO | null
691
-
692
- if (!user) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
693
-
694
- return user
695
- } catch (e) {
696
- return errorHandling({
697
- e,
698
- message: GE.INTERNAL_SERVER_ERROR,
699
- code: 'INTERNAL_SERVER_ERROR',
700
- log
701
- })
702
- }
703
- }
704
-
705
- export { getUsers, getUser }
706
- `,
707
- file: `${projectName}/src/graphQL/models/User/queries.ts`
708
- },
709
- queriesResolver: {
710
- content: `import { DefinedError } from 'ajv'
711
- import { ApolloError } from 'apollo-server-core'
712
-
713
- import { ajv, idSchema, UserDTO } from 'schemas'
714
- import { getUser, getUsers } from './queries'
715
- import { errorHandling, GE } from '../utils'
716
-
717
- const Query = {
718
- getUser: async (
719
- parent: unknown,
720
- { id }: { id: string },
721
- context: Context
722
- ): Promise<UserDTO> => {
723
- const { log } = context
724
- const validate = ajv.compile(idSchema)
725
-
726
- try {
727
- const ok = validate({ id })
728
-
729
- if (!ok)
730
- throw new ApolloError(
731
- \`id \${(validate.errors as DefinedError[])[0].message as string}\`,
732
- 'UNPROCESSABLE_ENTITY'
733
- )
734
-
735
- return await getUser({ id }, context)
736
- } catch (e) {
737
- log.error(validate.errors)
738
-
739
- return errorHandling({
740
- e,
741
- message: GE.INTERNAL_SERVER_ERROR,
742
- code: 'INTERNAL_SERVER_ERROR',
743
- log
744
- })
745
- }
746
- },
747
- getUsers
748
- }
749
-
750
- export { Query }
751
- `,
752
- file: `${projectName}/src/graphQL/models/User/queriesResolver.ts`
753
- },
754
- typeDefs: {
755
- content: `import { gql } from 'apollo-server-core'
756
-
757
- const User = gql\`
758
- type User {
759
- id: ID!
760
- name: String!
761
- lastName: String!
762
- createdAt: String!
763
- updatedAt: String!
764
- }
765
-
766
- type Query {
767
- getUsers: [User!]!
768
- getUser(id: ID!): User!
769
- }
770
-
771
- input StoreUserInput {
772
- lastName: String!
773
- name: String!
774
- }
775
-
776
- input UpdateUserInput {
777
- id: String!
778
- lastName: String!
779
- name: String!
780
- }
781
-
782
- type Mutation {
783
- storeUser(user: StoreUserInput!): User!
784
- deleteAllUsers: String
785
- updateUser(user: UpdateUserInput!): User!
786
- deleteUser(id: ID!): String
787
- }
788
- \`
789
-
790
- export { User }
791
- `,
792
- file: `${projectName}/src/graphQL/models/User/typeDefs.ts`
793
- }
794
- },
795
- utils: {
796
- messages: {
797
- index: {
798
- content: `enum GenericErrors {
799
- INTERNAL_SERVER_ERROR = 'Something went wrong'
800
- }
801
-
802
- export { GenericErrors as GE }
803
- export * from './user'
804
- `,
805
- file: `${projectName}/src/graphQL/models/utils/messages/index.ts`
806
- },
807
- user: {
808
- content: `enum ErrorForUser {
809
- NOT_FOUND = 'The requested user does not exists',
810
- NOTHING_TO_DELETE = 'There is no user to be deleted'
811
- }
812
-
813
- enum MessageForUser {
814
- ALL_USERS_DELETED = 'All the users were deleted successfully',
815
- USER_DELETED = 'The requested user was successfully deleted'
816
- }
817
-
818
- export { ErrorForUser as EFU, MessageForUser as MFU }
819
- `,
820
- file: `${projectName}/src/graphQL/models/utils/messages/user.ts`
821
- }
822
- },
823
- index: {
824
- content: `import { ApolloError } from 'apollo-server-core'
825
- import { HttpLogger } from 'express-pino-logger'
826
-
827
- const errorHandling = ({
828
- e,
829
- message,
830
- code,
831
- log
832
- }: {
833
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
834
- e: any
835
- message: string
836
- code: string
837
- log: HttpLogger['logger']
838
- }): never => {
839
- log.error(e)
840
-
841
- if (e instanceof ApolloError) throw e
842
-
843
- throw new ApolloError(message ?? e.message, code)
844
- }
845
-
846
- export { errorHandling }
847
- export * from './messages'
848
- `,
849
- file: `${projectName}/src/graphQL/models/utils/index.ts`
850
- }
851
- },
852
- index: {
853
- content: `import { mergeSchemas } from '@graphql-tools/schema'
854
-
855
- import { User } from './User'
856
-
857
- const mergedSchema = mergeSchemas({
858
- schemas: [User]
859
- })
860
-
861
- export { mergedSchema }
862
- `,
863
- file: `${projectName}/src/graphQL/models/index.ts`
864
- }
865
- }
866
- }
867
-
868
- const network = {
869
- response: {
870
- content: `interface ResponseProps {
871
- error: boolean
872
- message: unknown
873
- res: CustomResponse
874
- status: number
875
- }
876
-
877
- const response = ({ error, message, res, status }: ResponseProps): void => {
878
- res.status(status).send({ error, message })
879
- }
880
-
881
- export { response }
882
- `,
883
- file: `${projectName}/src/network/response.ts`
884
- },
885
- router: {
886
- content: `import { Application, Response, Request, Router, NextFunction } from 'express'
887
- import swaggerUi from 'swagger-ui-express'
888
- import httpErrors from 'http-errors'
889
-
890
- import { response } from './response'
891
- import { Home } from './routes'
892
- import { docs } from 'utils'
893
-
894
- const routers: Router[] = []
895
- const applyRoutes = (app: Application): void => {
896
- app.use('/', Home)
897
- app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(docs))
898
- routers.forEach((router: Router): Application => app.use('/api', router))
899
-
900
- // Handling 404 error
901
- app.use((req, res, next) => {
902
- next(new httpErrors.NotFound('This route does not exists'))
903
- })
904
- app.use(
905
- (
906
- error: httpErrors.HttpError,
907
- req: Request,
908
- res: Response,
909
- next: NextFunction
910
- ) => {
911
- response({
912
- error: true,
913
- message: error.message,
914
- res,
915
- status: error.status
916
- })
917
- next()
918
- }
919
- )
920
- }
921
-
922
- export { applyRoutes }
923
- `,
924
- file: `${projectName}/src/network/router.ts`
925
- },
926
- server: {
927
- content: `import { createServer, Server as HttpServer } from 'http'
928
- import express from 'express'
929
- import cors from 'cors'
930
- import pino, { HttpLogger } from 'express-pino-logger'
931
- import { ApolloServer } from 'apollo-server-express'
932
- import {
933
- ApolloServerPluginDrainHttpServer,
934
- ApolloServerPluginLandingPageDisabled,
935
- ApolloServerPluginLandingPageGraphQLPlayground
936
- } from 'apollo-server-core'
937
-
938
- import { dbConnection } from 'database'
939
- import { mergedSchema as schema } from 'graphQL'
940
- import { applyRoutes } from './router'
941
-
942
- const PORT = (process.env.PORT as string) || 1996
943
- const ENVIRONMENTS_WITHOUT_PRETTY_PRINT = ['production', 'ci']
944
-
945
- class Server {
946
- #app: express.Application
947
- #log: HttpLogger
948
- #server: HttpServer
949
- #connection: Awaited<ReturnType<typeof dbConnection>> | undefined
950
-
951
- constructor() {
952
- this.#app = express()
953
- this.#server = createServer(this.#app)
954
- this.#log = pino({
955
- transport: !ENVIRONMENTS_WITHOUT_PRETTY_PRINT.includes(
956
- process.env.NODE_ENV as string
957
- )
958
- ? {
959
- target: 'pino-pretty',
960
- options: {
961
- translateTime: 'HH:MM:ss Z',
962
- ignore: 'pid,hostname'
963
- }
964
- }
965
- : undefined
966
- })
967
- this.#config()
968
- }
969
-
970
- #config() {
971
- this.#app.use(cors())
972
- this.#app.use(express.json())
973
- this.#app.use(express.urlencoded({ extended: false }))
974
- this.#app.use(this.#log)
975
- this.#app.use(
976
- (
977
- req: express.Request,
978
- res: express.Response,
979
- next: express.NextFunction
980
- ) => {
981
- res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
982
- res.header('Access-Control-Allow-Origin', '*')
983
- res.header(
984
- 'Access-Control-Allow-Headers',
985
- 'Authorization, Content-Type'
986
- )
987
- res.header('x-powered-by', 'Simba.js')
988
- next()
989
- }
990
- )
991
- }
992
-
993
- async #dbConnection() {
994
- this.#connection = await dbConnection(this.#log.logger)
995
- }
996
-
997
- public async start(): Promise<void> {
998
- const server = new ApolloServer({
999
- schema,
1000
- plugins: [
1001
- ApolloServerPluginDrainHttpServer({ httpServer: this.#server }),
1002
- process.env.NODE_ENV === 'production'
1003
- ? ApolloServerPluginLandingPageDisabled()
1004
- : ApolloServerPluginLandingPageGraphQLPlayground()
1005
- ],
1006
- context: (): Context => ({
1007
- log: this.#log.logger
1008
- })
1009
- })
1010
-
1011
- try {
1012
- await server.start()
1013
- server.applyMiddleware({
1014
- app: this.#app,
1015
- path: '/api'
1016
- })
1017
- applyRoutes(this.#app)
1018
- await this.#dbConnection()
1019
- await this.#connection?.connect()
1020
- this.#server.listen(PORT, () => {
1021
- this.#log.logger.info(\`Server listening at port: \${PORT}\`)
1022
- this.#log.logger.info(
1023
- \`GraphQL server listening at: http://localhost:\${PORT}\${server.graphqlPath}\`
1024
- )
1025
- })
1026
- } catch (e) {
1027
- console.error(e)
1028
- }
1029
- }
1030
-
1031
- public async stop(): Promise<void> {
1032
- try {
1033
- await this.#connection?.disconnect()
1034
- this.#server?.close()
1035
- } catch (e) {
1036
- this.#log.logger.error(e)
1037
- }
1038
- }
1039
- }
1040
-
1041
- const server = new Server()
1042
-
1043
- export { server as Server }
1044
- `,
1045
- file: `${projectName}/src/network/server.ts`
1046
- },
1047
- routes: {
1048
- home: {
1049
- content: `import { Response, Request, Router } from 'express'
1050
-
1051
- import { response } from 'network/response'
1052
-
1053
- const Home = Router()
1054
-
1055
- Home.route('').get((req: Request, res: Response) => {
1056
- response({
1057
- error: false,
1058
- message: 'Welcome to your GraphQL Express Backend!',
1059
- res,
1060
- status: 200
1061
- })
1062
- })
1063
-
1064
- export { Home }
1065
- `,
1066
- file: `${projectName}/src/network/routes/home.ts`
1067
- },
1068
- index: {
1069
- content: "export * from './home'\n",
1070
- file: `${projectName}/src/network/routes/index.ts`
1071
- },
1072
- utils: {
1073
- index: {
1074
- content: `import { NextFunction } from 'express'
1075
- import httpErrors from 'http-errors'
1076
- import { TObject, TProperties } from '@sinclair/typebox'
1077
- import Ajv from 'ajv'
1078
-
1079
- const ajv = new Ajv({
1080
- removeAdditional: true,
1081
- useDefaults: true,
1082
- coerceTypes: true,
1083
- nullable: true
1084
- })
1085
-
1086
- type Middleware = (
1087
- req: CustomRequest,
1088
- res: CustomResponse,
1089
- next: NextFunction
1090
- ) => void
1091
-
1092
- const validatorCompiler = <T extends TProperties>(
1093
- schema: TObject<T>,
1094
- value: 'body' | 'params'
1095
- ): Middleware => {
1096
- return (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
1097
- const validate = ajv.compile(schema)
1098
- const ok = validate(req[value])
1099
-
1100
- if (!ok && validate.errors) {
1101
- const [error] = validate.errors
1102
- const errorMessage = \`\${error.dataPath.replace('.', '')} \${error.message}\`
1103
-
1104
- return next(new httpErrors.UnprocessableEntity(errorMessage))
1105
- }
1106
-
1107
- next()
1108
- }
1109
- }
1110
-
1111
- export { validatorCompiler }
1112
- `,
1113
- file: `${projectName}/src/network/routes/utils/index.ts`
1114
- }
1115
- }
1116
- }
1117
- }
1118
-
1119
- await Promise.all([
1120
- writeFile(graphQL.index.file, graphQL.index.content),
1121
- writeFile(
1122
- graphQL.models.User.index.file,
1123
- graphQL.models.User.index.content
1124
- ),
1125
- writeFile(
1126
- graphQL.models.User.mutations.file,
1127
- graphQL.models.User.mutations.content
1128
- ),
1129
- writeFile(
1130
- graphQL.models.User.mutationsResolver.file,
1131
- graphQL.models.User.mutationsResolver.content
1132
- ),
1133
- writeFile(
1134
- graphQL.models.User.queries.file,
1135
- graphQL.models.User.queries.content
1136
- ),
1137
- writeFile(
1138
- graphQL.models.User.queriesResolver.file,
1139
- graphQL.models.User.queriesResolver.content
1140
- ),
1141
- writeFile(
1142
- graphQL.models.User.typeDefs.file,
1143
- graphQL.models.User.typeDefs.content
1144
- ),
1145
- writeFile(
1146
- graphQL.models.utils.messages.index.file,
1147
- graphQL.models.utils.messages.index.content
1148
- ),
1149
- writeFile(
1150
- graphQL.models.utils.messages.user.file,
1151
- graphQL.models.utils.messages.user.content
1152
- ),
1153
- writeFile(
1154
- graphQL.models.utils.index.file,
1155
- graphQL.models.utils.index.content
1156
- ),
1157
- writeFile(graphQL.models.index.file, graphQL.models.index.content),
1158
- writeFile(network.response.file, network.response.content),
1159
- writeFile(network.router.file, network.router.content),
1160
- writeFile(network.server.file, network.server.content),
1161
- writeFile(network.routes.home.file, network.routes.home.content),
1162
- writeFile(network.routes.index.file, network.routes.index.content),
1163
- writeFile(
1164
- network.routes.utils.index.file,
1165
- network.routes.utils.index.content
1166
- )
1167
- ])
1168
- }
1169
-
1170
- /**
1171
- * @param {Object} args
1172
- * @param {String} args.projectName
1173
- */
1174
- const fastifyRestNetwork = async ({ projectName }) => {
1175
- const createFoldersCommand = `mkdir ${projectName}/src/network/utils`
1176
-
1177
- if (platform() === 'win32')
1178
- await exec(createFoldersCommand.replaceAll('/', '\\'))
1179
- else await exec(createFoldersCommand)
1180
-
1181
- const network = {
1182
- response: {
1183
- content: `import { FastifyReply } from 'fastify'
1184
-
1185
- const response = ({
1186
- error,
1187
- message,
1188
- reply,
1189
- status
1190
- }: {
1191
- error: boolean
1192
- message: unknown
1193
- reply: FastifyReply
1194
- status: number
1195
- }): void => {
1196
- reply.code(status).send({ error, message })
1197
- }
1198
-
1199
- export { response }
1200
- `,
1201
- file: `${projectName}/src/network/response.ts`
1202
- },
1203
- router: {
1204
- content: `import { FastifyInstance } from 'fastify'
1205
- import { HttpError } from 'http-errors'
1206
-
1207
- import { response } from './response'
1208
- import { Home, User, Docs } from './routes'
1209
-
1210
- const routers = [Docs, User]
1211
- const applyRoutes = (app: FastifyInstance): void => {
1212
- Home(app)
1213
- routers.forEach(router => router(app))
1214
-
1215
- // Handling 404 error
1216
- app.setNotFoundHandler((request, reply) => {
1217
- response({
1218
- error: true,
1219
- message: 'This route does not exists',
1220
- reply,
1221
- status: 404
1222
- })
1223
- })
1224
- app.setErrorHandler<HttpError>((error, request, reply) => {
1225
- response({
1226
- error: true,
1227
- message: error.message,
1228
- reply,
1229
- status: error.status ?? 500
1230
- })
1231
- })
1232
- }
1233
-
1234
- export { applyRoutes }
1235
- `,
1236
- file: `${projectName}/src/network/router.ts`
1237
- },
1238
- server: {
1239
- content: `import fastify, { FastifyInstance } from 'fastify'
1240
-
1241
- import { dbConnection } from 'database'
1242
- import { applyRoutes } from './router'
1243
- import { validatorCompiler } from './utils'
1244
-
1245
- const PORT = process.env.PORT ?? 1996
1246
- const ENVIRONMENTS_WITHOUT_PRETTY_PRINT = ['production', 'ci']
1247
-
1248
- class Server {
1249
- #app: FastifyInstance
1250
- #connection: Awaited<ReturnType<typeof dbConnection>> | undefined
1251
-
1252
- constructor() {
1253
- this.#app = fastify({
1254
- logger: {
1255
- prettyPrint: !ENVIRONMENTS_WITHOUT_PRETTY_PRINT.includes(
1256
- process.env.NODE_ENV as string
1257
- )
1258
- }
1259
- })
1260
- this.#config()
1261
- }
1262
-
1263
- #config() {
1264
- this.#app.register(require('@fastify/cors'), {})
1265
- this.#app.addHook('preHandler', (req, reply, done) => {
1266
- reply.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
1267
- reply.header('Access-Control-Allow-Origin', '*')
1268
- reply.header(
1269
- 'Access-Control-Allow-Headers',
1270
- 'Authorization, Content-Type'
1271
- )
1272
- reply.header('x-powered-by', 'Simba.js')
1273
- done()
1274
- })
1275
- this.#app.setValidatorCompiler(validatorCompiler)
1276
- applyRoutes(this.#app)
1277
- }
1278
-
1279
- async #dbConnection() {
1280
- this.#connection = await dbConnection(this.#app.log)
1281
- }
1282
-
1283
- public async start(): Promise<void> {
1284
- try {
1285
- await this.#dbConnection()
1286
- await this.#connection?.connect()
1287
- await this.#app.listen(PORT)
1288
- } catch (e) {
1289
- console.error(e)
1290
- }
1291
- }
1292
-
1293
- public async stop(): Promise<void> {
1294
- try {
1295
- await this.#connection?.disconnect()
1296
- await this.#app.close()
1297
- } catch (e) {
1298
- console.error(e)
1299
- }
1300
- }
1301
- }
1302
-
1303
- const server = new Server()
1304
-
1305
- export { server as Server }
1306
- `,
1307
- file: `${projectName}/src/network/server.ts`
1308
- },
1309
- routes: {
1310
- docs: {
1311
- content: `import { FastifyInstance } from 'fastify'
1312
- import fastifySwagger from '@fastify/swagger'
1313
-
1314
- const Docs = (app: FastifyInstance, prefix = '/api'): void => {
1315
- app.register(fastifySwagger, {
1316
- routePrefix: \`\${prefix}/docs\`,
1317
- openapi: {
1318
- info: {
1319
- title: 'Test swagger',
1320
- description: 'Testing the Fastify swagger API',
1321
- version: '0.1.0',
1322
- contact: {
1323
- email: 'sluzquinosa@uni.pe'
1324
- },
1325
- license: {
1326
- name: 'MIT',
1327
- url: 'https://opensource.org/licenses/MIT'
1328
- }
1329
- },
1330
- servers: [
1331
- {
1332
- url: 'http://localhost:1996/api',
1333
- description: 'test-fastify local API'
1334
- }
1335
- ],
1336
- tags: [
1337
- {
1338
- name: 'user',
1339
- description: 'User related endpoints'
1340
- }
1341
- ]
1342
- },
1343
- exposeRoute: true
1344
- })
1345
- }
1346
-
1347
- export { Docs }
1348
- `,
1349
- file: `${projectName}/src/network/routes/docs.ts`
1350
- },
1351
- home: {
1352
- content: `import { FastifyInstance } from 'fastify'
1353
- import { response } from 'network/response'
1354
-
1355
- const Home = (app: FastifyInstance, prefix = '/'): void => {
1356
- app.get(\`\${prefix}\`, (request, reply) => {
1357
- response({
1358
- error: false,
1359
- message: 'Welcome to your Fastify Backend!',
1360
- reply,
1361
- status: 200
1362
- })
1363
- })
1364
- }
1365
-
1366
- export { Home }
1367
- `,
1368
- file: `${projectName}/src/network/routes/home.ts`
1369
- },
1370
- index: {
1371
- content: `export * from './home'
1372
- export * from './user'
1373
- export * from './docs'
1374
- `,
1375
- file: `${projectName}/src/network/routes/index.ts`
1376
- },
1377
- user: {
1378
- content: `import { FastifyInstance } from 'fastify'
1379
- import { Type } from '@sinclair/typebox'
1380
-
1381
- import { response } from 'network/response'
1382
- import {
1383
- userDto,
1384
- idSchema,
1385
- IdSchema,
1386
- storeUserDto,
1387
- StoreUserDTO
1388
- } from 'schemas'
1389
- import { UserService } from 'services'
1390
-
1391
- const User = (app: FastifyInstance, prefix = '/api'): void => {
1392
- app
1393
- .post<{ Body: StoreUserDTO }>(
1394
- \`\${prefix}/users\`,
1395
- {
1396
- schema: {
1397
- body: storeUserDto,
1398
- response: {
1399
- 200: {
1400
- error: {
1401
- type: 'boolean'
1402
- },
1403
- message: userDto
1404
- }
1405
- },
1406
- tags: ['user']
1407
- }
1408
- },
1409
- async (request, reply) => {
1410
- const {
1411
- body: {
1412
- args: { lastName, name }
1413
- }
1414
- } = request
1415
- const us = new UserService({
1416
- user: { lastName, name }
1417
- })
1418
- const user = await us.process({ type: 'store' })
1419
-
1420
- response({
1421
- error: false,
1422
- message: user,
1423
- reply,
1424
- status: 201
1425
- })
1426
- }
1427
- )
1428
- .get(
1429
- \`\${prefix}/users\`,
1430
- {
1431
- schema: {
1432
- response: {
1433
- 200: {
1434
- error: {
1435
- type: 'boolean'
1436
- },
1437
- message: Type.Array(userDto)
1438
- }
1439
- },
1440
- tags: ['user']
1441
- }
1442
- },
1443
- async (request, reply) => {
1444
- const us = new UserService()
1445
- const users = await us.process({ type: 'getAll' })
1446
-
1447
- response({
1448
- error: false,
1449
- message: users,
1450
- reply,
1451
- status: 200
1452
- })
1453
- }
1454
- )
1455
- .delete(
1456
- \`\${prefix}/users\`,
1457
- {
1458
- schema: {
1459
- response: {
1460
- 200: {
1461
- error: {
1462
- type: 'boolean'
1463
- },
1464
- message: {
1465
- type: 'string'
1466
- }
1467
- }
1468
- },
1469
- tags: ['user']
1470
- }
1471
- },
1472
- async (request, reply) => {
1473
- const us = new UserService()
1474
- const result = await us.process({ type: 'deleteAll' })
1475
-
1476
- response({
1477
- error: false,
1478
- message: result,
1479
- reply,
1480
- status: 200
1481
- })
1482
- }
1483
- )
1484
- .get<{ Params: IdSchema }>(
1485
- \`\${prefix}/user/:id\`,
1486
- {
1487
- schema: {
1488
- params: idSchema,
1489
- response: {
1490
- 200: {
1491
- error: {
1492
- type: 'boolean'
1493
- },
1494
- message: userDto
1495
- }
1496
- },
1497
- tags: ['user']
1498
- }
1499
- },
1500
- async (request, reply) => {
1501
- const {
1502
- params: { id }
1503
- } = request
1504
- const us = new UserService({ id })
1505
- const user = await us.process({ type: 'getOne' })
1506
-
1507
- response({
1508
- error: false,
1509
- message: user,
1510
- reply,
1511
- status: 200
1512
- })
1513
- }
1514
- )
1515
- .patch<{ Body: StoreUserDTO; Params: IdSchema }>(
1516
- \`\${prefix}/user/:id\`,
1517
- {
1518
- schema: {
1519
- body: storeUserDto,
1520
- params: idSchema,
1521
- response: {
1522
- 200: {
1523
- error: {
1524
- type: 'boolean'
1525
- },
1526
- message: userDto
1527
- }
1528
- },
1529
- tags: ['user']
1530
- }
1531
- },
1532
- async (request, reply) => {
1533
- const {
1534
- body: {
1535
- args: { name, lastName }
1536
- },
1537
- params: { id }
1538
- } = request
1539
- const us = new UserService({
1540
- userWithId: { name, lastName, id }
1541
- })
1542
- const user = await us.process({ type: 'update' })
1543
-
1544
- response({
1545
- error: false,
1546
- message: user,
1547
- reply,
1548
- status: 200
1549
- })
1550
- }
1551
- )
1552
- .delete<{ Params: IdSchema }>(
1553
- \`\${prefix}/user/:id\`,
1554
- {
1555
- schema: {
1556
- params: idSchema,
1557
- response: {
1558
- 200: {
1559
- error: {
1560
- type: 'boolean'
1561
- },
1562
- message: {
1563
- type: 'string'
1564
- }
1565
- }
1566
- },
1567
- tags: ['user']
1568
- }
1569
- },
1570
- async (request, reply) => {
1571
- const {
1572
- params: { id }
1573
- } = request
1574
- const us = new UserService({ id })
1575
- const result = await us.process({ type: 'delete' })
1576
-
1577
- response({
1578
- error: false,
1579
- message: result,
1580
- reply,
1581
- status: 200
1582
- })
1583
- }
1584
- )
1585
- }
1586
-
1587
- export { User }
1588
- `,
1589
- file: `${projectName}/src/network/routes/user.ts`
1590
- }
1591
- },
1592
- utils: {
1593
- index: {
1594
- content: `/* eslint-disable @typescript-eslint/no-explicit-any */
1595
- import {
1596
- FastifyRouteSchemaDef,
1597
- FastifyValidationResult
1598
- } from 'fastify/types/schema'
1599
- import httpErrors from 'http-errors'
1600
- import Ajv from 'ajv'
1601
-
1602
- const ajv = new Ajv({
1603
- removeAdditional: true,
1604
- useDefaults: true,
1605
- coerceTypes: true,
1606
- nullable: true
1607
- })
1608
-
1609
- const validatorCompiler = ({
1610
- schema
1611
- }: FastifyRouteSchemaDef<any>): FastifyValidationResult => {
1612
- const validate = ajv.compile(schema)
1613
-
1614
- return (data: unknown): boolean => {
1615
- const ok = validate(data)
1616
-
1617
- if (!ok && validate.errors) {
1618
- const [error] = validate.errors
1619
- const errorMessage = \`\${error.dataPath.replace('.', '')} \${error.message}\`
1620
-
1621
- throw new httpErrors.UnprocessableEntity(errorMessage)
1622
- }
1623
-
1624
- return true
1625
- }
1626
- }
1627
-
1628
- export { validatorCompiler }
1629
- `,
1630
- file: `${projectName}/src/network/utils/index.ts`
1631
- }
1632
- }
1633
- }
1634
-
1635
- await Promise.all([
1636
- writeFile(network.response.file, network.response.content),
1637
- writeFile(network.router.file, network.router.content),
1638
- writeFile(network.server.file, network.server.content),
1639
- writeFile(network.routes.docs.file, network.routes.docs.content),
1640
- writeFile(network.routes.home.file, network.routes.home.content),
1641
- writeFile(network.routes.index.file, network.routes.index.content),
1642
- writeFile(network.routes.user.file, network.routes.user.content),
1643
- writeFile(network.utils.index.file, network.utils.index.content)
1644
- ])
1645
- }
1646
-
1647
- /**
1648
- * @param {Object} args
1649
- * @param {String} args.projectName
1650
- */
1651
- const fastifyGraphQLNetwork = async ({ projectName }) => {
1652
- const createFoldersCommand = `mkdir ${projectName}/src/graphQL/models \
1653
- ${projectName}/src/graphQL/models/User \
1654
- ${projectName}/src/graphQL/models/utils \
1655
- ${projectName}/src/graphQL/models/utils/messages`
1656
-
1657
- if (platform() === 'win32')
1658
- await exec(createFoldersCommand.replaceAll('/', '\\'))
1659
- else await exec(createFoldersCommand)
1660
-
1661
- const graphQL = {
1662
- index: {
1663
- content: "export * from './models'\n",
1664
- file: `${projectName}/src/graphQL/index.ts`
1665
- },
1666
- models: {
1667
- User: {
1668
- index: {
1669
- content: `import { makeExecutableSchema } from '@graphql-tools/schema'
1670
-
1671
- import { User as UserTD } from './typeDefs'
1672
- import { Query } from './queriesResolver'
1673
- import { Mutation } from './mutationsResolver'
1674
-
1675
- const resolvers = {
1676
- Query,
1677
- Mutation
1678
- }
1679
-
1680
- const User = makeExecutableSchema({
1681
- typeDefs: UserTD,
1682
- resolvers
1683
- })
1684
-
1685
- export { User }
1686
- `,
1687
- file: `${projectName}/src/graphQL/models/User/index.ts`
1688
- },
1689
- mutations: {
1690
- content: `import { ApolloError } from 'apollo-server-core'
1691
-
1692
- import { store, remove, update } from 'database'
1693
- import { User, UserDTO, UserWithId } from 'schemas'
1694
- import { EFU, MFU, GE, errorHandling } from '../utils'
1695
-
1696
- const storeUser = async (
1697
- { user }: { user: User },
1698
- { log }: Context
1699
- ): Promise<UserDTO> => {
1700
- try {
1701
- const result = await store(user)
1702
-
1703
- return result
1704
- } catch (e) {
1705
- return errorHandling({
1706
- e,
1707
- message: GE.INTERNAL_SERVER_ERROR,
1708
- code: 'INTERNAL_SERVER_ERROR',
1709
- log
1710
- })
1711
- }
1712
- }
1713
-
1714
- const deleteAllUsers = async ({ log }: Context): Promise<string> => {
1715
- try {
1716
- const usersDeleted = (await remove()) as number
1717
-
1718
- if (usersDeleted >= 1) return MFU.ALL_USERS_DELETED
1719
-
1720
- if (usersDeleted === 0)
1721
- throw new ApolloError(EFU.NOTHING_TO_DELETE, 'NOTHING_TO_DELETE')
1722
-
1723
- throw new ApolloError(GE.INTERNAL_SERVER_ERROR, 'INTERNAL_SERVER_ERROR')
1724
- } catch (e) {
1725
- return errorHandling({
1726
- e,
1727
- message: GE.INTERNAL_SERVER_ERROR,
1728
- code: 'INTERNAL_SERVER_ERROR',
1729
- log
1730
- })
1731
- }
1732
- }
1733
-
1734
- const updateUser = async (
1735
- { user }: { user: UserWithId },
1736
- { log }: Context
1737
- ): Promise<UserDTO> => {
1738
- try {
1739
- const updatedUser = await update(user)
1740
-
1741
- if (!updatedUser) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
1742
-
1743
- return updatedUser
1744
- } catch (e) {
1745
- return errorHandling({
1746
- e,
1747
- message: GE.INTERNAL_SERVER_ERROR,
1748
- code: 'INTERNAL_SERVER_ERROR',
1749
- log
1750
- })
1751
- }
1752
- }
1753
-
1754
- const deleteUser = async (
1755
- { id }: { id: string },
1756
- { log }: Context
1757
- ): Promise<string> => {
1758
- try {
1759
- const deletedUser = await remove(id)
1760
-
1761
- if (!deletedUser) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
1762
-
1763
- return MFU.USER_DELETED
1764
- } catch (e) {
1765
- return errorHandling({
1766
- e,
1767
- message: GE.INTERNAL_SERVER_ERROR,
1768
- code: 'INTERNAL_SERVER_ERROR',
1769
- log
1770
- })
1771
- }
1772
- }
1773
-
1774
- export { storeUser, deleteAllUsers, updateUser, deleteUser }
1775
- `,
1776
- file: `${projectName}/src/graphQL/models/User/mutations.ts`
1777
- },
1778
- mutationsResolver: {
1779
- content: `import { DefinedError } from 'ajv'
1780
- import { ApolloError } from 'apollo-server-core'
1781
-
1782
- import {
1783
- ajv,
1784
- idSchema,
1785
- User,
1786
- user as storeUserSchema,
1787
- UserDTO,
1788
- UserWithId,
1789
- userWithId as updateUserSchema
1790
- } from 'schemas'
1791
- import { storeUser, updateUser, deleteUser, deleteAllUsers } from './mutations'
1792
- import { errorHandling, GE } from '../utils'
1793
-
1794
- const Mutation = {
1795
- storeUser: async (
1796
- parent: unknown,
1797
- { user }: { user: User },
1798
- context: Context
1799
- ): Promise<UserDTO> => {
1800
- const { log } = context
1801
- const validate = ajv.compile(storeUserSchema)
1802
-
1803
- try {
1804
- const ok = validate(user)
1805
-
1806
- if (!ok)
1807
- throw new ApolloError(
1808
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
1809
- '/',
1810
- ''
1811
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
1812
- 'UNPROCESSABLE_ENTITY'
1813
- )
1814
-
1815
- return await storeUser({ user }, context)
1816
- } catch (e) {
1817
- log.error(validate.errors)
1818
-
1819
- return errorHandling({
1820
- e,
1821
- message: GE.INTERNAL_SERVER_ERROR,
1822
- code: 'INTERNAL_SERVER_ERROR',
1823
- log
1824
- })
1825
- }
1826
- },
1827
- updateUser: async (
1828
- parent: unknown,
1829
- { user }: { user: UserWithId },
1830
- context: Context
1831
- ): Promise<UserDTO> => {
1832
- const validate = ajv.compile(updateUserSchema)
1833
- const { log } = context
1834
-
1835
- try {
1836
- const ok = validate(user)
1837
-
1838
- if (!ok)
1839
- throw new ApolloError(
1840
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
1841
- '/',
1842
- ''
1843
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
1844
- 'UNPROCESSABLE_ENTITY'
1845
- )
1846
-
1847
- return await updateUser({ user }, context)
1848
- } catch (e) {
1849
- log.error(validate.errors)
1850
-
1851
- return errorHandling({
1852
- e,
1853
- message: GE.INTERNAL_SERVER_ERROR,
1854
- code: 'INTERNAL_SERVER_ERROR',
1855
- log
1856
- })
1857
- }
1858
- },
1859
- deleteUser: async (
1860
- parent: unknown,
1861
- { id }: { id: string },
1862
- context: Context
1863
- ): Promise<string> => {
1864
- const validate = ajv.compile(idSchema)
1865
- const { log } = context
1866
-
1867
- try {
1868
- const ok = validate({ id })
1869
-
1870
- if (!ok)
1871
- throw new ApolloError(
1872
- \`\${(validate.errors as DefinedError[])[0].instancePath.replace(
1873
- '/',
1874
- ''
1875
- )} \${(validate.errors as DefinedError[])[0].message as string}\`,
1876
- 'UNPROCESSABLE_ENTITY'
1877
- )
1878
-
1879
- return await deleteUser({ id }, context)
1880
- } catch (e) {
1881
- log.error(validate.errors)
1882
-
1883
- return errorHandling({
1884
- e,
1885
- message: GE.INTERNAL_SERVER_ERROR,
1886
- code: 'INTERNAL_SERVER_ERROR',
1887
- log
1888
- })
1889
- }
1890
- },
1891
- deleteAllUsers: async (
1892
- parent: unknown,
1893
- args: unknown,
1894
- context: Context
1895
- ): Promise<string> => {
1896
- const { log } = context
1897
- try {
1898
- return await deleteAllUsers(context)
1899
- } catch (e) {
1900
- return errorHandling({
1901
- e,
1902
- message: GE.INTERNAL_SERVER_ERROR,
1903
- code: 'INTERNAL_SERVER_ERROR',
1904
- log
1905
- })
1906
- }
1907
- }
1908
- }
1909
-
1910
- export { Mutation }
1911
- `,
1912
- file: `${projectName}/src/graphQL/models/User/mutationsResolver.ts`
1913
- },
1914
- queries: {
1915
- content: `import { ApolloError } from 'apollo-server-core'
1916
-
1917
- import { get } from 'database'
1918
- import { UserDTO } from 'schemas'
1919
- import { EFU, GE, errorHandling } from '../utils'
1920
-
1921
- const getUsers = async (
1922
- parent: unknown,
1923
- args: unknown,
1924
- { log }: Context
1925
- ): Promise<UserDTO[]> => {
1926
- try {
1927
- const users = (await get()) as UserDTO[]
1928
-
1929
- return users
1930
- } catch (e) {
1931
- return errorHandling({
1932
- e,
1933
- message: GE.INTERNAL_SERVER_ERROR,
1934
- code: 'INTERNAL_SERVER_ERROR',
1935
- log
1936
- })
1937
- }
1938
- }
1939
-
1940
- const getUser = async (
1941
- { id }: { id: string },
1942
- { log }: Context
1943
- ): Promise<UserDTO> => {
1944
- try {
1945
- const user = (await get(id as string)) as UserDTO | null
1946
-
1947
- if (!user) throw new ApolloError(EFU.NOT_FOUND, 'NOT_FOUND')
1948
-
1949
- return user
1950
- } catch (e) {
1951
- return errorHandling({
1952
- e,
1953
- message: GE.INTERNAL_SERVER_ERROR,
1954
- code: 'INTERNAL_SERVER_ERROR',
1955
- log
1956
- })
1957
- }
1958
- }
1959
-
1960
- export { getUsers, getUser }
1961
- `,
1962
- file: `${projectName}/src/graphQL/models/User/queries.ts`
1963
- },
1964
- queriesResolver: {
1965
- content: `import { DefinedError } from 'ajv'
1966
- import { ApolloError } from 'apollo-server-core'
1967
-
1968
- import { ajv, idSchema, UserDTO } from 'schemas'
1969
- import { getUser, getUsers } from './queries'
1970
- import { errorHandling, GE } from '../utils'
1971
-
1972
- const Query = {
1973
- getUser: async (
1974
- parent: unknown,
1975
- { id }: { id: string },
1976
- context: Context
1977
- ): Promise<UserDTO> => {
1978
- const { log } = context
1979
- const validate = ajv.compile(idSchema)
1980
-
1981
- try {
1982
- const ok = validate({ id })
1983
-
1984
- if (!ok)
1985
- throw new ApolloError(
1986
- \`id \${(validate.errors as DefinedError[])[0].message as string}\`,
1987
- 'UNPROCESSABLE_ENTITY'
1988
- )
1989
-
1990
- return await getUser({ id }, context)
1991
- } catch (e) {
1992
- log.error(validate.errors)
1993
-
1994
- return errorHandling({
1995
- e,
1996
- message: GE.INTERNAL_SERVER_ERROR,
1997
- code: 'INTERNAL_SERVER_ERROR',
1998
- log
1999
- })
2000
- }
2001
- },
2002
- getUsers
2003
- }
2004
-
2005
- export { Query }
2006
- `,
2007
- file: `${projectName}/src/graphQL/models/User/queriesResolver.ts`
2008
- },
2009
- typeDefs: {
2010
- content: `import { gql } from 'apollo-server-core'
2011
-
2012
- const User = gql\`
2013
- type User {
2014
- id: ID!
2015
- name: String!
2016
- lastName: String!
2017
- createdAt: String!
2018
- updatedAt: String!
2019
- }
2020
-
2021
- type Query {
2022
- getUsers: [User!]!
2023
- getUser(id: ID!): User!
2024
- }
2025
-
2026
- input StoreUserInput {
2027
- lastName: String!
2028
- name: String!
2029
- }
2030
-
2031
- input UpdateUserInput {
2032
- id: String!
2033
- lastName: String!
2034
- name: String!
2035
- }
2036
-
2037
- type Mutation {
2038
- storeUser(user: StoreUserInput!): User!
2039
- deleteAllUsers: String
2040
- updateUser(user: UpdateUserInput!): User!
2041
- deleteUser(id: ID!): String
2042
- }
2043
- \`
2044
-
2045
- export { User }
2046
- `,
2047
- file: `${projectName}/src/graphQL/models/User/typeDefs.ts`
2048
- }
2049
- },
2050
- utils: {
2051
- messages: {
2052
- index: {
2053
- content: `enum GenericErrors {
2054
- INTERNAL_SERVER_ERROR = 'Something went wrong'
2055
- }
2056
-
2057
- export { GenericErrors as GE }
2058
- export * from './user'
2059
- `,
2060
- file: `${projectName}/src/graphQL/models/utils/messages/index.ts`
2061
- },
2062
- user: {
2063
- content: `enum ErrorForUser {
2064
- NOT_FOUND = 'The requested user does not exists',
2065
- NOTHING_TO_DELETE = 'There is no user to be deleted'
2066
- }
2067
-
2068
- enum MessageForUser {
2069
- ALL_USERS_DELETED = 'All the users were deleted successfully',
2070
- USER_DELETED = 'The requested user was successfully deleted'
2071
- }
2072
-
2073
- export { ErrorForUser as EFU, MessageForUser as MFU }
2074
- `,
2075
- file: `${projectName}/src/graphQL/models/utils/messages/user.ts`
2076
- }
2077
- },
2078
- index: {
2079
- content: `import { ApolloError } from 'apollo-server-core'
2080
- import { FastifyLoggerInstance } from 'fastify'
2081
-
2082
- const errorHandling = ({
2083
- e,
2084
- message,
2085
- code,
2086
- log
2087
- }: {
2088
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2089
- e: any
2090
- message: string
2091
- code: string
2092
- log: FastifyLoggerInstance
2093
- }): never => {
2094
- log.error(e)
2095
-
2096
- if (e instanceof ApolloError) throw e
2097
-
2098
- throw new ApolloError(message ?? e.message, code)
2099
- }
2100
-
2101
- export { errorHandling }
2102
- export * from './messages'
2103
- `,
2104
- file: `${projectName}/src/graphQL/models/utils/index.ts`
2105
- }
2106
- },
2107
- index: {
2108
- content: `import { mergeSchemas } from '@graphql-tools/schema'
2109
-
2110
- import { User } from './User'
2111
-
2112
- const mergedSchema = mergeSchemas({
2113
- schemas: [User]
2114
- })
2115
-
2116
- export { mergedSchema }
2117
- `,
2118
- file: `${projectName}/src/graphQL/models/index.ts`
2119
- }
2120
- }
2121
- }
2122
- const network = {
2123
- response: {
2124
- content: `import { FastifyReply } from 'fastify'
2125
-
2126
- const response = ({
2127
- error,
2128
- message,
2129
- reply,
2130
- status
2131
- }: {
2132
- error: boolean
2133
- message: unknown
2134
- reply: FastifyReply
2135
- status: number
2136
- }): void => {
2137
- reply.code(status).send({ error, message })
2138
- }
2139
-
2140
- export { response }
2141
- `,
2142
- file: `${projectName}/src/network/response.ts`
2143
- },
2144
- router: {
2145
- content: `import { FastifyInstance } from 'fastify'
2146
- import { HttpError } from 'http-errors'
2147
-
2148
- import { response } from './response'
2149
- import { Home, Docs } from './routes'
2150
-
2151
- const routers = [Docs]
2152
- const applyRoutes = (app: FastifyInstance): void => {
2153
- Home(app)
2154
- routers.forEach(router => router(app))
2155
-
2156
- // Handling 404 error
2157
- app.setNotFoundHandler((request, reply) => {
2158
- response({
2159
- error: true,
2160
- message: 'This route does not exists',
2161
- reply,
2162
- status: 404
2163
- })
2164
- })
2165
- app.setErrorHandler<HttpError>((error, request, reply) => {
2166
- response({
2167
- error: true,
2168
- message: error.message,
2169
- reply,
2170
- status: error.status ?? 500
2171
- })
2172
- })
2173
- }
2174
-
2175
- export { applyRoutes }
2176
- `,
2177
- file: `${projectName}/src/network/router.ts`
2178
- },
2179
- server: {
2180
- content: `import fastify, { FastifyInstance } from 'fastify'
2181
- import { ApolloServer } from 'apollo-server-fastify'
2182
- import {
2183
- ApolloServerPluginDrainHttpServer,
2184
- ApolloServerPluginLandingPageGraphQLPlayground,
2185
- ApolloServerPluginLandingPageDisabled
2186
- } from 'apollo-server-core'
2187
- import { ApolloServerPlugin } from 'apollo-server-plugin-base'
2188
- import { dbConnection } from 'database'
2189
-
2190
- import { mergedSchema as schema } from 'graphQL'
2191
- import { applyRoutes } from './router'
2192
-
2193
- const PORT = process.env.PORT ?? 1996
2194
- const ENVIRONMENTS_WITHOUT_PRETTY_PRINT = ['production', 'ci']
2195
-
2196
- class Server {
2197
- #app: FastifyInstance
2198
- #connection: Awaited<ReturnType<typeof dbConnection>> | undefined
2199
-
2200
- constructor() {
2201
- this.#app = fastify({
2202
- logger: {
2203
- prettyPrint: !ENVIRONMENTS_WITHOUT_PRETTY_PRINT.includes(
2204
- process.env.NODE_ENV as string
2205
- )
2206
- }
2207
- })
2208
- this.#config()
2209
- }
2210
-
2211
- #config() {
2212
- this.#app.addHook('preHandler', (req, reply, done) => {
2213
- reply.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
2214
- reply.header('Access-Control-Allow-Origin', '*')
2215
- reply.header(
2216
- 'Access-Control-Allow-Headers',
2217
- 'Authorization, Content-Type'
2218
- )
2219
- reply.header('x-powered-by', 'Simba.js')
2220
- done()
2221
- })
2222
- applyRoutes(this.#app)
2223
- }
2224
-
2225
- async #dbConnection() {
2226
- this.#connection = await dbConnection(this.#app.log)
2227
- }
2228
-
2229
- #fastifyAppClosePlugin(): ApolloServerPlugin {
2230
- const app = this.#app
2231
-
2232
- return {
2233
- async serverWillStart() {
2234
- return {
2235
- async drainServer() {
2236
- await app.close()
2237
- }
2238
- }
2239
- }
2240
- }
2241
- }
2242
-
2243
- public async start(): Promise<void> {
2244
- const server = new ApolloServer({
2245
- schema,
2246
- plugins: [
2247
- this.#fastifyAppClosePlugin(),
2248
- ApolloServerPluginDrainHttpServer({ httpServer: this.#app.server }),
2249
- process.env.NODE_ENV === 'production'
2250
- ? ApolloServerPluginLandingPageDisabled()
2251
- : ApolloServerPluginLandingPageGraphQLPlayground()
2252
- ],
2253
- context: (): Context => ({
2254
- log: this.#app.log
2255
- })
2256
- })
2257
-
2258
- try {
2259
- await server.start()
2260
- this.#app.register(
2261
- server.createHandler({
2262
- path: '/api'
2263
- })
2264
- )
2265
- await this.#dbConnection()
2266
- await this.#connection?.connect()
2267
- await this.#app.listen(PORT)
2268
- this.#app.log.info(
2269
- \`GraphQL server listening at: http://localhost:\${PORT}\${server.graphqlPath}\`
2270
- )
2271
- } catch (e) {
2272
- console.error(e)
2273
- }
2274
- }
2275
-
2276
- public async stop(): Promise<void> {
2277
- try {
2278
- await this.#connection?.disconnect()
2279
- await this.#app.close()
2280
- } catch (e) {
2281
- console.error(e)
2282
- }
2283
- }
2284
- }
2285
-
2286
- const server = new Server()
2287
-
2288
- export { server as Server }
2289
- `,
2290
- file: `${projectName}/src/network/server.ts`
2291
- },
2292
- routes: {
2293
- docs: {
2294
- content: `import { FastifyInstance } from 'fastify'
2295
- import fastifySwagger from '@fastify/swagger'
2296
-
2297
- const Docs = (app: FastifyInstance, prefix = '/api'): void => {
2298
- app.register(fastifySwagger, {
2299
- routePrefix: \`\${prefix}/docs\`,
2300
- openapi: {
2301
- info: {
2302
- title: 'Test swagger',
2303
- description: 'Testing the Fastify swagger API',
2304
- version: '0.1.0',
2305
- contact: {
2306
- email: 'sluzquinosa@uni.pe'
2307
- },
2308
- license: {
2309
- name: 'MIT',
2310
- url: 'https://opensource.org/licenses/MIT'
2311
- }
2312
- },
2313
- servers: [
2314
- {
2315
- url: 'http://localhost:1996/api',
2316
- description: 'test-fastify local API'
2317
- }
2318
- ],
2319
- tags: [
2320
- {
2321
- name: 'user',
2322
- description: 'User related endpoints'
2323
- }
2324
- ]
2325
- },
2326
- exposeRoute: true
2327
- })
2328
- }
2329
-
2330
- export { Docs }
2331
- `,
2332
- file: `${projectName}/src/network/routes/docs.ts`
2333
- },
2334
- home: {
2335
- content: `import { FastifyInstance } from 'fastify'
2336
- import { response } from 'network/response'
2337
-
2338
- const Home = (app: FastifyInstance, prefix = '/'): void => {
2339
- app.get(\`\${prefix}\`, (request, reply) => {
2340
- response({
2341
- error: false,
2342
- message: 'Welcome to your Fastify GraphQL Backend!',
2343
- reply,
2344
- status: 200
2345
- })
2346
- })
2347
- }
2348
-
2349
- export { Home }
2350
- `,
2351
- file: `${projectName}/src/network/routes/home.ts`
2352
- },
2353
- index: {
2354
- content: `export * from './home'
2355
- export * from './docs'
2356
- `,
2357
- file: `${projectName}/src/network/routes/index.ts`
2358
- }
2359
- }
2360
- }
2361
-
2362
- await Promise.all([
2363
- writeFile(graphQL.index.file, graphQL.index.content),
2364
- writeFile(
2365
- graphQL.models.User.index.file,
2366
- graphQL.models.User.index.content
2367
- ),
2368
- writeFile(
2369
- graphQL.models.User.mutations.file,
2370
- graphQL.models.User.mutations.content
2371
- ),
2372
- writeFile(
2373
- graphQL.models.User.mutationsResolver.file,
2374
- graphQL.models.User.mutationsResolver.content
2375
- ),
2376
- writeFile(
2377
- graphQL.models.User.queries.file,
2378
- graphQL.models.User.queries.content
2379
- ),
2380
- writeFile(
2381
- graphQL.models.User.queriesResolver.file,
2382
- graphQL.models.User.queriesResolver.content
2383
- ),
2384
- writeFile(
2385
- graphQL.models.User.typeDefs.file,
2386
- graphQL.models.User.typeDefs.content
2387
- ),
2388
- writeFile(
2389
- graphQL.models.utils.messages.index.file,
2390
- graphQL.models.utils.messages.index.content
2391
- ),
2392
- writeFile(
2393
- graphQL.models.utils.messages.user.file,
2394
- graphQL.models.utils.messages.user.content
2395
- ),
2396
- writeFile(
2397
- graphQL.models.utils.index.file,
2398
- graphQL.models.utils.index.content
2399
- ),
2400
- writeFile(graphQL.models.index.file, graphQL.models.index.content),
2401
- writeFile(network.response.file, network.response.content),
2402
- writeFile(network.router.file, network.router.content),
2403
- writeFile(network.server.file, network.server.content),
2404
- writeFile(network.routes.docs.file, network.routes.docs.content),
2405
- writeFile(network.routes.home.file, network.routes.home.content),
2406
- writeFile(network.routes.index.file, network.routes.index.content)
2407
- ])
2408
- }
2409
-
2410
- /**
2411
- * @param {Object} args
2412
- * @param {Boolean} args.fastify
2413
- * @param {String} args.projectName
2414
- * @param {Boolean} args.graphql
2415
- */
2416
- module.exports = async ({ fastify, projectName, graphql }) => {
2417
- const createFoldersCommand = `mkdir ${projectName}/src/network \
2418
- ${projectName}/src/network/routes ${
2419
- graphql ? `${projectName}/src/graphQL` : ''
2420
- }`
2421
-
2422
- if (platform() === 'win32')
2423
- await exec(createFoldersCommand.replaceAll('/', '\\'))
2424
- else await exec(createFoldersCommand)
2425
-
2426
- const network = {
2427
- index: {
2428
- content: `export * from './routes'
2429
- export * from './server'
2430
- `,
2431
- file: `${projectName}/src/network/index.ts`
2432
- }
2433
- }
2434
-
2435
- await writeFile(network.index.file, network.index.content)
2436
-
2437
- if (fastify && graphql) return await fastifyGraphQLNetwork({ projectName })
2438
-
2439
- if (fastify) return await fastifyRestNetwork({ projectName })
2440
-
2441
- if (graphql) return await expressGraphQLNetwork({ projectName })
2442
-
2443
- return await expressRestNetwork({ projectName })
2444
- }