Available templates: ts, esm and cjs
21 |
22 | Usage example:
23 | node-mongo test-folder ts
24 |
25 | Usage example (for shorter command alternative):
26 | nmgo test-folder ts
27 |
28 | The above example bootstraps the ts template i.e.
29 | the typescript template into a folder named test-folder.
30 |
31 | Prompts:
32 | If you do not specify one or both arguments above,
33 | you will be prompted to add your folder name and/or
34 | choose template option from list. For foldername, you
35 | can choose to use the default foldername provided in
36 | the prompt or type in your preferred folder name.
37 |
38 | Flags:
39 | -h, --help Show help
40 | -v, --version Show version number
41 | -i, --install Install dependencies
42 | -g, --git Initialize git repo
43 | -s, --skip-install Skip installing dependencies
44 | -x, --skip-git Skip initializing git
45 | -y, --yes No prompt: see note on --yes flag below
46 |
47 | No prompt when --yes flag is used. It skips both install
48 | and git init. The CLI will generate the (default) ts template
49 | if no template is specified or if the template entered is not
50 | in the template collection. In the case of folder name, default
51 | foldername is used if no folder name is specified or when the
52 | folder name used already exists.
53 | `
54 | );
55 | }
56 |
57 | export const notRecognised = () => {
58 | console.log(
59 | `
60 | Flag(s) not recognised. Use any of the help command below for more info:
61 |
62 | Help command:
63 | node-mongo --help
64 |
65 | Help command (for shorter command alternative):
66 | nmgo --help`
67 | );
68 | }
69 |
70 | export const userSupport = () => {
71 | console.log (chalk.yellowBright(
72 | `
73 | It looks like the node-mongo-cli does not work yet for your computer's operating system. Let us know the issue with the CLI on your computer's operating system by reporting it here:
74 | https://github.com/code-collabo/node-mongo-cli/issues/new?assignees=&labels=bug&template=cli-user-error-report.md
75 |
76 | Alternatively, you can download and access the API boilerplate templates from the template's github repo:
77 | https://github.com/code-collabo/node-mongo-api-boilerplate-templates
78 | `)
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | import fs from 'fs';
3 | import ncp from 'ncp';
4 | import path from 'path';
5 | import { promisify } from 'util';
6 | import execa from 'execa';
7 | import Listr from 'listr';
8 | import { spawn } from 'child_process';
9 | import { userSupport } from './help';
10 |
11 | const access = promisify(fs.access);
12 | const copy = promisify(ncp);
13 |
14 | let createGitIgnoreFile = async (options) => {
15 | const content = '# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n#.env file\n.env\n\n# dependencies\n/node_modules';
16 | fs.writeFileSync(path.join(options.targetDirectory, '.gitignore'), content);
17 | return;
18 | }
19 |
20 | let createEnvFiles = async (options) => {
21 | let atlasPort;
22 | let localPort;
23 | const assignPort = () => {
24 | if (options.template === 'ts') {
25 | atlasPort = 8070;
26 | localPort = 8071;
27 | }
28 | if (options.template === 'esm') {
29 | atlasPort = 8080;
30 | localPort = 8081;
31 | }
32 | if (options.template === 'cjs') {
33 | atlasPort = 8090;
34 | localPort = 8091;
35 | }
36 | }
37 | assignPort();
38 | const content = `\nPORT_ATLAS=${atlasPort}\nMONGODB_ATLAS_URI=\n\nPORT_LOCAL=${localPort}\nMONGODB_LOCAL_URI=\n\nCLIENT_APP_PORT=\nCLIENT_APP_URL=\n`;
39 | fs.writeFileSync(path.join(options.targetDirectory, '.env.example'), content);
40 | return;
41 | }
42 |
43 | let createLisenceFiles = async (options) => {
44 | const content = 'ISC License (ISC)\n\nCopyright 2022-2023 Author Name\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.';
45 | fs.writeFileSync(path.join(options.targetDirectory, 'LICENSE'), content);
46 | return;
47 | }
48 |
49 | let copyTemplateFolderContent = async (options) => {
50 | return copy(options.templateDirectory, options.targetDirectory, {
51 | clobber: false
52 | });
53 | }
54 |
55 | let gitInit = async (options) => {
56 | if (options.git) { //git init only if git returns true
57 | const result = await execa('git', ['init'], {
58 | cwd: options.targetDirectory
59 | });
60 |
61 | if (result.failed) {
62 | return Promise.reject(new Error('Failed to initialize git'));
63 | }
64 | }
65 |
66 | return;
67 | }
68 |
69 | let npmInstall = async (options) => {
70 | if (options.runInstall) { //install only if runInstall returns true
71 | if (process.platform === 'win32') spawn('npm.cmd', ['install'], { cwd: options.targetDirectory, stdio: 'inherit' });
72 | else spawn('npm', ['install'], {cwd: options.targetDirectory, stdio: 'inherit'});
73 | }
74 |
75 | return;
76 | }
77 |
78 | export let downloadTemplateKit = async (options) => {
79 | options = {
80 | ...options,
81 | targetDirectory: options.targetDirectory || process.cwd() //root/parent folder at this point
82 | };
83 |
84 | //create folder with user input here...
85 | fs.mkdirSync(`./${options.folderName}`, { recursive: true });
86 |
87 | //change directory from root/parent folder into user's own folder (so that it becomes cwd)
88 | process.chdir(`./${options.folderName}`);
89 | options.targetDirectory = process.cwd();
90 |
91 | const currentFileUrl = import.meta.url;
92 |
93 | let newUrl;
94 | if (process.platform === "darwin" || process.platform === "linux") {
95 | newUrl = new URL(currentFileUrl).pathname;
96 | } else
97 | newUrl = new URL(currentFileUrl).pathname.substring(
98 | new URL(currentFileUrl).pathname.indexOf("/") + 1
99 | );
100 |
101 | const templateDir = path.resolve(newUrl, '../../templates', options.template.toLowerCase());
102 |
103 | options.templateDirectory = templateDir;
104 |
105 | try {
106 | await access(templateDir, fs.constants.R_OK).then(_ => {
107 | /* rename name option in package.json same as project/folder name */
108 | execa('npx', ['npe','name',options.folderName], {
109 | cwd: options.targetDirectory
110 | }).stdout.pipe(process.stdout);
111 | })
112 |
113 | }catch (err) {
114 | console.error(`\n%s Template name or directory path is (probably) incorrect`, chalk.red.bold('ERROR'));
115 | userSupport();
116 | process.exit(1);
117 | }
118 |
119 | await createGitIgnoreFile(options);
120 |
121 | await createEnvFiles(options);
122 |
123 | await createLisenceFiles(options);
124 |
125 | const listrTasks = new Listr([
126 | {
127 | title: `${chalk.green(`${options.template} template`)} copied into the generated folder ${chalk.green(`=> ${options.folderName}`)}`,
128 | task: () => copyTemplateFolderContent(options)
129 | },
130 | {
131 | title: 'git init',
132 | task: () => gitInit(options),
133 | skip: () => !options.git ? 'Skipped because you specified either --skip-git or --yes flags' : undefined
134 | },
135 | {
136 | title: 'npm install',
137 | task: () => npmInstall(options),
138 | skip: () => !options.runInstall ? 'Skipped because you specified either --skip-install or --yes flags' : undefined
139 | }
140 | ]);
141 |
142 | await listrTasks.run();
143 |
144 | return true;
145 | }
146 |
--------------------------------------------------------------------------------
/src/prompts/foldername.js:
--------------------------------------------------------------------------------
1 | import inquirer from 'inquirer';
2 | import chalk from 'chalk';
3 | import fs from 'fs';
4 |
5 | export const folderNameMissingOptionPrompt = async (options) => {
6 | let defaultFolderName = 'nm-kit';
7 | const folderQuestions = [];
8 |
9 | let questionPush = (msgString, folder) => {
10 | folderQuestions.push({
11 | type: 'input',
12 | name: 'folderName',
13 | message: msgString,
14 | default: folder
15 | });
16 | }
17 |
18 | const rootDir = process.cwd();
19 | const rootDirContent = fs.readdirSync(rootDir, (err, files) => {
20 | if (err) {
21 | throw err;
22 | }
23 |
24 | return files;
25 | });
26 |
27 | rootDirContent.push('');
28 |
29 | let matchDefaultValue = rootDirContent.filter(content => {
30 | return content.match(defaultFolderName);
31 | });
32 |
33 | let folderNameAnswers;
34 |
35 | let extractedNumbers = matchDefaultValue.map((value) => {
36 | let valueMatch = value.match(/(\d+)/);
37 | if (valueMatch !== null) return value.match(/(\d+)/).map(Number)[0];
38 | }).filter(value => { return value !== undefined; });
39 |
40 | let maxNumber = Math.max(...extractedNumbers);
41 |
42 | let incrementFolderName = () => {
43 | if (matchDefaultValue.length >= 1) {
44 | if (maxNumber === -Infinity) defaultFolderName = `${defaultFolderName}-${matchDefaultValue.length}`;
45 | else defaultFolderName = `${defaultFolderName}-${maxNumber + 1}`;
46 | }
47 | }
48 |
49 | if (!options.folderName && options.skipPrompts) incrementFolderName();
50 |
51 | if (!options.folderName && !options.skipPrompts) {
52 | incrementFolderName();
53 | questionPush('Please enter folder name:', defaultFolderName);
54 | folderNameAnswers = await inquirer.prompt(folderQuestions);
55 | }
56 |
57 | if (options.folderName && !options.skipPrompts) {
58 | try {
59 | fs.accessSync(`./${options.folderName}`, fs.constants.F_OK);
60 | console.log( chalk.cyanBright(`Folder name ${chalk.green(`${options.folderName}`)} already exists`) );
61 | questionPush( 'Enter different folder name:', null);
62 | folderNameAnswers = await inquirer.prompt(folderQuestions);
63 | } catch (err) {
64 | if (err) {
65 | folderNameAnswers = {folderName: ''};
66 | folderNameAnswers.folderName = options.folderName;
67 | }
68 | }
69 | }
70 |
71 | try {
72 | fs.accessSync(`./${folderNameAnswers.folderName}`, fs.constants.F_OK);
73 |
74 | let equalToAtLeastOneFolder;
75 |
76 | do {
77 | equalToAtLeastOneFolder = rootDirContent.some(content => {
78 | return content === folderNameAnswers.folderName;
79 | });
80 |
81 | if (equalToAtLeastOneFolder === true) {
82 | if (folderNameAnswers.folderName !== '') {
83 | console.log( `Folder name ${chalk.green(`${folderNameAnswers.folderName}`)} already exists` );
84 | } else {
85 | console.log( `${chalk.yellowBright(`Folder name cannot be empty`)}` );
86 | }
87 | folderQuestions.push({
88 | type: 'input',
89 | name: 'folderName',
90 | message: 'Enter different folder name:',
91 | });
92 | if (options.folderName) {
93 | folderNameAnswers = await inquirer.prompt(folderQuestions);
94 | } else {
95 | folderNameAnswers = await inquirer.prompt([folderQuestions[1]]);
96 | }
97 | }
98 | } while (equalToAtLeastOneFolder === true);
99 |
100 | } catch (err) {
101 | if (err) {
102 | //Dummy if statement to prevent: unhandledPromiseRejectionWarning in console
103 | }
104 | }
105 |
106 | //Note: This affects only the try block of the previous if (options.folderName && !options.skipPrompts) statement
107 | if (options.folderName && !options.skipPrompts) {
108 | options.folderName = folderNameAnswers.folderName;
109 | } //------------------------------------------------
110 |
111 | if (options.folderName && options.skipPrompts) {
112 | let matchFolderNameArg = rootDirContent.some(content => {
113 | return content === options.folderName;
114 | });
115 |
116 | if (matchFolderNameArg) {
117 | options.folderName = incrementFolderName();
118 | }
119 | }
120 |
121 | return [options, folderNameAnswers, defaultFolderName];
122 | }
--------------------------------------------------------------------------------
/src/prompts/template.js:
--------------------------------------------------------------------------------
1 | import inquirer from 'inquirer';
2 | import chalk from 'chalk';
3 |
4 | let skipPromptsModified = (options, defaultFolderName, notAmongTemplateCollection, defaultTemplate) => {
5 | if (notAmongTemplateCollection && (options.template !== undefined || options.template === undefined)) {
6 | options.template = defaultTemplate;
7 | }
8 |
9 | options = {
10 | ...options,
11 | folderName: options.folderName || defaultFolderName,
12 | template: options.template,
13 | runInstall: false,
14 | git: false
15 | }
16 |
17 | return options;
18 | }
19 |
20 | export const templateMissingOptionPrompt = async (options, folderNameAnswers, defaultFolderName) => {
21 | const templateQuestions = [];
22 |
23 | const apiTemplate = {
24 | ts: {
25 | name: 'typescript API template',
26 | abbrev: 'ts',
27 | },
28 | esm: {
29 | name: 'es Module API template',
30 | abbrev: 'esm',
31 | },
32 | cjs: {
33 | name: 'commonjs module API template',
34 | abbrev: 'cjs',
35 | }
36 | }
37 |
38 | const defaultTemplate = `${apiTemplate.ts.name} (${apiTemplate.ts.abbrev})`;
39 | const esmTemplate = `${apiTemplate.esm.name} (${apiTemplate.esm.abbrev})`;
40 | const cjsTemplate = `${apiTemplate.cjs.name} (${apiTemplate.cjs.abbrev})`;
41 |
42 | const templateCollection = [defaultTemplate, esmTemplate, cjsTemplate];
43 |
44 | // Use the abbrev template names for .some operation (for when template name is added through command line)
45 | const abbrevTemplateCollection = [apiTemplate.ts.abbrev, apiTemplate.esm.abbrev, apiTemplate.cjs.abbrev];
46 | const equalToAtLeastOneTemplate = abbrevTemplateCollection.some(tc => {
47 | return tc === options.template;
48 | });
49 |
50 | const notAmongTemplateCollection = equalToAtLeastOneTemplate === false;
51 |
52 | if (notAmongTemplateCollection && options.template !== undefined && !options.skipPrompts) console.log(`${chalk.redBright(`${options.template}`)} is not in the node-mongo template collection`);
53 |
54 | if (!options.template || notAmongTemplateCollection) {
55 | templateQuestions.push({
56 | type: 'list',
57 | name: 'template',
58 | message: 'Please choose which node-mongo API boilerplate template to use',
59 | choices: templateCollection, //uses full names of templates in prompt for better user experience
60 | default: defaultTemplate
61 | });
62 | }
63 |
64 | let templateAnswers;
65 |
66 | if (options.skipPrompts) {
67 | options = skipPromptsModified(options, defaultFolderName, notAmongTemplateCollection, defaultTemplate);
68 | templateQuestions.template = defaultTemplate;
69 | templateAnswers = templateQuestions;
70 | }
71 |
72 | if (!options.skipPrompts) templateAnswers = await inquirer.prompt(templateQuestions);
73 |
74 | // Transform template name/answers back to abbrev name for cli to process it accordingly
75 | const transformTemplateName = (templateOption) => {
76 | if (templateOption === defaultTemplate) templateAnswers.template = apiTemplate.ts.abbrev;
77 | if (templateOption === esmTemplate) templateAnswers.template = apiTemplate.esm.abbrev;
78 | if (templateOption === cjsTemplate) templateAnswers.template = apiTemplate.cjs.abbrev;
79 | }
80 | transformTemplateName(templateAnswers.template);
81 |
82 | if (notAmongTemplateCollection) {
83 | options = {
84 | ...options,
85 | folderName: options.folderName || folderNameAnswers.folderName,
86 | template: templateAnswers.template,
87 | git: options.git
88 | }
89 | }
90 |
91 | options = {
92 | ...options,
93 | folderName: options.folderName || folderNameAnswers.folderName,
94 | template: options.template || templateAnswers.template,
95 | git: options.git
96 | }
97 |
98 | return options;
99 | }
100 |
--------------------------------------------------------------------------------
/src/version.js:
--------------------------------------------------------------------------------
1 | import package_json from '../package.json';
2 |
3 | export const version = () => {
4 | console.log(
5 | `
6 | node-mongo-cli v${package_json.version}
7 | `
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/templates/cjs/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | end_of_line = lf
8 | indent_size = 2
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.js]
13 | quote_type = single
14 |
15 | [*.md]
16 | # max_line_length = off
17 | trim_trailing_whitespace = true
18 |
--------------------------------------------------------------------------------
/templates/cjs/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:import/warnings",
6 | "plugin:import/warnings"
7 | ],
8 | "parserOptions": {
9 | "ecmaVersion": 2017,
10 | "sourceType": "module"
11 | },
12 | "env": {
13 | "browser": true,
14 | "node": true
15 | },
16 | "rules": {
17 | "no-console": 1
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/templates/cjs/README.md:
--------------------------------------------------------------------------------
1 | # @code-collabo's node-mongo API boilerplate template (commonjs module)
2 |
3 | This project (@code-collabo/node-mongo-api-boilerplate-templates version 1.0.0) was generated with [@code-collabo/node-mongo-cli version 2.0.0](https://code-collabo.gitbook.io/node-mongo-v2.0.0).
4 |
5 | ## Connection option 1: Running the development server (mongoDB Atlas)
6 | #### Step 1
7 | Install dependencies:
8 | ````
9 | npm install
10 | ````
11 |
12 | #### Step 2
13 | - Ensure you have internet connection
14 | - Have a monogDB atlas cluster set up in the cloud
15 | - Get your atlas mongoDB uri string
16 |
17 | #### Step 3
18 | - Rename the `.env.example` file to `.env`
19 | - Change `PORT_ATLAS` environment variable to your preferred port number in the .env file
20 | - Add your atlas mongoDB uri string to the `MONGODB_ATLAS_URI` environment variable in the .env file
21 |
22 | #### Step 4
23 | Start the automated development server and choose ATLAS connection:
24 | ````
25 | npm run dev
26 | ````
27 |
28 | #### Step 4 (alternative)
29 | You can also use the (manual) development server alternative for connection to mongoDB atlas:
30 | ````
31 | npm run dev:atlas
32 | ````
33 |
34 | ## Connection option 2: Running the development server (mongoDB local)
35 | #### Step 1
36 | Install dependencies:
37 | ````
38 | npm install
39 | ````
40 |
41 | #### Step 2
42 | - Have mongoDB installed and running on your computer
43 | - Get your local mongoDB uri string
44 |
45 | #### Step 3
46 | - Rename the `.env.example` file to `.env`
47 | - Change `PORT_LOCAL` environment variable to your preferred port number in the .env file
48 | - Add your local mongoDB uri string to the `MONGODB_LOCAL_URI` environment variable in the .env file
49 |
50 | #### Step 4
51 | Start the automated development server and choose LOCAL connection:
52 | ````
53 | npm run dev
54 | ````
55 |
56 | #### Step 4 (alternative)
57 | You can also use the (manual) development server alternative for connection to local mongoDB:
58 | ````
59 | npm run dev:local
60 | ````
61 |
62 | ## Automated development server and commands
63 | - `npm run dev` is the command that starts the automated development server. It prompts you to choose your preferred connection setup type the first time you use it, and saves the chosen connection setup type for every other time you come back to use it. It also automatically installs or set up the db and server files for the chosen connection setup type.
64 | - `npm run dev:restore` resets the automated development server back to first time usage condition i.e. it removes your previously saved connection setup type and the development server will now assume that you are a first timer. After using this command, you will now have the option to set your preferred connection type again the next time you start the server with the `npm run dev` command.
65 | - `npm run dev:change` is useful for when you are not a first time user and want to change your connection set up type without restoring the automated development server to first time usage condition. It will prompt you to choose your connection setup type, but it will not install the db and server files for the chosen connection setup type.
66 |
67 | ## Testing with the demo setup
68 | A demo setup (i.e. collection, endpoints etc) already exists to help you get started with using the node-mongo API boilerplate templates. Running the demo setup will help you understand how to create your own collection endpoints etc. The API design and API call requests and responses sections below will help you understand how the demo setup works.
69 |
70 | ## API design
71 |
72 | |METHOD /endpoint|Description|Request body|
73 | |--|--|:--:|
74 | |GET /demo|Get all demo items in the database| No Request Body |
75 | |POST /demo|Create/add new demo item to the database|name, age|
76 | |GET /demo/:demoId|Get a demo item stored in the database by its ID|No Request Body|
77 | |PATCH /demo/:demoId|Update the value of one property of an already existing demo item in the database, using the demo item's ID|propName, value|
78 | |PUT /demo/:id|Update all properties of an existing demo item in the database, using the demo item's ID|name, age|
79 | |DELETE /demo/:demoId|Delete a demo item from the database, using the demo item's ID|No request body|
80 |
81 | ## API call requests and responses
82 |
83 |
84 | GET /demo
85 |
86 | Request body shape
87 |
88 |
89 | No request body
90 |
91 |
92 | Successful response shape
93 |
94 |
95 | {
96 | "count": number,
97 | "items": [
98 | {
99 | "_id": "string",
100 | "name": "string",
101 | "age": number,
102 | "request": {
103 | "type": "string",
104 | "url": "string"
105 | }
106 | },
107 | // etc.
108 | ]
109 | }
110 |
111 |
112 |
113 |
114 |
115 |
116 | POST /demo
117 |
118 | Request body shape
119 |
120 |
121 | {
122 | "name": "string",
123 | "age": number
124 | }
125 |
126 |
127 | Successful response shape
128 |
129 |
130 | {
131 | "message": "string",
132 | "newItem": {
133 | "_id": "string",
134 | "name": "string",
135 | "age": number,
136 | "request": {
137 | "type": "string",
138 | "url": "string"
139 | }
140 | }
141 | }
142 |
143 |
144 |
145 |
146 |
147 |
148 | GET /demo/:demoId
149 |
150 | Request body shape
151 |
152 |
153 | No request body
154 |
155 |
156 | Successful response shape
157 |
158 |
159 | {
160 | "_id": "string",
161 | "name": "string",
162 | "age": number,
163 | "request": {
164 | "type": "string",
165 | "description": "string",
166 | "url": "string"
167 | }
168 | }
169 |
170 |
171 |
172 |
173 |
174 |
175 | PATCH /demo/:demoId
176 |
177 | Request body shape
178 |
179 |
180 | [
181 | { "propName": "string", "value": "string" }
182 | ]
183 |
184 |
185 | OR
186 |
187 |
188 | [
189 | { "propName": "string", "value": number }
190 | ]
191 |
192 | i.e. propName can be string "name" or "age". Value is a string when name is the propName, while value is a number when age is the propName.
193 |
194 |
195 | Successful response shape
196 |
197 |
198 | {
199 | "message": "string",
200 | "request": {
201 | "type": "string",
202 | "description": "string",
203 | "url": "string"
204 | }
205 | }
206 |
207 |
208 |
209 |
210 |
211 |
212 | PUT /demo/:id
213 |
214 | Request body shape
215 |
216 |
217 | {
218 | "name": "string",
219 | "age": number
220 | }
221 |
222 |
223 | Successful response shape
224 |
225 |
226 | {
227 | "message": "string",
228 | "request": {
229 | "type": "string",
230 | "description": "string",
231 | "url": "string"
232 | }
233 | }
234 |
235 |
236 |
237 |
238 |
239 |
240 | DELETE /demo/:demoId
241 |
242 | Request body shape
243 |
244 |
245 | No request body
246 |
247 |
248 | Successful response shape
249 |
250 |
251 | {
252 | "message": "string",
253 | "request": {
254 | "type": "string",
255 | "description": "string",
256 | "url": "string",
257 | "body": {
258 | "name": "string",
259 | "age": "string"
260 | }
261 | }
262 | }
263 |
264 |
265 |
266 | ## Documentation
267 | See the links to the official documentation of the node-mongo project and community building it below:
268 | - [Node Mongo documentation](https://code-collabo.gitbook.io/node-mongo-v2.0.0)
269 | - [Code Collabo documentation](https://code-collabo.gitbook.io/community-doc-v1.0.0)
270 |
--------------------------------------------------------------------------------
/templates/cjs/node-mongo-helpers.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require('child_process');
2 | const package_json = require('./package.json');
3 | const chalk = require('chalk');
4 |
5 | /* eslint-disable no-console */
6 |
7 | // console
8 | const success = (message) => {
9 | console.log( chalk.greenBright(message) );
10 | }
11 |
12 | const warning = (message) => {
13 | console.log( chalk.yellowBright(message) );
14 | }
15 |
16 | const error = (message) => {
17 | console.log( chalk.redBright(message) );
18 | }
19 |
20 | // DB connect
21 | const watchEslint = () => {
22 | const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
23 | spawn(npm, ['run', 'lint:watch'], { cwd: './', stdio: 'inherit' });
24 | }
25 |
26 | const server = (serverPort) => {
27 | try {
28 | success(`\nnode-mongo (Commonjs Module) API boilerplate template v${package_json.version}`);
29 | success(`\nServer running at ${serverPort}`);
30 | } catch (err) {
31 | error(`${{ err }}`);
32 | }
33 | }
34 |
35 | const eslintAndServer = (serverPort) => {
36 | watchEslint();
37 | server(serverPort);
38 | }
39 |
40 | const afterAtlasDBconnectSuccessful = (serverPort) => {
41 | success('\nConnected to mongoDB ATLAS');
42 | eslintAndServer(serverPort);
43 | }
44 |
45 | const afterLocalDBconnectSuccessful = (serverPort) => {
46 | success('\nConnected to LOCAL mongoDB');
47 | eslintAndServer(serverPort);
48 | }
49 |
50 | const connectToDBunsuccessful = (err) => {
51 | error(`\nError in DB connection: ${err.message}\n`);
52 | warning('Refer to the node-mongo documentation: https://code-collabo.gitbook.io/node-mongo-v2.0.0\n\nGet further help from Code Collabo Community\'s Node mongo channel: https://matrix.to/#/#collabo-node-mongo:gitter.im\n');
53 | }
54 |
55 |
56 | // export all helper functions together
57 | module.exports = {
58 | success,
59 | warning,
60 | error,
61 | watchEslint,
62 | server,
63 | afterAtlasDBconnectSuccessful,
64 | afterLocalDBconnectSuccessful,
65 | connectToDBunsuccessful,
66 | };
67 |
--------------------------------------------------------------------------------
/templates/cjs/node-mongo.js:
--------------------------------------------------------------------------------
1 | import { chooseNodeMongoApiDBServer } from '@code-collabo/node-mongo-scripts/index';
2 |
3 | chooseNodeMongoApiDBServer();
4 |
5 |
--------------------------------------------------------------------------------
/templates/cjs/node-mongo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pathToCheck": "./src",
3 | "templateName": "cjs"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/cjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-mongo-api-boilerplate-template-cjs",
3 | "version": "1.0.0",
4 | "description": "@code-collabo node-mongo API boilerplate template - common js",
5 | "main": "src/server.[atlas/local].js",
6 | "scripts": {
7 | "dev": "node -r esm node-mongo.js",
8 | "dev:local": "nodemon --exec node src/server.local.js",
9 | "dev:atlas": "nodemon --exec node src/server.atlas.js",
10 | "lint": "esw src --color",
11 | "lint:watch": "npm run lint -- --watch",
12 | "dev:restore": "node -r esm node-mongo.js",
13 | "dev:change": "node -r esm node-mongo.js"
14 | },
15 | "author": "Author Name",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@code-collabo/node-mongo-scripts": "^1.2.0",
19 | "cors": "^2.8.5",
20 | "dotenv": "^16.0.3",
21 | "esm": "^3.2.25",
22 | "express": "^4.18.2",
23 | "mongoose": "^7.0.5",
24 | "morgan": "^1.10.0"
25 | },
26 | "devDependencies": {
27 | "chalk": "^4.1.0",
28 | "eslint": "^8.39.0",
29 | "eslint-plugin-import": "^2.27.5",
30 | "eslint-watch": "^8.0.0",
31 | "nodemon": "^2.0.22"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/controllers/app.controller.js:
--------------------------------------------------------------------------------
1 | const { success } = require('../../../node-mongo-helpers');
2 |
3 | async function getAppController (req, res) {
4 | const message = 'App works!';
5 | success(message);
6 | return res.status(200).json({ message });
7 | }
8 |
9 | module.exports = { getAppController };
10 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/controllers/demo.controller.js:
--------------------------------------------------------------------------------
1 | const {
2 | getDemoItemsService,
3 | createDemoItemService,
4 | getOneDemoItemService,
5 | deleteDemoItemService,
6 | updateOneDemoItemPropertyValueService,
7 | updateDemoItemPropertyValuesService,
8 | } = require('../services/demo.service');
9 | const { success, error } = require('../../../node-mongo-helpers');
10 |
11 | const routeName = 'demo';
12 | const item = `${routeName}-item`;
13 |
14 | let response = {};
15 |
16 | const getDemoItemsController = function (req, res) {
17 | getDemoItemsService()
18 | .then((docs) => {
19 | response = {
20 | count: docs.length,
21 | items: docs.map((doc) => {
22 | return {
23 | _id: doc._id,
24 | name: doc.name,
25 | age: doc.age,
26 | request: {
27 | type: 'GET',
28 | url: `http://localhost:3000/${routeName}/${doc._id}`,
29 | },
30 | };
31 | }),
32 | };
33 | success(`GET request successful!`);
34 | return res.status(200).json(response);
35 | })
36 | .catch((err) => {
37 | error(`Error retriving ${item}s: ${err}`);
38 | res.status(500).json({
39 | error: `${err}`,
40 | });
41 | });
42 | };
43 |
44 | const createDemoItemController = function (req, res) {
45 | createDemoItemService(req)
46 | .then((doc) => {
47 | response = {
48 | message: `${item} created successfully!`,
49 | newItem: {
50 | _id: doc._id,
51 | name: doc.name,
52 | age: doc.age,
53 | request: {
54 | type: 'GET',
55 | url: `http://localhost:3000/${routeName}/${doc._id}`,
56 | },
57 | },
58 | }
59 | success(`${item} CREATED successfully!`);
60 | return res.status(201).json(response);
61 | })
62 | .catch((err) => {
63 | error(`Error saving ${item}: ${err}`);
64 | res.status(500).json({
65 | error: `${err}`,
66 | });
67 | });
68 | };
69 |
70 | const getOneDemoItemController = function (req, res) {
71 | const id = req.params.demoId;
72 | getOneDemoItemService(id)
73 | .then((doc) => {
74 | if (doc) {
75 | response = {
76 | _id: doc._id,
77 | name: doc.name,
78 | age: doc.age,
79 | request: {
80 | type: 'GET',
81 | description: `Url link to all ${item}s`,
82 | url: `http://localhost:3000/${routeName}/`,
83 | },
84 | }
85 | success(`GET request successful!`);
86 | return res.status(200).json(response);
87 | } else {
88 | error('No record found for provided ID');
89 | return res.status(404).json({
90 | message: 'No record found for provided ID',
91 | });
92 | }
93 | })
94 | .catch((err) => {
95 | error(`Error retriving ${item}: ${err}`);
96 | res.status(500).json({
97 | message: 'Invalid ID',
98 | error: `${err}`,
99 | });
100 | });
101 | };
102 |
103 | const deleteDemoItemController = function (req, res) {
104 | const id = req.params.demoId;
105 | deleteDemoItemService(id)
106 | .then(() => {
107 | response = {
108 | message: `${item} deleted successfully!`,
109 | request: {
110 | type: 'POST',
111 | description: 'Url link to make post request to',
112 | url: `http://localhost:3000/${item}/`,
113 | body: {
114 | name: 'String',
115 | age: 'Number',
116 | },
117 | },
118 | }
119 | success(`${item} DELETED successfully!`);
120 | return res.status(200).json(response);
121 | })
122 | .catch((err) => {
123 | error(`Error deleting ${item}: ${err}`);
124 | res.status(500).json({
125 | message: `Error deleting ${item}`,
126 | error: `${err}`,
127 | });
128 | });
129 | };
130 |
131 | const updateOneDemoItemPropertyValueController = function (req, res) {
132 | const id = req.params.demoId;
133 | updateOneDemoItemPropertyValueService(id, req.body)
134 | .then(() => {
135 | response = {
136 | message: 'Patch request successful!',
137 | request: {
138 | type: 'GET',
139 | description: `Url link to updated ${item}`,
140 | url: `http://localhost:3000/${routeName}/${id}`,
141 | },
142 | };
143 | success(`PATCH request for ID ${id} successful!`);
144 | return res.status(200).json(response);
145 | })
146 | .catch((err) => {
147 | error(`Error updating ${item} property & value: ${err}`);
148 | res.status(500).json({
149 | message: `Error updating ${item} property & value`,
150 | error: `${err}`,
151 | });
152 | });
153 | };
154 |
155 | const updateDemoItemPropertyValuesController = function (req, res) {
156 | const id = req.params.id;
157 | updateDemoItemPropertyValuesService(id, req.body)
158 | .then(() => {
159 | response = {
160 | message: `Put request successful!`,
161 | request: {
162 | type: 'GET',
163 | description: `Url link to updated ${item}`,
164 | url: `http://localhost:3000/${routeName}/${id}`,
165 | },
166 | };
167 | success(`PUT request for ID ${id} successful!`);
168 | return res.status(200).json(response);
169 | })
170 | .catch((err) => {
171 | error(`Error updating ${item}: ${err}`);
172 | res.status(500).json({
173 | message: `Error updating ${item}`,
174 | error: `${err}`,
175 | });
176 | });
177 | };
178 |
179 | module.exports = {
180 | getDemoItemsController,
181 | createDemoItemController,
182 | getOneDemoItemController,
183 | deleteDemoItemController,
184 | updateOneDemoItemPropertyValueController,
185 | updateDemoItemPropertyValuesController,
186 | };
187 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/models/demo.model.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const collectionName = 'demo';
4 |
5 | const DemoSchema = mongoose.Schema({
6 | name: { type: String, required: true },
7 | age: { type: Number, required: true }
8 | });
9 |
10 | const DemoModel = mongoose.model(collectionName, DemoSchema, collectionName); //declare collection name a second time to prevent mongoose from pluralizing or adding 's' to the collection name
11 |
12 | module.exports = DemoModel;
13 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/routes/app.route.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const { getAppController} = require('../controllers/app.controller');
3 |
4 | const router = express.Router();
5 |
6 | router.get('/', getAppController);
7 |
8 | module.exports = router;
9 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/routes/demo.route.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const {
3 | getDemoItemsController,
4 | createDemoItemController,
5 | getOneDemoItemController,
6 | deleteDemoItemController,
7 | updateOneDemoItemPropertyValueController,
8 | updateDemoItemPropertyValuesController,
9 | } = require('../controllers/demo.controller');
10 |
11 | const router = express.Router();
12 |
13 | router.get('/', getDemoItemsController);
14 | router.post('/', createDemoItemController);
15 | router.get('/:demoId', getOneDemoItemController);
16 | router.delete('/:demoId', deleteDemoItemController);
17 | router.patch('/:demoId', updateOneDemoItemPropertyValueController);
18 | router.put('/:id', updateDemoItemPropertyValuesController);
19 |
20 | module.exports = router;
21 |
--------------------------------------------------------------------------------
/templates/cjs/src/api/services/demo.service.js:
--------------------------------------------------------------------------------
1 | const Demo = require('../models/demo.model');
2 |
3 | /* eslint-disable no-extra-semi */ // Note: only cjs api template needs this particular eslint comment
4 |
5 | function getDemoItemsService () {
6 | const query = Demo.find().select('_id name age').exec();
7 | return query;
8 | };
9 |
10 | function createDemoItemService (req) {
11 | const demo = new Demo({
12 | name: req.body.name,
13 | age: req.body.age,
14 | });
15 | return demo.save();
16 | };
17 |
18 | function getOneDemoItemService (paramsId) {
19 | const query = Demo.findById(paramsId).select('_id name age').exec();
20 | return query;
21 | };
22 |
23 | function deleteDemoItemService (paramsId) {
24 | const query = Demo.deleteOne({ _id: paramsId }).exec();
25 | return query;
26 | };
27 |
28 | function updateOneDemoItemPropertyValueService (paramsId, requestBody) {
29 | const updateOps = {};
30 | for (const ops of requestBody) {
31 | updateOps[ops.propName] = ops.value;
32 | }
33 | const query = Demo.updateOne({ _id: paramsId }, { $set: updateOps }).exec();
34 | return query;
35 | };
36 |
37 | function updateDemoItemPropertyValuesService (paramsId, requestBody) {
38 | const resetItem = {
39 | name: requestBody.name,
40 | age: requestBody.age,
41 | };
42 | const query = Demo.findByIdAndUpdate(paramsId, { $set: resetItem }, { new: true }).exec();
43 | return query;
44 | };
45 |
46 | module.exports = {
47 | getDemoItemsService,
48 | createDemoItemService,
49 | getOneDemoItemService,
50 | deleteDemoItemService,
51 | updateOneDemoItemPropertyValueService,
52 | updateDemoItemPropertyValuesService,
53 | };
54 |
--------------------------------------------------------------------------------
/templates/cjs/src/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const dotenv = require('dotenv');
3 | const morgan = require('morgan');
4 | const cors = require('cors');
5 | let appRouter = require('./api/routes/app.route');
6 | let demoRouter = require('./api/routes/demo.route');
7 |
8 | dotenv.config();
9 |
10 | const app = express();
11 |
12 | app.use(morgan('dev'));
13 | app.use(express.urlencoded({
14 | extended: false
15 | }));
16 | app.use(express.json());
17 | app.use(cors({ origin: [`http://localhost:${process.env.CLIENT_APP_PORT}`, `${process.env.CLIENT_APP_URL}`] }));
18 |
19 | //====== Use Routers =======
20 | app.use('/', appRouter);
21 | app.use('/demo', demoRouter);
22 | //==========================
23 |
24 | app.use(function (req, res, next) {
25 | const error = new Error('Route not found!');
26 | error.status = 404;
27 | next(error);
28 | });
29 |
30 | app.use(function (error, req, res) {
31 | res.status(error.status || 500);
32 | res.json({
33 | error: {
34 | message: error.message
35 | }
36 | });
37 | });
38 |
39 | module.exports = { app };
40 |
--------------------------------------------------------------------------------
/templates/cjs/src/db.atlas.connect.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const dotenv = require('dotenv');
3 | const { afterAtlasDBconnectSuccessful, connectToDBunsuccessful } = require('../node-mongo-helpers');
4 |
5 | dotenv.config();
6 |
7 | const mongooseAtlasConnect = async function (port) {
8 | try {
9 | await mongoose.connect(`${process.env.MONGODB_ATLAS_URI}`);
10 | afterAtlasDBconnectSuccessful(port);
11 | } catch (err) {
12 | connectToDBunsuccessful(err);
13 | }
14 | }
15 |
16 | module.exports = mongooseAtlasConnect;
17 |
--------------------------------------------------------------------------------
/templates/cjs/src/server.atlas.js:
--------------------------------------------------------------------------------
1 | const mongooseAtlasConnect = require('./db.atlas.connect');
2 | const { app } = require('./app');
3 |
4 | const port = process.env.PORT_ATLAS || 3000;
5 |
6 | app.listen(port, function () {
7 | mongooseAtlasConnect(port);
8 | });
9 |
--------------------------------------------------------------------------------
/templates/esm/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/templates/esm/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | end_of_line = lf
8 | indent_size = 2
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.js]
13 | quote_type = single
14 |
15 | [*.md]
16 | # max_line_length = off
17 | trim_trailing_whitespace = true
18 |
--------------------------------------------------------------------------------
/templates/esm/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@babel/eslint-parser",
4 | "extends": [
5 | "eslint:recommended",
6 | "plugin:import/warnings",
7 | "plugin:import/warnings"
8 | ],
9 | "parserOptions": {
10 | "ecmaVersion": 7,
11 | "requireConfigFile": false,
12 | "sourceType": "module"
13 | },
14 | "env": {
15 | "browser": true,
16 | "node": true
17 | },
18 | "rules": {
19 | "no-console": 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/templates/esm/README.md:
--------------------------------------------------------------------------------
1 | # @code-collabo's node-mongo API boilerplate template (es module)
2 |
3 | This project (@code-collabo/node-mongo-api-boilerplate-templates version 1.0.0) was generated with [@code-collabo/node-mongo-cli version 2.0.0](https://code-collabo.gitbook.io/node-mongo-v2.0.0).
4 |
5 | ## Connection option 1: Running the development server (mongoDB Atlas)
6 | #### Step 1
7 | Install dependencies:
8 | ````
9 | npm install
10 | ````
11 |
12 | #### Step 2
13 | - Ensure you have internet connection
14 | - Have a monogDB atlas cluster set up in the cloud
15 | - Get your atlas mongoDB uri string
16 |
17 | #### Step 3
18 | - Rename the `.env.example` file to `.env`
19 | - Change `PORT_ATLAS` environment variable to your preferred port number in the .env file
20 | - Add your atlas mongoDB uri string to the `MONGODB_ATLAS_URI` environment variable in the .env file
21 |
22 | #### Step 4
23 | Start the automated development server and choose ATLAS connection:
24 | ````
25 | npm run dev
26 | ````
27 |
28 | #### Step 4 (alternative)
29 | You can also use the (manual) development server alternative for connection to mongoDB atlas:
30 | ````
31 | npm run dev:atlas
32 | ````
33 |
34 | ## Connection option 2: Running the development server (mongoDB local)
35 | #### Step 1
36 | Install dependencies:
37 | ````
38 | npm install
39 | ````
40 |
41 | #### Step 2
42 | - Have mongoDB installed and running on your computer
43 | - Get your local mongoDB uri string
44 |
45 | #### Step 3
46 | - Rename the `.env.example` file to `.env`
47 | - Change `PORT_LOCAL` environment variable to your preferred port number in the .env file
48 | - Add your local mongoDB uri string to the `MONGODB_LOCAL_URI` environment variable in the .env file
49 |
50 | #### Step 4
51 | Start the automated development server and choose LOCAL connection:
52 | ````
53 | npm run dev
54 | ````
55 |
56 | #### Step 4 (alternative)
57 | You can also use the (manual) development server alternative for connection to local mongoDB:
58 | ````
59 | npm run dev:local
60 | ````
61 |
62 | ## Automated development server and commands
63 | - `npm run dev` is the command that starts the automated development server. It prompts you to choose your preferred connection setup type the first time you use it, and saves the chosen connection setup type for every other time you come back to use it. It also automatically installs or set up the db and server files for the chosen connection setup type.
64 | - `npm run dev:restore` resets the automated development server back to first time usage condition i.e. it removes your previously saved connection setup type and the development server will now assume that you are a first timer. After using this command, you will now have the option to set your preferred connection type again the next time you start the server with the `npm run dev` command.
65 | - `npm run dev:change` is useful for when you are not a first time user and want to change your connection set up type without restoring the automated development server to first time usage condition. It will prompt you to choose your connection setup type, but it will not install the db and server files for the chosen connection setup type.
66 |
67 | ## Testing with the demo setup
68 | A demo setup (i.e. collection, endpoints etc) already exists to help you get started with using the node-mongo API boilerplate templates. Running the demo setup will help you understand how to create your own collection endpoints etc. The API design and API call requests and responses sections below will help you understand how the demo setup works.
69 |
70 | ## API design
71 |
72 | |METHOD /endpoint|Description|Request body|
73 | |--|--|:--:|
74 | |GET /demo|Get all demo items in the database| No Request Body |
75 | |POST /demo|Create/add new demo item to the database|name, age|
76 | |GET /demo/:demoId|Get a demo item stored in the database by its ID|No Request Body|
77 | |PATCH /demo/:demoId|Update the value of one property of an already existing demo item in the database, using the demo item's ID|propName, value|
78 | |PUT /demo/:id|Update all properties of an existing demo item in the database, using the demo item's ID|name, age|
79 | |DELETE /demo/:demoId|Delete a demo item from the database, using the demo item's ID|No request body|
80 |
81 | ## API call requests and responses
82 |
83 |
84 | GET /demo
85 |
86 | Request body shape
87 |
88 |
89 | No request body
90 |
91 |
92 | Successful response shape
93 |
94 |
95 | {
96 | "count": number,
97 | "items": [
98 | {
99 | "_id": "string",
100 | "name": "string",
101 | "age": number,
102 | "request": {
103 | "type": "string",
104 | "url": "string"
105 | }
106 | },
107 | // etc.
108 | ]
109 | }
110 |
111 |
112 |
113 |
114 |
115 |
116 | POST /demo
117 |
118 | Request body shape
119 |
120 |
121 | {
122 | "name": "string",
123 | "age": number
124 | }
125 |
126 |
127 | Successful response shape
128 |
129 |
130 | {
131 | "message": "string",
132 | "newItem": {
133 | "_id": "string",
134 | "name": "string",
135 | "age": number,
136 | "request": {
137 | "type": "string",
138 | "url": "string"
139 | }
140 | }
141 | }
142 |
143 |
144 |
145 |
146 |
147 |
148 | GET /demo/:demoId
149 |
150 | Request body shape
151 |
152 |
153 | No request body
154 |
155 |
156 | Successful response shape
157 |
158 |
159 | {
160 | "_id": "string",
161 | "name": "string",
162 | "age": number,
163 | "request": {
164 | "type": "string",
165 | "description": "string",
166 | "url": "string"
167 | }
168 | }
169 |
170 |
171 |
172 |
173 |
174 |
175 | PATCH /demo/:demoId
176 |
177 | Request body shape
178 |
179 |
180 | [
181 | { "propName": "string", "value": "string" }
182 | ]
183 |
184 |
185 | OR
186 |
187 |
188 | [
189 | { "propName": "string", "value": number }
190 | ]
191 |
192 | i.e. propName can be string "name" or "age". Value is a string when name is the propName, while value is a number when age is the propName.
193 |
194 |
195 | Successful response shape
196 |
197 |
198 | {
199 | "message": "string",
200 | "request": {
201 | "type": "string",
202 | "description": "string",
203 | "url": "string"
204 | }
205 | }
206 |
207 |
208 |
209 |
210 |
211 |
212 | PUT /demo/:id
213 |
214 | Request body shape
215 |
216 |
217 | {
218 | "name": "string",
219 | "age": number
220 | }
221 |
222 |
223 | Successful response shape
224 |
225 |
226 | {
227 | "message": "string",
228 | "request": {
229 | "type": "string",
230 | "description": "string",
231 | "url": "string"
232 | }
233 | }
234 |
235 |
236 |
237 |
238 |
239 |
240 | DELETE /demo/:demoId
241 |
242 | Request body shape
243 |
244 |
245 | No request body
246 |
247 |
248 | Successful response shape
249 |
250 |
251 | {
252 | "message": "string",
253 | "request": {
254 | "type": "string",
255 | "description": "string",
256 | "url": "string",
257 | "body": {
258 | "name": "string",
259 | "age": "string"
260 | }
261 | }
262 | }
263 |
264 |
265 |
266 | ## Documentation
267 | See the links to the official documentation of the node-mongo project and community building it below:
268 | - [Node Mongo documentation](https://code-collabo.gitbook.io/node-mongo-v2.0.0)
269 | - [Code Collabo documentation](https://code-collabo.gitbook.io/community-doc-v1.0.0)
270 |
--------------------------------------------------------------------------------
/templates/esm/node-mongo-helpers.js:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process';
2 | import package_json from './package.json';
3 | import chalk from 'chalk';
4 |
5 | /* eslint-disable no-console */
6 |
7 | // console
8 | export const success = (message) => {
9 | console.log( chalk.greenBright(message) );
10 | }
11 |
12 | export const warning = (message) => {
13 | console.log( chalk.yellowBright(message) );
14 | }
15 |
16 | export const error = (message) => {
17 | console.log( chalk.redBright(message) );
18 | }
19 |
20 | // DB connect
21 | export const watchEslint = () => {
22 | const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
23 | spawn(npm, ['run', 'lint:watch'], { cwd: './', stdio: 'inherit' });
24 | }
25 |
26 | export const server = (serverPort) => {
27 | try {
28 | success(`\nnode-mongo (ES Module) API boilerplate template v${package_json.version}`);
29 | success(`\nServer running at ${serverPort}`);
30 | } catch (err) {
31 | error(`${{ err }}`);
32 | }
33 | }
34 |
35 | const eslintAndServer = (serverPort) => {
36 | watchEslint();
37 | server(serverPort);
38 | }
39 |
40 | export const afterAtlasDBconnectSuccessful = (serverPort) => {
41 | success('\nConnected to mongoDB ATLAS');
42 | eslintAndServer(serverPort);
43 | }
44 |
45 | export const afterLocalDBconnectSuccessful = (serverPort) => {
46 | success('\nConnected to LOCAL mongoDB');
47 | eslintAndServer(serverPort);
48 | }
49 |
50 | export const connectToDBunsuccessful = (err) => {
51 | error(`\nError in DB connection: ${err.message}\n`);
52 | warning('Refer to the node-mongo documentation: https://code-collabo.gitbook.io/node-mongo-v2.0.0\n\nGet further help from Code Collabo Community\'s Node mongo channel: https://matrix.to/#/#collabo-node-mongo:gitter.im\n');
53 | }
54 |
--------------------------------------------------------------------------------
/templates/esm/node-mongo.js:
--------------------------------------------------------------------------------
1 | import { chooseNodeMongoApiDBServer } from '@code-collabo/node-mongo-scripts/index';
2 |
3 | chooseNodeMongoApiDBServer();
4 |
5 |
--------------------------------------------------------------------------------
/templates/esm/node-mongo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pathToCheck": "./src",
3 | "templateName": "esm"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/esm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-mongo-api-boilerplate-template-esm",
3 | "version": "1.0.0",
4 | "description": "@code-collabo node-mongo API boilerplate template - es module",
5 | "main": "src/server.[atlas/local].js",
6 | "scripts": {
7 | "dev": "node -r esm node-mongo.js",
8 | "dev:local": "nodemon --exec babel-node src/server.local.js",
9 | "dev:atlas": "nodemon --exec babel-node src/server.atlas.js",
10 | "lint": "esw src --color",
11 | "lint:watch": "npm run lint -- --watch",
12 | "dev:restore": "node -r esm node-mongo.js",
13 | "dev:change": "node -r esm node-mongo.js"
14 | },
15 | "author": "Author Name",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@code-collabo/node-mongo-scripts": "^1.2.0",
19 | "cors": "^2.8.5",
20 | "dotenv": "^16.0.3",
21 | "esm": "^3.2.25",
22 | "express": "^4.18.2",
23 | "mongoose": "^7.0.5",
24 | "morgan": "^1.10.0"
25 | },
26 | "devDependencies": {
27 | "@babel/cli": "^7.21.0",
28 | "@babel/core": "^7.21.4",
29 | "@babel/eslint-parser": "^7.21.3",
30 | "@babel/node": "^7.20.7",
31 | "@babel/preset-env": "^7.21.4",
32 | "chalk": "^4.1.0",
33 | "eslint": "^8.39.0",
34 | "eslint-plugin-import": "^2.27.5",
35 | "eslint-watch": "^8.0.0",
36 | "nodemon": "^2.0.22"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/templates/esm/src/api/controllers/app.controller.js:
--------------------------------------------------------------------------------
1 | import { success } from '../../../node-mongo-helpers';
2 |
3 | export const getAppController = async (req, res) => {
4 | const message = 'App works!';
5 | success(message);
6 | return res.status(200).json({ message });
7 | }
8 |
--------------------------------------------------------------------------------
/templates/esm/src/api/controllers/demo.controller.js:
--------------------------------------------------------------------------------
1 | import {
2 | getDemoItemsService,
3 | createDemoItemService,
4 | getOneDemoItemService,
5 | deleteDemoItemService,
6 | updateOneDemoItemPropertyValueService,
7 | updateDemoItemPropertyValuesService,
8 | } from '../services/demo.service';
9 | import { success, error } from '../../../node-mongo-helpers';
10 |
11 | const routeName = 'demo';
12 | const item = `${routeName}-item`;
13 |
14 | let response = {};
15 |
16 | const getDemoItemsController = async (req, res) => {
17 | try {
18 | const docs = await getDemoItemsService();
19 | response = {
20 | count: docs.length,
21 | items: docs.map((doc) => {
22 | return {
23 | _id: doc._id,
24 | name: doc.name,
25 | age: doc.age,
26 | request: {
27 | type: 'GET',
28 | url: `http://localhost:3000/${routeName}/${doc._id}`,
29 | },
30 | };
31 | }),
32 | };
33 | success(`GET request successful!`);
34 | return res.status(200).json(response);
35 | } catch (err) {
36 | error(`Error retriving ${item}s: ${err}`);
37 | res.status(500).json({
38 | error: `${err}`,
39 | });
40 | }
41 | };
42 |
43 | const createDemoItemController = async (req, res) => {
44 | try {
45 | const doc = await createDemoItemService(req.body);
46 | response = {
47 | message: `${item} created successfully!`,
48 | newItem: {
49 | _id: doc._id,
50 | name: doc.name,
51 | age: doc.age,
52 | request: {
53 | type: 'GET',
54 | url: `http://localhost:3000/${routeName}/${doc._id}`,
55 | },
56 | },
57 | }
58 | success(`${item} CREATED successfully!`);
59 | return res.status(201).json(response);
60 | } catch (err) {
61 | error(`Error saving ${item}: ${err}`);
62 | res.status(500).json({
63 | error: `${err}`,
64 | });
65 | }
66 | };
67 |
68 | const getOneDemoItemController = async (req, res) => {
69 | try {
70 | const doc = await getOneDemoItemService(req.params.demoId);
71 | if (doc) {
72 | response = {
73 | _id: doc._id,
74 | name: doc.name,
75 | age: doc.age,
76 | request: {
77 | type: 'GET',
78 | description: `Url link to all ${item}s`,
79 | url: `http://localhost:3000/${routeName}/`,
80 | },
81 | }
82 | success(`GET request successful!`);
83 | return res.status(200).json(response);
84 | } else {
85 | error('No record found for provided ID');
86 | return res.status(404).json({
87 | message: 'No record found for provided ID',
88 | });
89 | }
90 | } catch (err) {
91 | error(`Error retriving ${item}: ${err}`);
92 | res.status(500).json({
93 | message: 'Invalid ID',
94 | error: `${err}`,
95 | });
96 | }
97 | };
98 |
99 | const deleteDemoItemController = async (req, res) => {
100 | try {
101 | await deleteDemoItemService(req.params.demoId);
102 | response = {
103 | message: `${item} deleted successfully!`,
104 | request: {
105 | type: 'POST',
106 | description: 'Url link to make post request to',
107 | url: `http://localhost:3000/${item}/`,
108 | body: {
109 | name: 'String',
110 | age: 'Number',
111 | },
112 | },
113 | }
114 | success(`${item} DELETED successfully!`);
115 | return res.status(200).json(response);
116 | } catch (err) {
117 | error(`Error deleting ${item}: ${err}`);
118 | res.status(500).json({
119 | message: `Error deleting ${item}`,
120 | error: `${err}`,
121 | });
122 | }
123 | };
124 |
125 | const updateOneDemoItemPropertyValueController = async (req, res) => {
126 | try {
127 | const id = req.params.demoId;
128 | await updateOneDemoItemPropertyValueService(id, req.body);
129 | response = {
130 | message: 'Patch request successful!',
131 | request: {
132 | type: 'GET',
133 | description: `Url link to updated ${item}`,
134 | url: `http://localhost:3000/${routeName}/${id}`,
135 | },
136 | };
137 | success(`PATCH request for ID ${id} successful!`);
138 | return res.status(200).json(response);
139 | } catch (err) {
140 | error(`Error updating ${item} property & value: ${err}`);
141 | res.status(500).json({
142 | message: `Error updating ${item} property & value`,
143 | error: `${err}`,
144 | });
145 | }
146 | };
147 |
148 | const updateDemoItemPropertyValuesController = async (req, res) => {
149 | try {
150 | const id = req.params.id;
151 | await updateDemoItemPropertyValuesService(id, req.body);
152 | response = {
153 | message: `Put request successful!`,
154 | request: {
155 | type: 'GET',
156 | description: `Url link to updated ${item}`,
157 | url: `http://localhost:3000/${routeName}/${id}`,
158 | },
159 | };
160 | success(`PUT request for ID ${id} successful!`);
161 | return res.status(200).json(response);
162 | } catch (err) {
163 | error(`Error updating ${item}: ${err}`);
164 | res.status(500).json({
165 | message: `Error updating ${item}`,
166 | error: `${err}`,
167 | });
168 | }
169 | };
170 |
171 | export {
172 | getDemoItemsController,
173 | createDemoItemController,
174 | getOneDemoItemController,
175 | deleteDemoItemController,
176 | updateOneDemoItemPropertyValueController,
177 | updateDemoItemPropertyValuesController,
178 | };
179 |
--------------------------------------------------------------------------------
/templates/esm/src/api/models/demo.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 |
3 | const collectionName = 'demo';
4 |
5 | const DemoSchema = mongoose.Schema({
6 | name: { type: String, required: true },
7 | age: { type: Number, required: true }
8 | });
9 |
10 | const DemoModel = mongoose.model(collectionName, DemoSchema, collectionName); //declare collection name a second time to prevent mongoose from pluralizing or adding 's' to the collection name
11 |
12 | export { DemoModel };
13 |
--------------------------------------------------------------------------------
/templates/esm/src/api/routes/app.route.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { getAppController } from '../controllers/app.controller';
3 |
4 | const router = express.Router();
5 |
6 | router.get('/', getAppController);
7 |
8 | export { router };
9 |
--------------------------------------------------------------------------------
/templates/esm/src/api/routes/demo.route.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import {
3 | getDemoItemsController,
4 | createDemoItemController,
5 | getOneDemoItemController,
6 | deleteDemoItemController,
7 | updateOneDemoItemPropertyValueController,
8 | updateDemoItemPropertyValuesController,
9 | } from '../controllers/demo.controller';
10 |
11 | const router = express.Router();
12 |
13 | router.get('/', getDemoItemsController);
14 | router.post('/', createDemoItemController);
15 | router.get('/:demoId', getOneDemoItemController);
16 | router.delete('/:demoId', deleteDemoItemController);
17 | router.patch('/:demoId', updateOneDemoItemPropertyValueController);
18 | router.put('/:id', updateDemoItemPropertyValuesController);
19 |
20 | export { router };
21 |
--------------------------------------------------------------------------------
/templates/esm/src/api/services/demo.service.js:
--------------------------------------------------------------------------------
1 | import { DemoModel as Demo } from '../models/demo.model';
2 |
3 | export const getDemoItemsService = async () => {
4 | const query = await Demo.find().select('_id name age').exec();
5 | return query;
6 | };
7 |
8 | export const createDemoItemService = async (requestBody) => {
9 | const demo = new Demo({
10 | name: requestBody.name,
11 | age: requestBody.age,
12 | });
13 | const save = await demo.save();
14 | return save;
15 | };
16 |
17 | export const getOneDemoItemService = async (paramsId) => {
18 | const query = Demo.findById(paramsId).select('_id name age').exec();
19 | return query;
20 | };
21 |
22 | export const deleteDemoItemService = async (paramsId) => {
23 | const query = await Demo.deleteOne({ _id: paramsId }).exec();
24 | return query;
25 | };
26 |
27 | export const updateOneDemoItemPropertyValueService = async (paramsId, requestBody) => {
28 | const updateOps = {};
29 | for (const ops of requestBody) {
30 | updateOps[ops.propName] = ops.value;
31 | }
32 | const query = await Demo.updateOne({ _id: paramsId }, { $set: updateOps }).exec();
33 | return query;
34 | };
35 |
36 | export const updateDemoItemPropertyValuesService = async (paramsId, requestBody) => {
37 | const resetItem = {
38 | name: requestBody.name,
39 | age: requestBody.age,
40 | };
41 | const query = await Demo.findByIdAndUpdate(paramsId, { $set: resetItem }, { new: true }).exec();
42 | return query;
43 | };
44 |
--------------------------------------------------------------------------------
/templates/esm/src/app.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import dotenv from 'dotenv';
3 | import morgan from 'morgan';
4 | import cors from 'cors';
5 | import { router as appRouter } from './api/routes/app.route';
6 | import { router as demoRouter } from './api/routes/demo.route';
7 |
8 | dotenv.config();
9 |
10 | const app = express();
11 |
12 | app.use(morgan('dev'));
13 | app.use(express.urlencoded({
14 | extended: false
15 | }));
16 | app.use(express.json());
17 | app.use(cors({ origin: [`http://localhost:${process.env.CLIENT_APP_PORT}`, `${process.env.CLIENT_APP_URL}`] }));
18 |
19 | //====== Use Routers =======
20 | app.use('/', appRouter);
21 | app.use('/demo', demoRouter);
22 | //==========================
23 |
24 | app.use((req, res, next) => {
25 | const error = new Error('Route not found!');
26 | error.status = 404;
27 | next(error);
28 | });
29 |
30 | app.use((error, req, res) => {
31 | res.status(error.status || 500);
32 | res.json({
33 | error: {
34 | message: error.message
35 | }
36 | });
37 | });
38 |
39 | export { app };
40 |
--------------------------------------------------------------------------------
/templates/esm/src/db.atlas.connect.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 | import dotenv from 'dotenv';
3 | import { afterAtlasDBconnectSuccessful, connectToDBunsuccessful } from '../node-mongo-helpers';
4 |
5 | dotenv.config();
6 |
7 | const mongooseAtlasConnect = async (port) => {
8 | try {
9 | await mongoose.connect(`${process.env.MONGODB_ATLAS_URI}`);
10 | afterAtlasDBconnectSuccessful(port);
11 | } catch (err) {
12 | connectToDBunsuccessful(err);
13 | }
14 | }
15 |
16 | export default mongooseAtlasConnect;
17 |
--------------------------------------------------------------------------------
/templates/esm/src/server.atlas.js:
--------------------------------------------------------------------------------
1 | import mongooseAtlasConnect from './db.atlas.connect';
2 | import { app as app } from './app';
3 |
4 | const port = process.env.PORT_ATLAS || 3000;
5 |
6 | app.listen(port, () => {
7 | mongooseAtlasConnect(port);
8 | });
9 |
--------------------------------------------------------------------------------
/templates/ts/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | end_of_line = lf
8 | indent_size = 2
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.ts]
13 | quote_type = single
14 |
15 | [*.md]
16 | # max_line_length = off
17 | trim_trailing_whitespace = true
18 |
--------------------------------------------------------------------------------
/templates/ts/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "extends": [
5 | "eslint:recommended",
6 | "plugin:@typescript-eslint/recommended"
7 | ],
8 | "parserOptions": {
9 | "ecmaVersion": 12,
10 | "sourceType": "module"
11 | },
12 | "plugins": [
13 | "@typescript-eslint"
14 | ],
15 | "env": {
16 | "browser": true,
17 | "es2021": true
18 | },
19 | "rules": {
20 | "no-console": 1,
21 | "@typescript-eslint/no-unused-vars": "error",
22 | "@typescript-eslint/consistent-type-definitions": ["error"] // you can also add "type" in the array
23 | }
24 | }
--------------------------------------------------------------------------------
/templates/ts/README.md:
--------------------------------------------------------------------------------
1 | # @code-collabo's node-mongo API boilerplate template (typescript)
2 |
3 | This project (@code-collabo/node-mongo-api-boilerplate-templates version 1.0.0) was generated with [@code-collabo/node-mongo-cli version 2.0.0](https://code-collabo.gitbook.io/node-mongo-v2.0.0).
4 |
5 | ## Connection option 1: Running the development server (mongoDB Atlas)
6 | #### Step 1
7 | Install dependencies:
8 | ````
9 | npm install
10 | ````
11 |
12 | #### Step 2
13 | - Ensure you have internet connection
14 | - Have a monogDB atlas cluster set up in the cloud
15 | - Get your atlas mongoDB uri string
16 |
17 | #### Step 3
18 | - Rename the `.env.example` file to `.env`
19 | - Change `PORT_ATLAS` environment variable to your preferred port number in the .env file
20 | - Add your atlas mongoDB uri string to the `MONGODB_ATLAS_URI` environment variable in the .env file
21 |
22 | #### Step 4
23 | Start the automated development server and choose ATLAS connection:
24 | ````
25 | npm run dev
26 | ````
27 |
28 | #### Step 4 (alternative)
29 | You can also use the (manual) development server alternative for connection to mongoDB atlas:
30 | ````
31 | npm run dev:atlas
32 | ````
33 |
34 | ## Connection option 2: Running the development server (mongoDB local)
35 | #### Step 1
36 | Install dependencies:
37 | ````
38 | npm install
39 | ````
40 |
41 | #### Step 2
42 | - Have mongoDB installed and running on your computer
43 | - Get your local mongoDB uri string
44 |
45 | #### Step 3
46 | - Rename the `.env.example` file to `.env`
47 | - Change `PORT_LOCAL` environment variable to your preferred port number in the .env file
48 | - Add your local mongoDB uri string to the `MONGODB_LOCAL_URI` environment variable in the .env file
49 |
50 | #### Step 4
51 | Start the automated development server and choose LOCAL connection:
52 | ````
53 | npm run dev
54 | ````
55 |
56 | #### Step 4 (alternative)
57 | You can also use the (manual) development server alternative for connection to local mongoDB:
58 | ````
59 | npm run dev:local
60 | ````
61 |
62 | ## Automated development server and commands
63 | - `npm run dev` is the command that starts the automated development server. It prompts you to choose your preferred connection setup type the first time you use it, and saves the chosen connection setup type for every other time you come back to use it. It also automatically installs or set up the db and server files for the chosen connection setup type.
64 | - `npm run dev:restore` resets the automated development server back to first time usage condition i.e. it removes your previously saved connection setup type and the development server will now assume that you are a first timer. After using this command, you will now have the option to set your preferred connection type again the next time you start the server with the `npm run dev` command.
65 | - `npm run dev:change` is useful for when you are not a first time user and want to change your connection set up type without restoring the automated development server to first time usage condition. It will prompt you to choose your connection setup type, but it will not install the db and server files for the chosen connection setup type.
66 |
67 | ## Testing with the demo setup
68 | A demo setup (i.e. collection, endpoints etc) already exists to help you get started with using the node-mongo API boilerplate templates. Running the demo setup will help you understand how to create your own collection endpoints etc. The API design and API call requests and responses sections below will help you understand how the demo setup works.
69 |
70 | ## API design
71 |
72 | |METHOD /endpoint|Description|Request body|
73 | |--|--|:--:|
74 | |GET /demo|Get all demo items in the database| No Request Body |
75 | |POST /demo|Create/add new demo item to the database|name, age|
76 | |GET /demo/:demoId|Get a demo item stored in the database by its ID|No Request Body|
77 | |PATCH /demo/:demoId|Update the value of one property of an already existing demo item in the database, using the demo item's ID|propName, value|
78 | |PUT /demo/:id|Update all properties of an existing demo item in the database, using the demo item's ID|name, age|
79 | |DELETE /demo/:demoId|Delete a demo item from the database, using the demo item's ID|No request body|
80 |
81 | ## API call requests and responses
82 |
83 |
84 | GET /demo
85 |
86 | Request body shape
87 |
88 |
89 | No request body
90 |
91 |
92 | Successful response shape
93 |
94 |
95 | {
96 | "count": number,
97 | "items": [
98 | {
99 | "_id": "string",
100 | "name": "string",
101 | "age": number,
102 | "request": {
103 | "type": "string",
104 | "url": "string"
105 | }
106 | },
107 | // etc.
108 | ]
109 | }
110 |
111 |
112 |
113 |
114 |
115 |
116 | POST /demo
117 |
118 | Request body shape
119 |
120 |
121 | {
122 | "name": "string",
123 | "age": number
124 | }
125 |
126 |
127 | Successful response shape
128 |
129 |
130 | {
131 | "message": "string",
132 | "newItem": {
133 | "_id": "string",
134 | "name": "string",
135 | "age": number,
136 | "request": {
137 | "type": "string",
138 | "url": "string"
139 | }
140 | }
141 | }
142 |
143 |
144 |
145 |
146 |
147 |
148 | GET /demo/:demoId
149 |
150 | Request body shape
151 |
152 |
153 | No request body
154 |
155 |
156 | Successful response shape
157 |
158 |
159 | {
160 | "_id": "string",
161 | "name": "string",
162 | "age": number,
163 | "request": {
164 | "type": "string",
165 | "description": "string",
166 | "url": "string"
167 | }
168 | }
169 |
170 |
171 |
172 |
173 |
174 |
175 | PATCH /demo/:demoId
176 |
177 | Request body shape
178 |
179 |
180 | [
181 | { "propName": "string", "value": "string" }
182 | ]
183 |
184 |
185 | OR
186 |
187 |
188 | [
189 | { "propName": "string", "value": number }
190 | ]
191 |
192 | i.e. propName can be string "name" or "age". Value is a string when name is the propName, while value is a number when age is the propName.
193 |
194 |
195 | Successful response shape
196 |
197 |
198 | {
199 | "message": "string",
200 | "request": {
201 | "type": "string",
202 | "description": "string",
203 | "url": "string"
204 | }
205 | }
206 |
207 |
208 |
209 |
210 |
211 |
212 | PUT /demo/:id
213 |
214 | Request body shape
215 |
216 |
217 | {
218 | "name": "string",
219 | "age": number
220 | }
221 |
222 |
223 | Successful response shape
224 |
225 |
226 | {
227 | "message": "string",
228 | "request": {
229 | "type": "string",
230 | "description": "string",
231 | "url": "string"
232 | }
233 | }
234 |
235 |
236 |
237 |
238 |
239 |
240 | DELETE /demo/:demoId
241 |
242 | Request body shape
243 |
244 |
245 | No request body
246 |
247 |
248 | Successful response shape
249 |
250 |
251 | {
252 | "message": "string",
253 | "request": {
254 | "type": "string",
255 | "description": "string",
256 | "url": "string",
257 | "body": {
258 | "name": "string",
259 | "age": "string"
260 | }
261 | }
262 | }
263 |
264 |
265 |
266 | ## Documentation
267 | See the links to the official documentation of the node-mongo project and community building it below:
268 | - [Node Mongo documentation](https://code-collabo.gitbook.io/node-mongo-v2.0.0)
269 | - [Code Collabo documentation](https://code-collabo.gitbook.io/community-doc-v1.0.0)
270 |
--------------------------------------------------------------------------------
/templates/ts/node-mongo-helpers.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process';
2 | import package_json from './package.json';
3 | import chalk from 'chalk';
4 |
5 | /* eslint-disable no-console */
6 |
7 | // console
8 | export const success = (message: string) => {
9 | console.log( chalk.greenBright(message) );
10 | }
11 |
12 | export const warning = (message: string) => {
13 | console.log( chalk.yellowBright(message) );
14 | }
15 |
16 | export const error = (message: string) => {
17 | console.log( chalk.redBright(message) );
18 | }
19 |
20 | // DB connect
21 | export const npmRunPackageJsonScript = ({ script, currentWorkingDir } : { script: string, currentWorkingDir: string }): void => {
22 | const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
23 | spawn(npm, ['run', script], { cwd: currentWorkingDir, stdio: 'inherit' });
24 | }
25 |
26 | export const server = (serverPort: number | string): void => {
27 | try {
28 | success(`\nnode-mongo (Typescript) API boilerplate template v${package_json.version}`);
29 | success(`\nServer running at ${serverPort}`);
30 | } catch (err) {
31 | error(`${{ err }}`);
32 | }
33 | }
34 |
35 | const eslintAndServer = (serverPort: number | string) => {
36 | npmRunPackageJsonScript({ script: 'lint:watch', currentWorkingDir: './' });
37 | server(serverPort);
38 | }
39 |
40 | export const afterAtlasDBconnectSuccessful = (serverPort: number | string) => {
41 | success('\nConnected to mongoDB ATLAS');
42 | eslintAndServer(serverPort);
43 | }
44 |
45 | export const afterLocalDBconnectSuccessful = (serverPort: number | string) => {
46 | success('\nConnected to LOCAL mongoDB');
47 | eslintAndServer(serverPort);
48 | }
49 |
50 | export const connectToDBunsuccessful = (err: { message: unknown; }) => {
51 | error(`\nError in DB connection: ${err.message}\n`);
52 | warning('Refer to the node-mongo documentation: https://code-collabo.gitbook.io/node-mongo-v2.0.0\n\nGet further help from Code Collabo Community\'s Node mongo channel: https://matrix.to/#/#collabo-node-mongo:gitter.im\n');
53 | }
54 |
--------------------------------------------------------------------------------
/templates/ts/node-mongo.js:
--------------------------------------------------------------------------------
1 | import { chooseNodeMongoApiDBServer } from '@code-collabo/node-mongo-scripts/index';
2 |
3 | chooseNodeMongoApiDBServer();
4 |
5 |
--------------------------------------------------------------------------------
/templates/ts/node-mongo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pathToCheck": "./src",
3 | "templateName": "ts"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-mongo-api-boilerplate-template-ts",
3 | "version": "1.0.0",
4 | "description": "@code-collabo node-mongo API boilerplate template - typescript",
5 | "main": "src/server.[atlas/local].ts",
6 | "scripts": {
7 | "dev": "node -r esm node-mongo.js",
8 | "dev:local": "nodemon --exec ts-node src/server.local.ts",
9 | "dev:atlas": "nodemon --exec ts-node src/server.atlas.ts",
10 | "lint": "esw src --ext .ts --color",
11 | "lint:watch": "npm run lint -- --watch",
12 | "dev:restore": "node -r esm node-mongo.js",
13 | "dev:change": "node -r esm node-mongo.js"
14 | },
15 | "author": "Author Name",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@code-collabo/node-mongo-scripts": "^1.2.0",
19 | "cors": "^2.8.5",
20 | "dotenv": "^16.0.3",
21 | "esm": "^3.2.25",
22 | "express": "^4.18.2",
23 | "mongoose": "^7.0.5",
24 | "morgan": "^1.10.0"
25 | },
26 | "devDependencies": {
27 | "@types/body-parser": "^1.19.2",
28 | "@types/cors": "^2.8.13",
29 | "@types/express": "^4.17.17",
30 | "@types/morgan": "^1.9.4",
31 | "@types/node": "^18.16.1",
32 | "@typescript-eslint/eslint-plugin": "^5.59.1",
33 | "@typescript-eslint/parser": "^5.59.1",
34 | "chalk": "^4.1.0",
35 | "eslint": "^8.39.0",
36 | "eslint-watch": "^8.0.0",
37 | "nodemon": "^2.0.22",
38 | "ts-node": "^10.9.1",
39 | "typescript": "^5.0.4"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/templates/ts/src/api/controllers/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { success } from '../../../node-mongo-helpers';
3 |
4 | export const getAppController = async (req: Request, res: Response) => {
5 | const message = 'App works!';
6 | success(message);
7 | return res.status(200).json({ message });
8 | }
9 |
--------------------------------------------------------------------------------
/templates/ts/src/api/controllers/demo.controller.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import {
3 | getDemoItemsService,
4 | createDemoItemService,
5 | getOneDemoItemService,
6 | deleteDemoItemService,
7 | updateOneDemoItemPropertyValueService,
8 | updateDemoItemPropertyValuesService,
9 | } from '../services/demo.service';
10 | import { success, error } from '../../../node-mongo-helpers';
11 |
12 | const routeName = 'demo';
13 | const item = `${routeName}-item`;
14 |
15 | let response: { [key: string]: unknown } = {};
16 |
17 | export const getDemoItemsController = async (req: Request, res: Response) => {
18 | try {
19 | const docs = await getDemoItemsService();
20 | response = {
21 | count: docs.length,
22 | items: docs.map(doc => {
23 | return {
24 | _id: doc._id,
25 | name: doc.name,
26 | age: doc.age,
27 | request: {
28 | type: 'GET',
29 | url: `http://localhost:3000/${routeName}/${doc._id}`
30 | }
31 | }
32 | })
33 | };
34 | success(`GET request successful!`);
35 | return res.status(200).json(response);
36 | } catch (err) {
37 | error(`Error retriving ${item}s: ${err}`);
38 | res.status(500).json({
39 | error: `${err}`
40 | });
41 | }
42 | }
43 |
44 | export const createDemoItemController = async (req: Request, res: Response) => {
45 | try {
46 | const doc = await createDemoItemService(req.body);
47 | response = {
48 | message: `${item} created successfully!`,
49 | newItem: {
50 | _id: doc._id,
51 | name: doc.name,
52 | age: doc.age,
53 | request: {
54 | type: 'GET',
55 | url: `http://localhost:3000/${routeName}/${doc._id}`,
56 | },
57 | },
58 | }
59 | success(`${item} CREATED successfully!`);
60 | return res.status(201).json(response);
61 | } catch (err) {
62 | error(`Error saving ${item}: ${err}`);
63 | res.status(500).json({
64 | error: `${err}`,
65 | });
66 | }
67 | }
68 |
69 | export const getOneDemoItemController = async (req: Request, res: Response) => {
70 | try {
71 | const doc = await getOneDemoItemService(req.params.demoId);
72 | if (doc) {
73 | response = {
74 | _id: doc._id,
75 | name: doc.name,
76 | age: doc.age,
77 | request: {
78 | type: 'GET',
79 | description: `Url link to all ${item}s`,
80 | url: `http://localhost:3000/${routeName}/`,
81 | },
82 | }
83 | success(`GET request successful!`);
84 | return res.status(200).json(response);
85 | } else {
86 | error('No record found for provided ID');
87 | return res.status(404).json({
88 | message: 'No record found for provided ID',
89 | });
90 | }
91 | } catch (err) {
92 | error(`Error retriving ${item}: ${err}`);
93 | res.status(500).json({
94 | message: 'Invalid ID',
95 | error: `${err}`,
96 | });
97 | }
98 | }
99 |
100 | export const deleteDemoItemController = async (req: Request, res: Response) => {
101 | try {
102 | await deleteDemoItemService(req.params.demoId);
103 | response = {
104 | message: `${item} deleted successfully!`,
105 | request: {
106 | type: 'POST',
107 | description: 'Url link to make post request to',
108 | url: `http://localhost:3000/${item}/`,
109 | body: {
110 | name: 'String',
111 | age: 'Number',
112 | },
113 | },
114 | }
115 | success(`${item} DELETED successfully!`);
116 | return res.status(200).json(response);
117 | } catch (err) {
118 | error(`Error deleting ${item}: ${err}`);
119 | res.status(500).json({
120 | message: `Error deleting ${item}`,
121 | error: `${err}`,
122 | });
123 | }
124 | };
125 |
126 | export const updateOneDemoItemPropertyValueController = async (req: Request, res: Response) => {
127 | try {
128 | const id: string = req.params.demoId;
129 | await updateOneDemoItemPropertyValueService(id, req.body);
130 | response = {
131 | message: 'Patch request successful!',
132 | request: {
133 | type: 'GET',
134 | description: `Url link to updated ${item}`,
135 | url: `http://localhost:3000/${routeName}/${id}`,
136 | },
137 | };
138 | success(`PATCH request for ID ${id} successful!`);
139 | return res.status(200).json(response);
140 | } catch (err) {
141 | error(`Error updating ${item} property & value: ${err}`);
142 | res.status(500).json({
143 | message: `Error updating ${item} property & value`,
144 | error: `${err}`,
145 | });
146 | }
147 | };
148 |
149 | export const updateDemoItemPropertyValuesController = async (req: Request, res: Response) => {
150 | try {
151 | const id: string = req.params.id;
152 | await updateDemoItemPropertyValuesService(id, req.body);
153 | response = {
154 | message: `Put request successful!`,
155 | request: {
156 | type: 'GET',
157 | description: `Url link to updated ${item}`,
158 | url: `http://localhost:3000/${routeName}/${id}`,
159 | },
160 | };
161 | success(`PUT request for ID ${id} successful!`);
162 | return res.status(200).json(response);
163 | } catch (err) {
164 | error(`Error updating ${item}: ${err}`);
165 | res.status(500).json({
166 | message: `Error updating ${item}`,
167 | error: `${err}`,
168 | });
169 | }
170 | };
171 |
--------------------------------------------------------------------------------
/templates/ts/src/api/models/demo.model.ts:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 |
3 | export interface DemoDocument extends mongoose.Document {
4 | name: string;
5 | age: number;
6 | }
7 |
8 | const collectionName = 'demo';
9 |
10 | const DemoSchema = new mongoose.Schema({
11 | name: { type: String, required: true },
12 | age: { type: Number, required: true }
13 | });
14 |
15 | const DemoModel = mongoose.model(collectionName, DemoSchema, collectionName); //declare collection name a second time to prevent mongoose from pluralizing or adding 's' to the collection name
16 |
17 | export { DemoModel };
18 |
--------------------------------------------------------------------------------
/templates/ts/src/api/routes/app.route.ts:
--------------------------------------------------------------------------------
1 | import express, { IRouter } from 'express';
2 | import { getAppController } from '../controllers/app.controller';
3 |
4 | const router: IRouter = express.Router();
5 |
6 | router.get('/', getAppController);
7 |
8 | export { router };
9 |
--------------------------------------------------------------------------------
/templates/ts/src/api/routes/demo.route.ts:
--------------------------------------------------------------------------------
1 | import express, { IRouter } from 'express';
2 | import {
3 | getDemoItemsController,
4 | createDemoItemController,
5 | getOneDemoItemController,
6 | deleteDemoItemController,
7 | updateOneDemoItemPropertyValueController,
8 | updateDemoItemPropertyValuesController,
9 | } from '../controllers/demo.controller';
10 |
11 | const router: IRouter = express.Router();
12 |
13 | router.get('/', getDemoItemsController);
14 | router.post('/', createDemoItemController);
15 | router.get('/:demoId', getOneDemoItemController);
16 | router.delete('/:demoId', deleteDemoItemController);
17 | router.patch('/:demoId', updateOneDemoItemPropertyValueController);
18 | router.put('/:id', updateDemoItemPropertyValuesController);
19 |
20 | export { router };
21 |
--------------------------------------------------------------------------------
/templates/ts/src/api/services/demo.service.ts:
--------------------------------------------------------------------------------
1 | import { DemoDocument, DemoModel as Demo } from '../models/demo.model';
2 |
3 | export const getDemoItemsService = async () => {
4 | const query = await Demo.find().select('_id name age').exec();
5 | return query;
6 | }
7 |
8 | export const createDemoItemService = async (requestBody: DemoDocument): Promise => {
9 | const demo = new Demo({
10 | name: requestBody.name,
11 | age: requestBody.age
12 | });
13 | const save = await demo.save();
14 | return save;
15 | }
16 |
17 | export const getOneDemoItemService = async (paramsId: string) => {
18 | const query = Demo.findById(paramsId).select('_id name age').exec();
19 | return query;
20 | }
21 |
22 | export const deleteDemoItemService = async (paramsId: string) => {
23 | const query = await Demo.deleteOne({ _id: paramsId }).exec();
24 | return query;
25 | }
26 |
27 | export const updateOneDemoItemPropertyValueService = async (paramsId: string, requestBody: { propName: string, value: string }[]) => {
28 | const updateOps: Record = {};
29 | for (const ops of requestBody) {
30 | updateOps[ops.propName] = ops.value;
31 | }
32 | const query = await Demo.updateOne({ _id: paramsId }, { $set: updateOps }).exec();
33 | return query;
34 | };
35 |
36 | export const updateDemoItemPropertyValuesService = async (paramsId: string, requestBody: DemoDocument) => {
37 | const resetItem = {
38 | name: requestBody.name,
39 | age: requestBody.age,
40 | };
41 | const query = await Demo.findByIdAndUpdate(paramsId, { $set: resetItem }, { new: true }).exec();
42 | return query;
43 | };
44 |
--------------------------------------------------------------------------------
/templates/ts/src/app.ts:
--------------------------------------------------------------------------------
1 | import express, { Express, NextFunction, Request, Response } from 'express';
2 | import dotenv from 'dotenv';
3 | import morgan from 'morgan';
4 | import cors from 'cors';
5 | import { router as appRouter } from './api/routes/app.route';
6 | import { router as demoRouter } from './api/routes/demo.route';
7 |
8 | dotenv.config();
9 |
10 | const app: Express = express();
11 |
12 | app.use(morgan('dev'));
13 | app.use(express.urlencoded({
14 | extended: false
15 | }));
16 | app.use(express.json());
17 | app.use(cors({ origin: [`http://localhost:${process.env.CLIENT_APP_PORT}`, `${process.env.CLIENT_APP_URL}`] }));
18 |
19 | //====== Use Routers =======
20 | app.use('/', appRouter);
21 | app.use('/demo', demoRouter);
22 | //==========================
23 |
24 | interface Error {
25 | status?: number;
26 | message: string;
27 | }
28 |
29 | app.use((req: Request, res: Response, next: NextFunction) => {
30 | const err: Error = new Error('Route not found!');
31 | err.status = 404;
32 | next(err);
33 | });
34 |
35 | app.use((err: Error, req: Request, res: Response) => {
36 | res.status(err.status || 500);
37 | res.json({
38 | error: {
39 | message: err.message
40 | }
41 | });
42 | });
43 |
44 | export { app };
45 |
--------------------------------------------------------------------------------
/templates/ts/src/db.atlas.connect.ts:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 | import dotenv from 'dotenv';
3 | import { afterAtlasDBconnectSuccessful, connectToDBunsuccessful } from '../node-mongo-helpers';
4 |
5 | dotenv.config();
6 |
7 | const mongooseAtlasConnect = async (port: number | string) => {
8 | try {
9 | await mongoose.connect(`${process.env.MONGODB_ATLAS_URI}`);
10 | afterAtlasDBconnectSuccessful(port);
11 | } catch (err) {
12 | connectToDBunsuccessful(err);
13 | }
14 | }
15 |
16 | export default mongooseAtlasConnect;
17 |
--------------------------------------------------------------------------------
/templates/ts/src/server.atlas.ts:
--------------------------------------------------------------------------------
1 | import mongooseAtlasConnect from './db.atlas.connect';
2 | import { app as app } from './app';
3 |
4 | const port = process.env.PORT_ATLAS || 3000;
5 |
6 | app.listen(port, () => {
7 | mongooseAtlasConnect(port);
8 | });
9 |
--------------------------------------------------------------------------------
/templates/ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Enable incremental compilation */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
20 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | /* Modules */
24 | "module": "commonjs", /* Specify what module code is generated. */
25 | // "rootDir": "src", /* Specify the root folder within your source files. */
26 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
27 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
28 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
29 | "rootDirs": ["./", "src"], /* Allow multiple folders to be treated as one when resolving modules. */
30 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
31 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
32 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
33 | "resolveJsonModule": true, /* Enable importing .json files */
34 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */
35 | /* JavaScript Support */
36 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
37 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
38 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
39 | /* Emit */
40 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
41 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
42 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
43 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
44 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
45 | "outDir": "build", /* Specify an output folder for all emitted files. */
46 | // "removeComments": true, /* Disable emitting comments. */
47 | // "noEmit": true, /* Disable emitting files from a compilation. */
48 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
49 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
50 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
51 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
52 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
53 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
54 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
55 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
56 | // "newLine": "crlf", /* Set the newline character for emitting files. */
57 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
58 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
59 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
60 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
61 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
62 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
63 | /* Interop Constraints */
64 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
65 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
66 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
67 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
68 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
69 | /* Type Checking */
70 | "strict": true, /* Enable all strict type-checking options. */
71 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
72 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
73 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
74 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
75 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
76 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
77 | "useUnknownInCatchVariables": false, /* Type catch clause variables as 'unknown' instead of 'any'. */
78 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
79 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
80 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
81 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
82 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
83 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
84 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
85 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
86 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
87 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
88 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
89 | /* Completeness */
90 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
91 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
92 | },
93 | "include": [
94 | "src/**/*.ts"
95 | ],
96 | "exclude": [
97 | "node_modules/**/*",
98 | "dist/**/*"
99 | ]
100 | }
101 |
--------------------------------------------------------------------------------