create-epic-graphql-server 0.5.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/.eslintignore +7 -0
- package/.eslintrc.js +198 -0
- package/.husky/pre-commit +2 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/bin/create-epic-graphql-server.js +81 -0
- package/db-seed.json5 +37 -0
- package/package.json +75 -0
- package/specs/Customer.spec.ts +41 -0
- package/specs/Order.spec.ts +80 -0
- package/specs/logger.spec.ts +92 -0
- package/specs/schema.spec.ts +103 -0
- package/src/config/db.ts +267 -0
- package/src/index.ts +57 -0
- package/src/models/Customer.ts +10 -0
- package/src/models/Order.ts +12 -0
- package/src/routes/check.ts +18 -0
- package/src/routes/index.ts +8 -0
- package/src/schema/schema.ts +227 -0
- package/src/typing/enums.ts +37 -0
- package/src/typing/interfaces.ts +44 -0
- package/src/typing/types.ts +5 -0
- package/src/utilities/logger.ts +191 -0
- package/tsconfig.json +42 -0
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
parser: '@typescript-eslint/parser',
|
|
4
|
+
parserOptions: {
|
|
5
|
+
ecmaVersion: 2020,
|
|
6
|
+
sourceType: 'script',
|
|
7
|
+
project: ['./tsconfig.json'],
|
|
8
|
+
tsconfigRootDir: __dirname,
|
|
9
|
+
},
|
|
10
|
+
env: {
|
|
11
|
+
node: true,
|
|
12
|
+
es2021: true,
|
|
13
|
+
},
|
|
14
|
+
extends: [
|
|
15
|
+
'plugin:@typescript-eslint/recommended',
|
|
16
|
+
],
|
|
17
|
+
plugins: [
|
|
18
|
+
'@typescript-eslint',
|
|
19
|
+
],
|
|
20
|
+
rules: {
|
|
21
|
+
// 📜 @typescript-eslint
|
|
22
|
+
'quotes': 'off',
|
|
23
|
+
'@typescript-eslint/quotes': ['error', 'single'],
|
|
24
|
+
'no-dupe-class-members': 'off', // handled by .ts
|
|
25
|
+
'no-undef': 'off', // handled by .ts
|
|
26
|
+
'@typescript-eslint/consistent-type-assertions': 'error',
|
|
27
|
+
'no-array-constructor': 'off',
|
|
28
|
+
'@typescript-eslint/no-array-constructor': 'error',
|
|
29
|
+
'no-var': 'error', // makes 'no-redeclare' irrelevant
|
|
30
|
+
// 'no-redeclare': 'off', // see above
|
|
31
|
+
// '@typescript-eslint/no-redeclare': 'error', // see above
|
|
32
|
+
'no-use-before-define': 'off',
|
|
33
|
+
'@typescript-eslint/no-use-before-define': [
|
|
34
|
+
'error',
|
|
35
|
+
{
|
|
36
|
+
'functions': true,
|
|
37
|
+
'classes': true,
|
|
38
|
+
'variables': true,
|
|
39
|
+
'allowNamedExports': true,
|
|
40
|
+
'enums': true,
|
|
41
|
+
'typedefs': true,
|
|
42
|
+
'ignoreTypeReferences': true,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
'no-unused-expressions': 'off',
|
|
46
|
+
'@typescript-eslint/no-unused-expressions': [
|
|
47
|
+
'error',
|
|
48
|
+
{
|
|
49
|
+
'allowShortCircuit': true,
|
|
50
|
+
'allowTernary': true,
|
|
51
|
+
'allowTaggedTemplates': true,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
'no-unused-vars': 'off',
|
|
55
|
+
'@typescript-eslint/no-unused-vars': [
|
|
56
|
+
'error',
|
|
57
|
+
{
|
|
58
|
+
'args': 'none',
|
|
59
|
+
'ignoreRestSiblings': true,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
'no-useless-constructor': 'off',
|
|
63
|
+
'@typescript-eslint/no-useless-constructor': 'error',
|
|
64
|
+
'@typescript-eslint/no-var-requires': 'off',
|
|
65
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
66
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
67
|
+
'warn',
|
|
68
|
+
{
|
|
69
|
+
prefer: 'type-imports',
|
|
70
|
+
disallowTypeAnnotations: false,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
// 📜 eslint
|
|
75
|
+
'array-callback-return': 'error',
|
|
76
|
+
'default-case': 'error',
|
|
77
|
+
'dot-location': ['error', 'property'],
|
|
78
|
+
'eqeqeq': ['error', 'always'],
|
|
79
|
+
'new-parens': 'error',
|
|
80
|
+
'no-caller': 'error',
|
|
81
|
+
'no-cond-assign': ['error', 'except-parens'],
|
|
82
|
+
'no-const-assign': 'error',
|
|
83
|
+
'no-control-regex': 'error',
|
|
84
|
+
'no-delete-var': 'error',
|
|
85
|
+
'no-dupe-args': 'error',
|
|
86
|
+
'no-dupe-keys': 'error',
|
|
87
|
+
'no-duplicate-case': 'error',
|
|
88
|
+
'no-empty-character-class': 'error',
|
|
89
|
+
'no-empty-pattern': 'error',
|
|
90
|
+
'no-eval': 'error',
|
|
91
|
+
'no-ex-assign': 'error',
|
|
92
|
+
'no-extend-native': 'error',
|
|
93
|
+
'no-extra-bind': 'error',
|
|
94
|
+
'no-extra-label': 'error',
|
|
95
|
+
'no-fallthrough': 'error',
|
|
96
|
+
'no-func-assign': 'error',
|
|
97
|
+
'no-implied-eval': 'error',
|
|
98
|
+
'no-invalid-regexp': 'error',
|
|
99
|
+
'no-iterator': 'error',
|
|
100
|
+
'no-label-var': 'error',
|
|
101
|
+
'no-labels': [
|
|
102
|
+
'error',
|
|
103
|
+
{
|
|
104
|
+
'allowLoop': true,
|
|
105
|
+
'allowSwitch': false,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
'no-lone-blocks': 'error',
|
|
109
|
+
'no-loop-func': 'error',
|
|
110
|
+
'no-mixed-operators': [
|
|
111
|
+
'error',
|
|
112
|
+
{
|
|
113
|
+
'groups': [
|
|
114
|
+
['&', '|', '^', '~', '<<', '>>', '>>>'],
|
|
115
|
+
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
|
|
116
|
+
['&&', '||'],
|
|
117
|
+
['in', 'instanceof'],
|
|
118
|
+
],
|
|
119
|
+
'allowSamePrecedence': false,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
'no-multi-str': 'error',
|
|
123
|
+
'no-native-reassign': 'error',
|
|
124
|
+
'no-negated-in-lhs': 'error',
|
|
125
|
+
'no-new-func': 'error',
|
|
126
|
+
'no-new-object': 'error',
|
|
127
|
+
'no-new-symbol': 'error',
|
|
128
|
+
'no-new-wrappers': 'error',
|
|
129
|
+
'no-obj-calls': 'error',
|
|
130
|
+
'no-octal': 'error',
|
|
131
|
+
'no-octal-escape': 'error',
|
|
132
|
+
'no-redeclare': 'error',
|
|
133
|
+
'no-regex-spaces': 'error',
|
|
134
|
+
'no-restricted-syntax': ['error', 'WithStatement'],
|
|
135
|
+
'no-script-url': 'error',
|
|
136
|
+
'no-self-assign': 'error',
|
|
137
|
+
'no-self-compare': 'error',
|
|
138
|
+
'no-sequences': 'error',
|
|
139
|
+
'no-shadow-restricted-names': 'error',
|
|
140
|
+
'no-sparse-arrays': 'error',
|
|
141
|
+
'no-template-curly-in-string': 'error',
|
|
142
|
+
'no-this-before-super': 'error',
|
|
143
|
+
'no-throw-literal': 'error',
|
|
144
|
+
'no-unreachable': 'error',
|
|
145
|
+
'no-unused-labels': 'error',
|
|
146
|
+
'no-useless-computed-key': 'error',
|
|
147
|
+
'no-useless-concat': 'error',
|
|
148
|
+
'no-useless-escape': 'error',
|
|
149
|
+
'no-useless-rename': [
|
|
150
|
+
'error',
|
|
151
|
+
{
|
|
152
|
+
'ignoreDestructuring': false,
|
|
153
|
+
'ignoreImport': false,
|
|
154
|
+
'ignoreExport': false,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
'no-with': 'error',
|
|
158
|
+
'no-whitespace-before-property': 'error',
|
|
159
|
+
'require-yield': 'error',
|
|
160
|
+
'rest-spread-spacing': ['error', 'never'],
|
|
161
|
+
'strict': ['error', 'never'],
|
|
162
|
+
'unicode-bom': ['error', 'never'],
|
|
163
|
+
'use-isnan': 'error',
|
|
164
|
+
'valid-typeof': 'error',
|
|
165
|
+
'getter-return': 'error',
|
|
166
|
+
|
|
167
|
+
// 📜 eslint, some additional styles
|
|
168
|
+
'array-element-newline': ['error', 'consistent'],
|
|
169
|
+
'comma-dangle': ['error', {
|
|
170
|
+
'arrays': 'always-multiline',
|
|
171
|
+
'objects': 'always-multiline',
|
|
172
|
+
'imports': 'always-multiline',
|
|
173
|
+
'exports': 'always-multiline',
|
|
174
|
+
// 'functions': 'never'
|
|
175
|
+
}],
|
|
176
|
+
'semi': ['error', 'never'],
|
|
177
|
+
'curly': ['error', 'multi-line'],
|
|
178
|
+
'indent': [
|
|
179
|
+
'error',
|
|
180
|
+
2,
|
|
181
|
+
{
|
|
182
|
+
'SwitchCase': 1,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
'arrow-spacing': ['error'],
|
|
186
|
+
'object-curly-spacing': ['error', 'always'],
|
|
187
|
+
'array-bracket-spacing': ['error', 'never'],
|
|
188
|
+
'no-irregular-whitespace': ['error'],
|
|
189
|
+
'eol-last': ['error', 'always'],
|
|
190
|
+
'no-trailing-spaces': [
|
|
191
|
+
'error',
|
|
192
|
+
{
|
|
193
|
+
'skipBlankLines': false,
|
|
194
|
+
'ignoreComments': true,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 daveKontro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
# Create Epic GraphQL Server
|
|
6
|
+
This module provides a configured [GraphQL](https://www.npmjs.com/package/graphql) server.
|
|
7
|
+
|
|
8
|
+
Having a preconfigured server will jump-start your project so you never have to begin from scratch.
|
|
9
|
+
|
|
10
|
+
Use GraphQL to unify you data into a single API. Take advantage of the powerful, intuitive GraphQL query language.
|
|
11
|
+
|
|
12
|
+
Interact with this project template live [here](https://www.createepicgraphqlserver.com/).
|
|
13
|
+
|
|
14
|
+
## installation
|
|
15
|
+
first install globally
|
|
16
|
+
```
|
|
17
|
+
npm install -g create-epic-graphql-server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
then create your project
|
|
21
|
+
```
|
|
22
|
+
npx create-epic-graphql-server --name={my-project}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## usage
|
|
26
|
+
spin up the mock database (described below) `npm run dev:db`
|
|
27
|
+
|
|
28
|
+
now spin up the development server `npm run dev`
|
|
29
|
+
|
|
30
|
+
congrats, your API is now live!
|
|
31
|
+
|
|
32
|
+
### Graph*i*QL
|
|
33
|
+
use the Graph*i*QL IDE to interact with your development server's API
|
|
34
|
+
|
|
35
|
+
this project provides the [ruru](https://www.npmjs.com/package/ruru) Graph*i*QL interface on `http://localhost:3000` (by default) during development
|
|
36
|
+
|
|
37
|
+
sample queries are provided later in this document
|
|
38
|
+
|
|
39
|
+
### mock database
|
|
40
|
+
in order to get up and running quickly without the need to initially plug into a real database, this project provides a mock database via [json-server](https://www.npmjs.com/package/json-server)
|
|
41
|
+
|
|
42
|
+
the database starts when you run `npm run dev:db`, which automatically creates and runs of the file `db.json5`; the db file is seeded upon each startup from `db-seed.json`
|
|
43
|
+
|
|
44
|
+
see file `/src/config/db.ts` to understand how the database works; you will likely, eventually remove this file when you plug into a real database and replace the contents of `/src/models` with actual database models... that said, the provided pattern gives you scaffolding to build around
|
|
45
|
+
|
|
46
|
+
⚠️ please note this mock database is not intended or suited for production use ⚠️
|
|
47
|
+
|
|
48
|
+
## schema and types
|
|
49
|
+
the schema sits at the heart of the GraphQL service
|
|
50
|
+
|
|
51
|
+
the types describe what data you can query, and the schema is the collection of what the service provides
|
|
52
|
+
|
|
53
|
+
a sample schema has been configured in `/src/schema/schema` with model validation, read all, read on, mutations, etc... again, your usecase will necessitate alterations to the schema, but the provided pattern might help you along the way
|
|
54
|
+
|
|
55
|
+
## test suite
|
|
56
|
+
the test suite is enabled to run unit and integration tests
|
|
57
|
+
|
|
58
|
+
to create a test add a file tp `specs/` and follow this file naming format: `*.spec.ts`
|
|
59
|
+
|
|
60
|
+
tests run automatically during development via `npm run dev` or the test suite stand alone like so
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
npm run test
|
|
64
|
+
npm run test:watch
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## linting
|
|
68
|
+
linting rules are in `.eslintrc.js`; install the [ESLint](https://www.npmjs.com/package/eslint) pluggin if using vscode
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
npm run lint
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## custom logger
|
|
75
|
+
A customized [winston](https://www.npmjs.com/package/winston) logger instance resides in `utilities/logger`, with usage found thoughout the codebase methods are provide to log a timestamp or the current node environment
|
|
76
|
+
|
|
77
|
+
## pre-commit
|
|
78
|
+
scripts in `.husky/pre-commit` are run on commits for quality control
|
|
79
|
+
|
|
80
|
+
add or remove scripts you'd like run before code is commited
|
|
81
|
+
|
|
82
|
+
## environmental settings
|
|
83
|
+
you can create a `.env` file at the project root and add the following variables
|
|
84
|
+
|
|
85
|
+
also add any additional variables your project needs
|
|
86
|
+
|
|
87
|
+
### develop (dev server)
|
|
88
|
+
```
|
|
89
|
+
# optional but recommended
|
|
90
|
+
NODE_ENV=development
|
|
91
|
+
PORT={port number}
|
|
92
|
+
# optional
|
|
93
|
+
JSON_SERVER_PORT={dev db port, default 3210}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### production (build)
|
|
97
|
+
```
|
|
98
|
+
# optional but recommended
|
|
99
|
+
NODE_ENV=production
|
|
100
|
+
PORT={port number}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## query samples
|
|
104
|
+
Run these queries against the development server to get a feel the GraphQL query language.
|
|
105
|
+
|
|
106
|
+
### get all orders
|
|
107
|
+
```
|
|
108
|
+
{
|
|
109
|
+
orders {
|
|
110
|
+
__typename
|
|
111
|
+
id
|
|
112
|
+
name
|
|
113
|
+
notes
|
|
114
|
+
status
|
|
115
|
+
customer {
|
|
116
|
+
__typename
|
|
117
|
+
name
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### get all customers
|
|
124
|
+
```
|
|
125
|
+
{
|
|
126
|
+
customers {
|
|
127
|
+
id
|
|
128
|
+
name
|
|
129
|
+
email
|
|
130
|
+
phone
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### get an order by id
|
|
136
|
+
```
|
|
137
|
+
{
|
|
138
|
+
order(id: "2") {
|
|
139
|
+
id
|
|
140
|
+
name
|
|
141
|
+
status
|
|
142
|
+
customer {
|
|
143
|
+
name
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### get orders by status
|
|
150
|
+
```
|
|
151
|
+
{
|
|
152
|
+
orders(filter: { status: "Not Started" }) {
|
|
153
|
+
id
|
|
154
|
+
name
|
|
155
|
+
status
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### create a customer
|
|
161
|
+
```
|
|
162
|
+
mutation {
|
|
163
|
+
addCustomer(
|
|
164
|
+
name: "Tom Hill",
|
|
165
|
+
email: "tom@example.com",
|
|
166
|
+
phone: "555-5555"
|
|
167
|
+
) {
|
|
168
|
+
id
|
|
169
|
+
name
|
|
170
|
+
email
|
|
171
|
+
phone
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### create an order
|
|
177
|
+
- use id from new customer "Tom Hill"
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
mutation {
|
|
181
|
+
addOrder(
|
|
182
|
+
name: productOne,
|
|
183
|
+
status: notStarted,
|
|
184
|
+
customerId: "{id}"
|
|
185
|
+
) {
|
|
186
|
+
id
|
|
187
|
+
name
|
|
188
|
+
status
|
|
189
|
+
customer {
|
|
190
|
+
id
|
|
191
|
+
name
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### update and order
|
|
198
|
+
- use id from new order created above
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
mutation {
|
|
202
|
+
updateOrder(
|
|
203
|
+
id: "{id}",
|
|
204
|
+
status: processing,
|
|
205
|
+
) {
|
|
206
|
+
id
|
|
207
|
+
name
|
|
208
|
+
status
|
|
209
|
+
customer {
|
|
210
|
+
id
|
|
211
|
+
name
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### delete a customer by id
|
|
218
|
+
- use id from new customer "Tom Hill"
|
|
219
|
+
- resolver is setup to delete associated orders too
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
mutation {
|
|
223
|
+
deleteCustomer(id: "{id}") {
|
|
224
|
+
id
|
|
225
|
+
name
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
const { writeFile } = require('fs/promises')
|
|
3
|
+
const { execSync } = require('child_process')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const yargs = require('yargs/yargs')
|
|
6
|
+
const { hideBin } = require('yargs/helpers')
|
|
7
|
+
const packageJson = require('../package.json')
|
|
8
|
+
|
|
9
|
+
const run = async () => {
|
|
10
|
+
const repo = 'https://github.com/daveKontro/create-epic-graphql-server/tarball/main'
|
|
11
|
+
|
|
12
|
+
const execCommand = (command) => {
|
|
13
|
+
try {
|
|
14
|
+
execSync(command, { stdio: 'inherit' })
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(`${command} failed`, error)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// consume arguments
|
|
24
|
+
const argv = yargs(hideBin(process.argv)).argv
|
|
25
|
+
|
|
26
|
+
if (!argv.name) {
|
|
27
|
+
console.error('WARNING add a project name like so: npx create-epic-graphql-server --name={my-project}')
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const projectPaths = {
|
|
32
|
+
root: path.resolve('.', argv.name),
|
|
33
|
+
get bin() { return path.resolve(this.root, 'bin') },
|
|
34
|
+
get env() { return path.resolve(this.root, '.env') },
|
|
35
|
+
get packageJson() { return path.resolve(this.root, 'package.json') },
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isNameDotCommand = (argv.name === '.')
|
|
39
|
+
const projectName = isNameDotCommand ? path.basename(process.cwd()) : argv.name
|
|
40
|
+
|
|
41
|
+
console.info('')
|
|
42
|
+
console.info(`Your project name is: ${projectName}`)
|
|
43
|
+
console.info('')
|
|
44
|
+
|
|
45
|
+
// create project
|
|
46
|
+
execCommand(`curl -L ${repo} | tar zx --one-top-level=${argv.name} --strip-components 1`)
|
|
47
|
+
|
|
48
|
+
// groom project
|
|
49
|
+
if (packageJson.hasOwnProperty('name')) packageJson.name = projectName
|
|
50
|
+
if (packageJson.hasOwnProperty('version')) packageJson.version = '1.0.0'
|
|
51
|
+
if (packageJson.hasOwnProperty('description')) packageJson.description = ''
|
|
52
|
+
if (packageJson.hasOwnProperty('author')) packageJson.author = ''
|
|
53
|
+
if (packageJson.hasOwnProperty('bin')) delete packageJson.bin
|
|
54
|
+
if (packageJson.hasOwnProperty('repository')) delete packageJson.repository
|
|
55
|
+
if (packageJson.dependencies.hasOwnProperty('yargs')) delete packageJson.dependencies.yargs
|
|
56
|
+
|
|
57
|
+
execCommand(`rm ${projectPaths.packageJson}`)
|
|
58
|
+
execCommand(`rm -rf ${projectPaths.bin}`)
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await writeFile(projectPaths.packageJson, JSON.stringify(packageJson, null, 2), {
|
|
62
|
+
encoding: 'utf8',
|
|
63
|
+
})
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('package.json creation failed', error)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
execCommand(`touch ${projectPaths.env}`)
|
|
70
|
+
|
|
71
|
+
// install project dependencies
|
|
72
|
+
execCommand(`npm --prefix ${projectPaths.root} install`)
|
|
73
|
+
|
|
74
|
+
// finish up
|
|
75
|
+
console.info('')
|
|
76
|
+
console.info('Thanks for using Create Epic GraphQL Server!')
|
|
77
|
+
|
|
78
|
+
process.exit(0)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
run()
|
package/db-seed.json5
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
customers: [
|
|
3
|
+
{
|
|
4
|
+
id: '1',
|
|
5
|
+
name: 'Jane Doe',
|
|
6
|
+
email: 'jane@example.com',
|
|
7
|
+
phone: '555-1234'
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: '2',
|
|
11
|
+
name: 'John Smith',
|
|
12
|
+
email: 'john@example.com',
|
|
13
|
+
phone: '555-9876'
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
orders: [
|
|
17
|
+
{
|
|
18
|
+
id: '1',
|
|
19
|
+
name: 'Product One',
|
|
20
|
+
notes: 'Urgent delivery',
|
|
21
|
+
status: 'Processing',
|
|
22
|
+
customerId: '1'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: '2',
|
|
26
|
+
name: 'Product Two',
|
|
27
|
+
status: 'Not Started',
|
|
28
|
+
customerId: '2'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: '3',
|
|
32
|
+
name: 'Product Three',
|
|
33
|
+
status: 'Not Started',
|
|
34
|
+
customerId: '1'
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-epic-graphql-server",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "a configured graphql server",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-epic-graphql-server": "bin/create-epic-graphql-server.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc --build",
|
|
11
|
+
"start": "node ./build/index.js",
|
|
12
|
+
"dev": "concurrently \"tsx watch ./src/index.ts\" \"npm run test:watch:min\"",
|
|
13
|
+
"dev:db": "dotenv -e .env -- bash -c 'cp db-seed.json5 db.json5 && json-server db.json5 --port ${JSON_SERVER_PORT:-3210}'",
|
|
14
|
+
"test": "mocha --require ts-node/register specs/**/*.ts",
|
|
15
|
+
"test:watch": "mocha --require ts-node/register specs/**/*.ts --watch --watch-files src/**/*.ts,specs/**/*.ts",
|
|
16
|
+
"test:watch:min": "mocha --require ts-node/register specs/**/*.ts --watch --watch-files src/**/*.ts,specs/**/*.ts --reporter min",
|
|
17
|
+
"lint": "eslint src/**/*.{ts,json}",
|
|
18
|
+
"lint:fix": "eslint --fix src/**/*.{ts,json}",
|
|
19
|
+
"prepare": "husky"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"template",
|
|
23
|
+
"server",
|
|
24
|
+
"graphql",
|
|
25
|
+
"node",
|
|
26
|
+
"express",
|
|
27
|
+
"typescript",
|
|
28
|
+
"eslint",
|
|
29
|
+
"mocha",
|
|
30
|
+
"chai",
|
|
31
|
+
"sinon",
|
|
32
|
+
"supertest",
|
|
33
|
+
"javascript"
|
|
34
|
+
],
|
|
35
|
+
"author": "David Kontrovitz",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"chalk": "4.1.2",
|
|
39
|
+
"concurrently": "9.2.0",
|
|
40
|
+
"cors": "2.8.5",
|
|
41
|
+
"dotenv-cli": "8.0.0",
|
|
42
|
+
"dotenv-flow": "4.1.0",
|
|
43
|
+
"express": "5.1.0",
|
|
44
|
+
"graphql": "16.11.0",
|
|
45
|
+
"graphql-http": "1.22.4",
|
|
46
|
+
"helmet": "8.1.0",
|
|
47
|
+
"husky": "9.1.7",
|
|
48
|
+
"json-server": "1.0.0-beta.3",
|
|
49
|
+
"ruru": "2.0.0-beta.22",
|
|
50
|
+
"winston": "3.17.0",
|
|
51
|
+
"winston-daily-rotate-file": "5.0.0",
|
|
52
|
+
"yargs": "17.7.2",
|
|
53
|
+
"zod": "3.24.4"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/chai": "4.3.11",
|
|
57
|
+
"@types/chalk": "0.4.31",
|
|
58
|
+
"@types/dotenv-flow": "3.3.3",
|
|
59
|
+
"@types/express": "5.0.2",
|
|
60
|
+
"@types/mocha": "10.0.10",
|
|
61
|
+
"@types/node": "22.15.18",
|
|
62
|
+
"@types/sinon": "17.0.4",
|
|
63
|
+
"@types/supertest": "6.0.3",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "6.5.0",
|
|
65
|
+
"@typescript-eslint/parser": "6.5.0",
|
|
66
|
+
"chai": "4.3.10",
|
|
67
|
+
"eslint": "8.57.1",
|
|
68
|
+
"mocha": "9.2.2",
|
|
69
|
+
"sinon": "21.0.0",
|
|
70
|
+
"supertest": "7.1.1",
|
|
71
|
+
"ts-node": "10.9.2",
|
|
72
|
+
"tsx": "4.19.4",
|
|
73
|
+
"typescript": "5.8.3"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { expect } from 'chai'
|
|
2
|
+
import { customerSchema } from '../src/models/Customer'
|
|
3
|
+
|
|
4
|
+
describe('customerSchema', () => {
|
|
5
|
+
it('accepts a valid customer object', () => {
|
|
6
|
+
const data = {
|
|
7
|
+
name: 'John Doe',
|
|
8
|
+
email: 'john@example.com',
|
|
9
|
+
phone: '555-1234'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const parsed = customerSchema.parse(data)
|
|
13
|
+
expect(parsed).to.deep.equal(data)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('allows an optional id', () => {
|
|
17
|
+
const data = {
|
|
18
|
+
id: 'cust-1',
|
|
19
|
+
name: 'Jane Doe',
|
|
20
|
+
email: 'jane@example.com',
|
|
21
|
+
phone: '555-5678'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const parsed = customerSchema.parse(data)
|
|
25
|
+
expect(parsed).to.deep.equal(data)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('fails if email is invalid', () => {
|
|
29
|
+
const data = {
|
|
30
|
+
name: 'Invalid Email',
|
|
31
|
+
email: 'not-an-email',
|
|
32
|
+
phone: '555-0000'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
expect(() => customerSchema.parse(data)).to.throw()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('fails if required fields are missing', () => {
|
|
39
|
+
expect(() => customerSchema.parse({})).to.throw()
|
|
40
|
+
})
|
|
41
|
+
})
|