@anthonylzq/simba.js 5.1.1 → 5.2.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/README.md +2 -0
- package/lib/src/functions/api/database.js +6 -5
- package/lib/src/functions/api/network.js +121 -108
- package/lib/src/functions/api/schemas.js +18 -29
- package/lib/src/functions/api/services.js +7 -9
- package/lib/src/functions/api/types.js +0 -1
- package/lib/src/functions/docker.js +20 -4
- package/lib/src/index.js +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://github.com/AnthonyLzq/simba.js/blob/master/LICENSE)
|
|
8
8
|
[](https://standardjs.com)
|
|
9
9
|
[](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
|
10
|
+
[](https://github.com/AnthonyLzq/simba.js/actions/workflows/lint.yml)
|
|
10
11
|
|
|
11
12
|
Set up a modern backend app by running one command. This project has the goal to create a complete setup for a backend application using `TypeScript` and `Express` or `Fastify`. It will create many files that are usually created manually. Think about Simba.js like a [CRA](https://create-react-app.dev/), but for backend development. Check the [**project structure**](#project-structure) for more information.
|
|
12
13
|
|
|
@@ -442,6 +443,7 @@ If you want to check the content of the files, please check the [example](https:
|
|
|
442
443
|
- If you provide a project name that contains spaces, something like 'My awesome Project', every space will be replaced with a hyphen. So at the end your project name will be 'My-awesome-project', but in its README.md file, the hyphens will be removed and the project name will be parsed to title case (My Awesome Project).
|
|
443
444
|
- Finally, `git` will be initialized and a list of libraries will be installed. Check the [**notes**](#notes).
|
|
444
445
|
- Relative imports is already configured, you do not need to import a file using `../../../some/very/nested/stuff/in/other/folder`, you can use `some/very/nested/stuff/in/other/folder` assuming that your folder is under the `src` folder.
|
|
446
|
+
- The Fastify version is set to v3 because Apollo Server has not yet provided support for Fastify v4 yet, and it is difficult to have support for two different major versions of Fastify, so until Apollo Server supports Fastify v4, this package will use Fastify v3.
|
|
445
447
|
|
|
446
448
|
## What is new?
|
|
447
449
|
|
|
@@ -72,13 +72,14 @@ export { UserModel }
|
|
|
72
72
|
file: `${projectName}/src/database/mongo/queries/index.ts`
|
|
73
73
|
},
|
|
74
74
|
user: {
|
|
75
|
-
content: `import { Document, Types } from 'mongoose'
|
|
75
|
+
content: `import { Document, MergeType, Types } from 'mongoose'
|
|
76
76
|
|
|
77
77
|
import { UserModel } from '..'
|
|
78
|
-
import { UserDTO } from 'schemas'
|
|
78
|
+
import { User, UserDTO, UserWithId } from 'schemas'
|
|
79
79
|
|
|
80
80
|
const userDBOtoDTO = (
|
|
81
|
-
userDBO: Document<unknown, unknown, UserDBO
|
|
81
|
+
userDBO: Document<unknown, unknown, MergeType<UserDBO, UserDBO>> &
|
|
82
|
+
Omit<UserDBO, keyof UserDBO> &
|
|
82
83
|
UserDBO & {
|
|
83
84
|
_id: Types.ObjectId
|
|
84
85
|
}
|
|
@@ -88,7 +89,7 @@ const userDBOtoDTO = (
|
|
|
88
89
|
updatedAt: userDBO.updatedAt.toISOString()
|
|
89
90
|
})
|
|
90
91
|
|
|
91
|
-
const store = async (userData:
|
|
92
|
+
const store = async (userData: User): Promise<UserDTO> => {
|
|
92
93
|
const user = new UserModel(userData)
|
|
93
94
|
|
|
94
95
|
await user.save()
|
|
@@ -124,7 +125,7 @@ const get = async (
|
|
|
124
125
|
return users.map(u => userDBOtoDTO(u))
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
const update = async (userData:
|
|
128
|
+
const update = async (userData: UserWithId): Promise<UserDTO | null> => {
|
|
128
129
|
const { id, ...rest } = userData
|
|
129
130
|
const user = await UserModel.findByIdAndUpdate(id, rest, { new: true })
|
|
130
131
|
|
|
@@ -73,7 +73,8 @@ export { applyRoutes }
|
|
|
73
73
|
file: `${projectName}/src/network/router.ts`
|
|
74
74
|
},
|
|
75
75
|
server: {
|
|
76
|
-
content: `import
|
|
76
|
+
content: `import { Server as HttpServer } from 'http'
|
|
77
|
+
import express from 'express'
|
|
77
78
|
import mongoose from 'mongoose'
|
|
78
79
|
import cors from 'cors'
|
|
79
80
|
import pino, { HttpLogger } from 'express-pino-logger'
|
|
@@ -86,16 +87,21 @@ class Server {
|
|
|
86
87
|
#app: express.Application
|
|
87
88
|
#connection: mongoose.Connection | undefined
|
|
88
89
|
#log: HttpLogger
|
|
90
|
+
#server: HttpServer | undefined
|
|
89
91
|
|
|
90
92
|
constructor() {
|
|
91
93
|
this.#app = express()
|
|
92
94
|
this.#log = pino({
|
|
93
|
-
transport:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
transport:
|
|
96
|
+
process.env.ENVIRONMENT !== 'production'
|
|
97
|
+
? {
|
|
98
|
+
target: 'pino-pretty',
|
|
99
|
+
options: {
|
|
100
|
+
translateTime: 'HH:MM:ss Z',
|
|
101
|
+
ignore: 'pid,hostname'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
: undefined
|
|
99
105
|
})
|
|
100
106
|
this.#config()
|
|
101
107
|
}
|
|
@@ -104,6 +110,7 @@ class Server {
|
|
|
104
110
|
this.#app.use(cors())
|
|
105
111
|
this.#app.use(express.json())
|
|
106
112
|
this.#app.use(express.urlencoded({ extended: false }))
|
|
113
|
+
this.#app.use(this.#log)
|
|
107
114
|
this.#app.use(
|
|
108
115
|
(
|
|
109
116
|
req: express.Request,
|
|
@@ -158,7 +165,7 @@ class Server {
|
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
public start(): void {
|
|
161
|
-
this.#app.listen(PORT, () => {
|
|
168
|
+
this.#server = this.#app.listen(PORT, () => {
|
|
162
169
|
this.#log.logger.info(\`Server running at port \${PORT}\`)
|
|
163
170
|
})
|
|
164
171
|
|
|
@@ -168,6 +175,16 @@ class Server {
|
|
|
168
175
|
this.#log.logger.error(e)
|
|
169
176
|
}
|
|
170
177
|
}
|
|
178
|
+
|
|
179
|
+
public stop(): void {
|
|
180
|
+
try {
|
|
181
|
+
if (this.#server) this.#server.close()
|
|
182
|
+
|
|
183
|
+
process.exit(0)
|
|
184
|
+
} catch (e) {
|
|
185
|
+
this.#log.logger.error(e)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
171
188
|
}
|
|
172
189
|
|
|
173
190
|
const server = new Server()
|
|
@@ -208,14 +225,14 @@ export * from './user'
|
|
|
208
225
|
|
|
209
226
|
import { response } from 'network/response'
|
|
210
227
|
import { UserService } from 'services'
|
|
211
|
-
import { idSchema,
|
|
228
|
+
import { idSchema, storeUserDto, UserWithId } from 'schemas'
|
|
212
229
|
import { validatorCompiler } from './utils'
|
|
213
230
|
|
|
214
231
|
const User = Router()
|
|
215
232
|
|
|
216
233
|
User.route('/users')
|
|
217
234
|
.post(
|
|
218
|
-
validatorCompiler(
|
|
235
|
+
validatorCompiler(storeUserDto, 'body'),
|
|
219
236
|
async (
|
|
220
237
|
req: CustomRequest,
|
|
221
238
|
res: CustomResponse,
|
|
@@ -223,9 +240,9 @@ User.route('/users')
|
|
|
223
240
|
): Promise<void> => {
|
|
224
241
|
try {
|
|
225
242
|
const {
|
|
226
|
-
body: { args }
|
|
243
|
+
body: { args: user }
|
|
227
244
|
} = req
|
|
228
|
-
const us = new UserService({
|
|
245
|
+
const us = new UserService({ user })
|
|
229
246
|
const result = await us.process({ type: 'store' })
|
|
230
247
|
|
|
231
248
|
response({ error: false, message: result, res, status: 201 })
|
|
@@ -290,7 +307,7 @@ User.route('/user/:id')
|
|
|
290
307
|
)
|
|
291
308
|
.patch(
|
|
292
309
|
validatorCompiler(idSchema, 'params'),
|
|
293
|
-
validatorCompiler(
|
|
310
|
+
validatorCompiler(storeUserDto, 'body'),
|
|
294
311
|
async (
|
|
295
312
|
req: CustomRequest,
|
|
296
313
|
res: CustomResponse,
|
|
@@ -301,11 +318,11 @@ User.route('/user/:id')
|
|
|
301
318
|
body: { args },
|
|
302
319
|
params: { id }
|
|
303
320
|
} = req
|
|
304
|
-
const
|
|
321
|
+
const userWithId = {
|
|
305
322
|
id,
|
|
306
323
|
...args
|
|
307
|
-
} as
|
|
308
|
-
const us = new UserService({
|
|
324
|
+
} as UserWithId
|
|
325
|
+
const us = new UserService({ userWithId })
|
|
309
326
|
const result = await us.process({ type: 'update' })
|
|
310
327
|
|
|
311
328
|
response({ error: false, message: result, res, status: 200 })
|
|
@@ -447,11 +464,11 @@ export { User }
|
|
|
447
464
|
content: `import { ApolloError } from 'apollo-server-core'
|
|
448
465
|
|
|
449
466
|
import { store, remove, update } from 'database'
|
|
450
|
-
import { UserDTO } from 'schemas'
|
|
467
|
+
import { User, UserDTO, UserWithId } from 'schemas'
|
|
451
468
|
import { EFU, MFU, GE, errorHandling } from '../utils'
|
|
452
469
|
|
|
453
470
|
const storeUser = async (
|
|
454
|
-
{ user }: { user:
|
|
471
|
+
{ user }: { user: User },
|
|
455
472
|
{ log }: Context
|
|
456
473
|
): Promise<UserDTO> => {
|
|
457
474
|
try {
|
|
@@ -489,7 +506,7 @@ const deleteAllUsers = async ({ log }: Context): Promise<string> => {
|
|
|
489
506
|
}
|
|
490
507
|
|
|
491
508
|
const updateUser = async (
|
|
492
|
-
{ user }: { user:
|
|
509
|
+
{ user }: { user: UserWithId },
|
|
493
510
|
{ log }: Context
|
|
494
511
|
): Promise<UserDTO> => {
|
|
495
512
|
try {
|
|
@@ -536,15 +553,22 @@ export { storeUser, deleteAllUsers, updateUser, deleteUser }
|
|
|
536
553
|
content: `import { DefinedError } from 'ajv'
|
|
537
554
|
import { ApolloError } from 'apollo-server-core'
|
|
538
555
|
|
|
539
|
-
import {
|
|
540
|
-
|
|
556
|
+
import {
|
|
557
|
+
ajv,
|
|
558
|
+
idSchema,
|
|
559
|
+
User,
|
|
560
|
+
user as storeUserSchema,
|
|
561
|
+
UserDTO,
|
|
562
|
+
UserWithId,
|
|
563
|
+
userWithId as updateUserSchema
|
|
564
|
+
} from 'schemas'
|
|
541
565
|
import { storeUser, updateUser, deleteUser, deleteAllUsers } from './mutations'
|
|
542
566
|
import { errorHandling, GE } from '../utils'
|
|
543
567
|
|
|
544
568
|
const Mutation = {
|
|
545
569
|
storeUser: async (
|
|
546
570
|
parent: unknown,
|
|
547
|
-
{ user }: { user:
|
|
571
|
+
{ user }: { user: User },
|
|
548
572
|
context: Context
|
|
549
573
|
): Promise<UserDTO> => {
|
|
550
574
|
const { log } = context
|
|
@@ -576,7 +600,7 @@ const Mutation = {
|
|
|
576
600
|
},
|
|
577
601
|
updateUser: async (
|
|
578
602
|
parent: unknown,
|
|
579
|
-
{ user }: { user:
|
|
603
|
+
{ user }: { user: UserWithId },
|
|
580
604
|
context: Context
|
|
581
605
|
): Promise<UserDTO> => {
|
|
582
606
|
const validate = ajv.compile(updateUserSchema)
|
|
@@ -756,30 +780,6 @@ export { Query }
|
|
|
756
780
|
`,
|
|
757
781
|
file: `${projectName}/src/graphQL/models/User/queriesResolver.ts`
|
|
758
782
|
},
|
|
759
|
-
schemas: {
|
|
760
|
-
content: `import { Type } from '@sinclair/typebox'
|
|
761
|
-
|
|
762
|
-
const updateUserSchema = Type.Object(
|
|
763
|
-
{
|
|
764
|
-
id: Type.String({ minLength: 24, maxLength: 24 }),
|
|
765
|
-
lastName: Type.String(),
|
|
766
|
-
name: Type.String()
|
|
767
|
-
},
|
|
768
|
-
{ additionalProperties: false }
|
|
769
|
-
)
|
|
770
|
-
|
|
771
|
-
const storeUserSchema = Type.Object(
|
|
772
|
-
{
|
|
773
|
-
lastName: Type.String({ minLength: 1 }),
|
|
774
|
-
name: Type.String({ minLength: 1 })
|
|
775
|
-
},
|
|
776
|
-
{ additionalProperties: false }
|
|
777
|
-
)
|
|
778
|
-
|
|
779
|
-
export { updateUserSchema, storeUserSchema }
|
|
780
|
-
`,
|
|
781
|
-
file: `${projectName}/src/graphQL/models/User/schemas.ts`
|
|
782
|
-
},
|
|
783
783
|
typeDefs: {
|
|
784
784
|
content: `import { gql } from 'apollo-server-core'
|
|
785
785
|
|
|
@@ -788,6 +788,7 @@ const User = gql\`
|
|
|
788
788
|
id: ID!
|
|
789
789
|
name: String!
|
|
790
790
|
lastName: String!
|
|
791
|
+
createdAt: String!
|
|
791
792
|
updatedAt: String!
|
|
792
793
|
}
|
|
793
794
|
|
|
@@ -952,7 +953,7 @@ export { applyRoutes }
|
|
|
952
953
|
file: `${projectName}/src/network/router.ts`
|
|
953
954
|
},
|
|
954
955
|
server: {
|
|
955
|
-
content: `import
|
|
956
|
+
content: `import { createServer, Server as HttpServer } from 'http'
|
|
956
957
|
import express from 'express'
|
|
957
958
|
import mongoose from 'mongoose'
|
|
958
959
|
import cors from 'cors'
|
|
@@ -972,28 +973,32 @@ const PORT = (process.env.PORT as string) || 1996
|
|
|
972
973
|
class Server {
|
|
973
974
|
#app: express.Application
|
|
974
975
|
#connection: mongoose.Connection | undefined
|
|
975
|
-
#
|
|
976
|
+
#server: HttpServer
|
|
976
977
|
#log: HttpLogger
|
|
977
978
|
|
|
978
979
|
constructor() {
|
|
979
980
|
this.#app = express()
|
|
980
|
-
this.#
|
|
981
|
+
this.#server = createServer(this.#app)
|
|
981
982
|
this.#log = pino({
|
|
982
|
-
transport:
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
983
|
+
transport:
|
|
984
|
+
process.env.ENVIRONMENT !== 'production'
|
|
985
|
+
? {
|
|
986
|
+
target: 'pino-pretty',
|
|
987
|
+
options: {
|
|
988
|
+
translateTime: 'HH:MM:ss Z',
|
|
989
|
+
ignore: 'pid,hostname'
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
: undefined
|
|
988
993
|
})
|
|
989
994
|
this.#config()
|
|
990
995
|
}
|
|
991
996
|
|
|
992
997
|
#config() {
|
|
993
998
|
this.#app.use(cors())
|
|
994
|
-
this.#app.use(this.#log)
|
|
995
999
|
this.#app.use(express.json())
|
|
996
1000
|
this.#app.use(express.urlencoded({ extended: false }))
|
|
1001
|
+
this.#app.use(this.#log)
|
|
997
1002
|
this.#app.use(
|
|
998
1003
|
(
|
|
999
1004
|
req: express.Request,
|
|
@@ -1050,7 +1055,7 @@ class Server {
|
|
|
1050
1055
|
const server = new ApolloServer({
|
|
1051
1056
|
schema,
|
|
1052
1057
|
plugins: [
|
|
1053
|
-
ApolloServerPluginDrainHttpServer({ httpServer: this.#
|
|
1058
|
+
ApolloServerPluginDrainHttpServer({ httpServer: this.#server }),
|
|
1054
1059
|
process.env.NODE_ENV === 'production'
|
|
1055
1060
|
? ApolloServerPluginLandingPageDisabled()
|
|
1056
1061
|
: ApolloServerPluginLandingPageGraphQLPlayground()
|
|
@@ -1068,7 +1073,7 @@ class Server {
|
|
|
1068
1073
|
})
|
|
1069
1074
|
applyRoutes(this.#app)
|
|
1070
1075
|
await this.#mongo()
|
|
1071
|
-
this.#
|
|
1076
|
+
this.#server.listen(PORT, () => {
|
|
1072
1077
|
this.#log.logger.info(\`Server listening at port: \${PORT}\`)
|
|
1073
1078
|
this.#log.logger.info(
|
|
1074
1079
|
\`GraphQL server listening at: http://localhost:\${PORT}\${server.graphqlPath}\`
|
|
@@ -1078,6 +1083,16 @@ class Server {
|
|
|
1078
1083
|
console.error(e)
|
|
1079
1084
|
}
|
|
1080
1085
|
}
|
|
1086
|
+
|
|
1087
|
+
public stop(): void {
|
|
1088
|
+
try {
|
|
1089
|
+
if (this.#server) this.#server.close()
|
|
1090
|
+
|
|
1091
|
+
process.exit(0)
|
|
1092
|
+
} catch (e) {
|
|
1093
|
+
this.#log.logger.error(e)
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1081
1096
|
}
|
|
1082
1097
|
|
|
1083
1098
|
const server = new Server()
|
|
@@ -1180,10 +1195,6 @@ export { validatorCompiler }
|
|
|
1180
1195
|
graphQL.models.User.queriesResolver.file,
|
|
1181
1196
|
graphQL.models.User.queriesResolver.content
|
|
1182
1197
|
),
|
|
1183
|
-
writeFile(
|
|
1184
|
-
graphQL.models.User.schemas.file,
|
|
1185
|
-
graphQL.models.User.schemas.content
|
|
1186
|
-
),
|
|
1187
1198
|
writeFile(
|
|
1188
1199
|
graphQL.models.User.typeDefs.file,
|
|
1189
1200
|
graphQL.models.User.typeDefs.content
|
|
@@ -1295,7 +1306,9 @@ class Server {
|
|
|
1295
1306
|
#connection: mongoose.Connection | undefined
|
|
1296
1307
|
|
|
1297
1308
|
constructor() {
|
|
1298
|
-
this.#app = fastify({
|
|
1309
|
+
this.#app = fastify({
|
|
1310
|
+
logger: { prettyPrint: process.env.NODE_ENV !== 'production' }
|
|
1311
|
+
})
|
|
1299
1312
|
this.#config()
|
|
1300
1313
|
}
|
|
1301
1314
|
|
|
@@ -1357,6 +1370,15 @@ class Server {
|
|
|
1357
1370
|
console.error(e)
|
|
1358
1371
|
}
|
|
1359
1372
|
}
|
|
1373
|
+
|
|
1374
|
+
public async stop(): Promise<void> {
|
|
1375
|
+
try {
|
|
1376
|
+
await this.#app.close()
|
|
1377
|
+
process.exit(0)
|
|
1378
|
+
} catch (e) {
|
|
1379
|
+
console.error(e)
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1360
1382
|
}
|
|
1361
1383
|
|
|
1362
1384
|
const server = new Server()
|
|
@@ -1442,18 +1464,18 @@ import {
|
|
|
1442
1464
|
userDto,
|
|
1443
1465
|
idSchema,
|
|
1444
1466
|
IdSchema,
|
|
1445
|
-
|
|
1446
|
-
|
|
1467
|
+
storeUserDto,
|
|
1468
|
+
StoreUserDTO
|
|
1447
1469
|
} from 'schemas'
|
|
1448
1470
|
import { UserService } from 'services'
|
|
1449
1471
|
|
|
1450
1472
|
const User = (app: FastifyInstance, prefix = '/api'): void => {
|
|
1451
1473
|
app
|
|
1452
|
-
.post<{ Body:
|
|
1474
|
+
.post<{ Body: StoreUserDTO }>(
|
|
1453
1475
|
\`\${prefix}/users\`,
|
|
1454
1476
|
{
|
|
1455
1477
|
schema: {
|
|
1456
|
-
body:
|
|
1478
|
+
body: storeUserDto,
|
|
1457
1479
|
response: {
|
|
1458
1480
|
200: {
|
|
1459
1481
|
error: {
|
|
@@ -1472,7 +1494,7 @@ const User = (app: FastifyInstance, prefix = '/api'): void => {
|
|
|
1472
1494
|
}
|
|
1473
1495
|
} = request
|
|
1474
1496
|
const us = new UserService({
|
|
1475
|
-
|
|
1497
|
+
user: { lastName, name }
|
|
1476
1498
|
})
|
|
1477
1499
|
const user = await us.process({ type: 'store' })
|
|
1478
1500
|
|
|
@@ -1571,11 +1593,11 @@ const User = (app: FastifyInstance, prefix = '/api'): void => {
|
|
|
1571
1593
|
})
|
|
1572
1594
|
}
|
|
1573
1595
|
)
|
|
1574
|
-
.patch<{ Body:
|
|
1596
|
+
.patch<{ Body: StoreUserDTO; Params: IdSchema }>(
|
|
1575
1597
|
\`\${prefix}/user/:id\`,
|
|
1576
1598
|
{
|
|
1577
1599
|
schema: {
|
|
1578
|
-
body:
|
|
1600
|
+
body: storeUserDto,
|
|
1579
1601
|
params: idSchema,
|
|
1580
1602
|
response: {
|
|
1581
1603
|
200: {
|
|
@@ -1596,7 +1618,7 @@ const User = (app: FastifyInstance, prefix = '/api'): void => {
|
|
|
1596
1618
|
params: { id }
|
|
1597
1619
|
} = request
|
|
1598
1620
|
const us = new UserService({
|
|
1599
|
-
|
|
1621
|
+
userWithId: { name, lastName, id }
|
|
1600
1622
|
})
|
|
1601
1623
|
const user = await us.process({ type: 'update' })
|
|
1602
1624
|
|
|
@@ -1749,11 +1771,11 @@ export { User }
|
|
|
1749
1771
|
content: `import { ApolloError } from 'apollo-server-core'
|
|
1750
1772
|
|
|
1751
1773
|
import { store, remove, update } from 'database'
|
|
1752
|
-
import { UserDTO } from 'schemas'
|
|
1774
|
+
import { User, UserDTO, UserWithId } from 'schemas'
|
|
1753
1775
|
import { EFU, MFU, GE, errorHandling } from '../utils'
|
|
1754
1776
|
|
|
1755
1777
|
const storeUser = async (
|
|
1756
|
-
{ user }: { user:
|
|
1778
|
+
{ user }: { user: User },
|
|
1757
1779
|
{ log }: Context
|
|
1758
1780
|
): Promise<UserDTO> => {
|
|
1759
1781
|
try {
|
|
@@ -1791,7 +1813,7 @@ const deleteAllUsers = async ({ log }: Context): Promise<string> => {
|
|
|
1791
1813
|
}
|
|
1792
1814
|
|
|
1793
1815
|
const updateUser = async (
|
|
1794
|
-
{ user }: { user:
|
|
1816
|
+
{ user }: { user: UserWithId },
|
|
1795
1817
|
{ log }: Context
|
|
1796
1818
|
): Promise<UserDTO> => {
|
|
1797
1819
|
try {
|
|
@@ -1838,15 +1860,22 @@ export { storeUser, deleteAllUsers, updateUser, deleteUser }
|
|
|
1838
1860
|
content: `import { DefinedError } from 'ajv'
|
|
1839
1861
|
import { ApolloError } from 'apollo-server-core'
|
|
1840
1862
|
|
|
1841
|
-
import {
|
|
1842
|
-
|
|
1863
|
+
import {
|
|
1864
|
+
ajv,
|
|
1865
|
+
idSchema,
|
|
1866
|
+
User,
|
|
1867
|
+
user as storeUserSchema,
|
|
1868
|
+
UserDTO,
|
|
1869
|
+
UserWithId,
|
|
1870
|
+
userWithId as updateUserSchema
|
|
1871
|
+
} from 'schemas'
|
|
1843
1872
|
import { storeUser, updateUser, deleteUser, deleteAllUsers } from './mutations'
|
|
1844
1873
|
import { errorHandling, GE } from '../utils'
|
|
1845
1874
|
|
|
1846
1875
|
const Mutation = {
|
|
1847
1876
|
storeUser: async (
|
|
1848
1877
|
parent: unknown,
|
|
1849
|
-
{ user }: { user:
|
|
1878
|
+
{ user }: { user: User },
|
|
1850
1879
|
context: Context
|
|
1851
1880
|
): Promise<UserDTO> => {
|
|
1852
1881
|
const { log } = context
|
|
@@ -1878,7 +1907,7 @@ const Mutation = {
|
|
|
1878
1907
|
},
|
|
1879
1908
|
updateUser: async (
|
|
1880
1909
|
parent: unknown,
|
|
1881
|
-
{ user }: { user:
|
|
1910
|
+
{ user }: { user: UserWithId },
|
|
1882
1911
|
context: Context
|
|
1883
1912
|
): Promise<UserDTO> => {
|
|
1884
1913
|
const validate = ajv.compile(updateUserSchema)
|
|
@@ -2058,30 +2087,6 @@ export { Query }
|
|
|
2058
2087
|
`,
|
|
2059
2088
|
file: `${projectName}/src/graphQL/models/User/queriesResolver.ts`
|
|
2060
2089
|
},
|
|
2061
|
-
schemas: {
|
|
2062
|
-
content: `import { Type } from '@sinclair/typebox'
|
|
2063
|
-
|
|
2064
|
-
const updateUserSchema = Type.Object(
|
|
2065
|
-
{
|
|
2066
|
-
id: Type.String({ minLength: 24, maxLength: 24 }),
|
|
2067
|
-
lastName: Type.String(),
|
|
2068
|
-
name: Type.String()
|
|
2069
|
-
},
|
|
2070
|
-
{ additionalProperties: false }
|
|
2071
|
-
)
|
|
2072
|
-
|
|
2073
|
-
const storeUserSchema = Type.Object(
|
|
2074
|
-
{
|
|
2075
|
-
lastName: Type.String({ minLength: 1 }),
|
|
2076
|
-
name: Type.String({ minLength: 1 })
|
|
2077
|
-
},
|
|
2078
|
-
{ additionalProperties: false }
|
|
2079
|
-
)
|
|
2080
|
-
|
|
2081
|
-
export { updateUserSchema, storeUserSchema }
|
|
2082
|
-
`,
|
|
2083
|
-
file: `${projectName}/src/graphQL/models/User/schemas.ts`
|
|
2084
|
-
},
|
|
2085
2090
|
typeDefs: {
|
|
2086
2091
|
content: `import { gql } from 'apollo-server-core'
|
|
2087
2092
|
|
|
@@ -2090,6 +2095,7 @@ const User = gql\`
|
|
|
2090
2095
|
id: ID!
|
|
2091
2096
|
name: String!
|
|
2092
2097
|
lastName: String!
|
|
2098
|
+
createdAt: String!
|
|
2093
2099
|
updatedAt: String!
|
|
2094
2100
|
}
|
|
2095
2101
|
|
|
@@ -2272,7 +2278,9 @@ class Server {
|
|
|
2272
2278
|
#connection: mongoose.Connection | undefined
|
|
2273
2279
|
|
|
2274
2280
|
constructor() {
|
|
2275
|
-
this.#app = fastify({
|
|
2281
|
+
this.#app = fastify({
|
|
2282
|
+
logger: { prettyPrint: process.env.NODE_ENV !== 'production' }
|
|
2283
|
+
})
|
|
2276
2284
|
this.#config()
|
|
2277
2285
|
}
|
|
2278
2286
|
|
|
@@ -2369,6 +2377,15 @@ class Server {
|
|
|
2369
2377
|
console.error(e)
|
|
2370
2378
|
}
|
|
2371
2379
|
}
|
|
2380
|
+
|
|
2381
|
+
public async stop(): Promise<void> {
|
|
2382
|
+
try {
|
|
2383
|
+
await this.#app.close()
|
|
2384
|
+
process.exit(0)
|
|
2385
|
+
} catch (e) {
|
|
2386
|
+
console.error(e)
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2372
2389
|
}
|
|
2373
2390
|
|
|
2374
2391
|
const server = new Server()
|
|
@@ -2469,10 +2486,6 @@ export * from './docs'
|
|
|
2469
2486
|
graphQL.models.User.queriesResolver.file,
|
|
2470
2487
|
graphQL.models.User.queriesResolver.content
|
|
2471
2488
|
),
|
|
2472
|
-
writeFile(
|
|
2473
|
-
graphQL.models.User.schemas.file,
|
|
2474
|
-
graphQL.models.User.schemas.content
|
|
2475
|
-
),
|
|
2476
2489
|
writeFile(
|
|
2477
2490
|
graphQL.models.User.typeDefs.file,
|
|
2478
2491
|
graphQL.models.User.typeDefs.content
|
|
@@ -46,6 +46,10 @@ const user = Type.Object({
|
|
|
46
46
|
|
|
47
47
|
type User = Static<typeof user>
|
|
48
48
|
|
|
49
|
+
const userWithId = Type.Intersect([user, Type.Object({ id })])
|
|
50
|
+
|
|
51
|
+
type UserWithId = Static<typeof userWithId>
|
|
52
|
+
|
|
49
53
|
const userDto = Type.Object({
|
|
50
54
|
id: Type.Optional(id),
|
|
51
55
|
lastName: Type.String(),
|
|
@@ -56,19 +60,28 @@ const userDto = Type.Object({
|
|
|
56
60
|
|
|
57
61
|
type UserDTO = Static<typeof userDto>
|
|
58
62
|
|
|
59
|
-
const
|
|
63
|
+
const storeUserDto = Type.Object({
|
|
60
64
|
args: user
|
|
61
65
|
})
|
|
62
66
|
|
|
63
|
-
type
|
|
64
|
-
|
|
65
|
-
export {
|
|
67
|
+
type StoreUserDTO = Static<typeof storeUserDto>
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
userDto,
|
|
71
|
+
UserDTO,
|
|
72
|
+
userWithId,
|
|
73
|
+
UserWithId,
|
|
74
|
+
user,
|
|
75
|
+
User,
|
|
76
|
+
storeUserDto,
|
|
77
|
+
StoreUserDTO
|
|
78
|
+
}
|
|
66
79
|
`,
|
|
67
80
|
file: `${projectName}/src/schemas/user.ts`
|
|
68
81
|
}
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
if (graphql)
|
|
84
|
+
if (graphql)
|
|
72
85
|
schemas.index.content = `import { Static, Type } from '@sinclair/typebox'
|
|
73
86
|
import Ajv from 'ajv/dist/2019.js'
|
|
74
87
|
import addFormats from 'ajv-formats'
|
|
@@ -90,30 +103,6 @@ const ajv = addFormats(new Ajv(), ['email'])
|
|
|
90
103
|
export { id, ID, idSchema, IDSchema, ajv }
|
|
91
104
|
export * from './user'
|
|
92
105
|
`
|
|
93
|
-
schemas.user.content = `import { Static, Type } from '@sinclair/typebox'
|
|
94
|
-
|
|
95
|
-
import { id } from '.'
|
|
96
|
-
|
|
97
|
-
const user = Type.Object({
|
|
98
|
-
lastName: Type.String(),
|
|
99
|
-
name: Type.String()
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
type User = Static<typeof user>
|
|
103
|
-
|
|
104
|
-
const userDto = Type.Object({
|
|
105
|
-
id: Type.Optional(id),
|
|
106
|
-
lastName: Type.String(),
|
|
107
|
-
name: Type.String(),
|
|
108
|
-
createdAt: Type.Optional(Type.String()),
|
|
109
|
-
updatedAt: Type.Optional(Type.String())
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
type UserDTO = Static<typeof userDto>
|
|
113
|
-
|
|
114
|
-
export { userDto, UserDTO, user, User }
|
|
115
|
-
`
|
|
116
|
-
}
|
|
117
106
|
|
|
118
107
|
await Promise.all([
|
|
119
108
|
writeFile(schemas.index.file, schemas.index.content),
|
|
@@ -25,7 +25,7 @@ ${projectName}/src/services/utils/messages`
|
|
|
25
25
|
content: `import httpErrors from 'http-errors'
|
|
26
26
|
|
|
27
27
|
import { store, remove, get, update } from 'database'
|
|
28
|
-
import { UserDTO } from 'schemas'
|
|
28
|
+
import { User, UserDTO, UserWithId } from 'schemas'
|
|
29
29
|
import { EFU, MFU, GE, errorHandling } from './utils'
|
|
30
30
|
|
|
31
31
|
type Process = {
|
|
@@ -34,8 +34,8 @@ type Process = {
|
|
|
34
34
|
|
|
35
35
|
type Arguments = {
|
|
36
36
|
id?: string
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
user?: User
|
|
38
|
+
userWithId?: UserWithId
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
class UserService {
|
|
@@ -66,12 +66,10 @@ class UserService {
|
|
|
66
66
|
|
|
67
67
|
async #store(): Promise<UserDTO> {
|
|
68
68
|
try {
|
|
69
|
-
if (!this.#args.
|
|
69
|
+
if (!this.#args.user)
|
|
70
70
|
throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
|
|
71
71
|
|
|
72
|
-
const result = await store(
|
|
73
|
-
...this.#args.userDtoWithoutId
|
|
74
|
-
})
|
|
72
|
+
const result = await store(this.#args.user)
|
|
75
73
|
|
|
76
74
|
return result
|
|
77
75
|
} catch (e) {
|
|
@@ -122,10 +120,10 @@ class UserService {
|
|
|
122
120
|
|
|
123
121
|
async #update(): Promise<UserDTO> {
|
|
124
122
|
try {
|
|
125
|
-
if (!this.#args.
|
|
123
|
+
if (!this.#args.userWithId || !this.#args.userWithId.id)
|
|
126
124
|
throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
|
|
127
125
|
|
|
128
|
-
const updatedUser = await update(this.#args.
|
|
126
|
+
const updatedUser = await update(this.#args.userWithId)
|
|
129
127
|
|
|
130
128
|
if (!updatedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
|
|
131
129
|
|
|
@@ -10,7 +10,7 @@ module.exports = async projectName => {
|
|
|
10
10
|
|
|
11
11
|
WORKDIR /app
|
|
12
12
|
|
|
13
|
-
COPY
|
|
13
|
+
COPY . ./
|
|
14
14
|
|
|
15
15
|
RUN yarn install --prod
|
|
16
16
|
|
|
@@ -20,12 +20,28 @@ RUN yarn build
|
|
|
20
20
|
|
|
21
21
|
RUN yarn remove webpack webpack-node-externals tsconfig-paths-webpack-plugin
|
|
22
22
|
|
|
23
|
-
COPY dist /app/dist
|
|
24
|
-
|
|
25
23
|
CMD [ "yarn", "start" ]
|
|
26
24
|
`,
|
|
27
|
-
dockerFile: 'Dockerfile'
|
|
25
|
+
dockerFile: 'Dockerfile',
|
|
26
|
+
dockerIgnoreContent: `.eslintignore
|
|
27
|
+
.eslintrc
|
|
28
|
+
.gitignore
|
|
29
|
+
CHANGELOG.md
|
|
30
|
+
Dockerfile
|
|
31
|
+
heroku.yml
|
|
32
|
+
*.http
|
|
33
|
+
LICENSE
|
|
34
|
+
nodemon.json
|
|
35
|
+
README.md
|
|
36
|
+
|
|
37
|
+
# optionally you may want to ignore the .env file, but that depends on your own implementation
|
|
38
|
+
.env`,
|
|
39
|
+
dockerIgnoreFile: '.dockerignore'
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
await writeFile(`${projectName}/${data.dockerFile}`, data.dockerContent)
|
|
43
|
+
await writeFile(
|
|
44
|
+
`${projectName}/${data.dockerIgnoreFile}`,
|
|
45
|
+
data.dockerIgnoreContent
|
|
46
|
+
)
|
|
31
47
|
}
|
package/lib/src/index.js
CHANGED
|
@@ -62,7 +62,7 @@ module.exports = async ({
|
|
|
62
62
|
graphql ? 'apollo-server-express' : ''
|
|
63
63
|
}`
|
|
64
64
|
|
|
65
|
-
const fastifyProdPackages = `fastify @fastify/swagger @fastify/cors ${
|
|
65
|
+
const fastifyProdPackages = `fastify@^3 @fastify/swagger@^6 @fastify/cors@^7 ${
|
|
66
66
|
graphql ? 'apollo-server-fastify apollo-server-plugin-base' : ''
|
|
67
67
|
}`
|
|
68
68
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anthonylzq/simba.js",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "set up a modern backend app by running one command",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
|
-
"directories": {
|
|
7
|
-
"example": "example",
|
|
8
|
-
"lib": "lib"
|
|
9
|
-
},
|
|
10
6
|
"scripts": {
|
|
11
7
|
"service": "node ./bin",
|
|
12
8
|
"help": "node ./bin --help",
|
|
@@ -45,23 +41,23 @@
|
|
|
45
41
|
"author": "AnthonyLzq",
|
|
46
42
|
"license": "MIT",
|
|
47
43
|
"dependencies": {
|
|
48
|
-
"cli-progress": "^3.
|
|
44
|
+
"cli-progress": "^3.11.2",
|
|
49
45
|
"colors": "^1.4.0",
|
|
50
46
|
"readline-sync": "^1.4.10",
|
|
51
|
-
"underscore": "^1.13.
|
|
52
|
-
"yargs": "^17.
|
|
47
|
+
"underscore": "^1.13.4",
|
|
48
|
+
"yargs": "^17.5.1"
|
|
53
49
|
},
|
|
54
50
|
"devDependencies": {
|
|
55
|
-
"dotenv": "^16.0.
|
|
56
|
-
"eslint": "^8.
|
|
51
|
+
"dotenv": "^16.0.1",
|
|
52
|
+
"eslint": "^8.18.0",
|
|
57
53
|
"eslint-config-prettier": "^8.5.0",
|
|
58
|
-
"eslint-config-standard": "^
|
|
54
|
+
"eslint-config-standard": "^17.0.0",
|
|
59
55
|
"eslint-plugin-import": "^2.26.0",
|
|
60
56
|
"eslint-plugin-node": "^11.1.0",
|
|
61
57
|
"eslint-plugin-prettier": "^4.0.0",
|
|
62
58
|
"eslint-plugin-promise": "^6.0.0",
|
|
63
|
-
"prettier": "^2.
|
|
64
|
-
"standard-version": "^9.
|
|
59
|
+
"prettier": "^2.7.1",
|
|
60
|
+
"standard-version": "^9.5.0"
|
|
65
61
|
},
|
|
66
62
|
"repository": {
|
|
67
63
|
"type": "git",
|
|
@@ -81,5 +77,9 @@
|
|
|
81
77
|
"commit": true,
|
|
82
78
|
"bump": true
|
|
83
79
|
}
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"npm": ">=8.0.0",
|
|
83
|
+
"node": ">=16.0.0"
|
|
84
84
|
}
|
|
85
85
|
}
|