Skip to content

Commit 391f4b3

Browse files
committed
feat(generators): create new app :with-postgres-typeorm
1 parent 26e8d0d commit 391f4b3

File tree

8 files changed

+279
-2
lines changed

8 files changed

+279
-2
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ $ yo create-service-component:with-mongo
6363

6464
# or :with-postgres
6565
$ yo create-service-component:with-postgres
66+
67+
# or :with-postgres-typeorm
68+
$ yo create-service-component:with-postgres-typeorm
6669
```
6770

6871
This scaffolds out:
@@ -74,8 +77,8 @@ This scaffolds out:
7477
│ │   │   ├── constants.ts
7578
│ │   │   ├── controller.ts
7679
│ │   │   ├── index.ts
77-
│ │   │   ├── model.ts (:with-mongo/:with-postgres)
78-
│ │   │   ├── repository.ts (:with-mongo/:with-postgres)
80+
│ │   │   ├── model.ts (:with-mongo, :with-postgres or :with-postgres-typeorm)
81+
│ │   │   ├── repository.ts (:with-mongo, :with-postgres or :with-postgres-typeorm)
7982
│ │   │   ├── types.ts
8083
│ │   └── ...
8184
│ ├── ...
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const Generator = require('yeoman-generator');
2+
const chalk = require('chalk');
3+
const yosay = require('yosay');
4+
const changeCase = require('change-case');
5+
const pluralize = require('pluralize');
6+
7+
const pkg = require('../../package.json');
8+
9+
module.exports = class extends Generator {
10+
async prompting() {
11+
this.log(yosay(`Welcome to the ${chalk.red(pkg.name)} generator!`));
12+
13+
const prompts = [
14+
{
15+
type: 'input',
16+
name: 'compName',
17+
message: 'Name of the new component (singular)?',
18+
default: 'thing',
19+
},
20+
];
21+
22+
return this.prompt(prompts).then((props) => {
23+
const compNameParamCase = changeCase.paramCase(props.compName);
24+
const compNamePascalCase = changeCase.pascalCase(props.compName);
25+
26+
this.props = {
27+
...props,
28+
compNameParamCase,
29+
compNamePascalCase,
30+
compNameParamCasePlural: pluralize(compNameParamCase),
31+
};
32+
});
33+
}
34+
35+
writing() {
36+
this.fs.copyTpl(
37+
[this.templatePath('**/*'), this.templatePath('**/.*')],
38+
this.destinationPath('src/components'),
39+
this.props,
40+
);
41+
}
42+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const ENTITY = '<%= compNameParamCase %>';
2+
const RESOURCE = '<%= compNameParamCasePlural %>';
3+
4+
export { ENTITY, RESOURCE };
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { Request, Response, NextFunction } from 'express';
2+
import { StatusCodes } from 'http-status-codes';
3+
import { HttpError } from '@boringcodes/utils/error';
4+
5+
import { <%= compNamePascalCase %> } from './types';
6+
import { ENTITY } from './constants';
7+
import repository from './repository';
8+
9+
interface MyRequest extends Request {
10+
readonly [ENTITY]: Required<<%= compNamePascalCase %>>;
11+
}
12+
13+
const getById = async (
14+
req: Request,
15+
_: Response,
16+
next: NextFunction,
17+
): Promise<void> => {
18+
try {
19+
const { id = '' } = req.params;
20+
if (id === '') {
21+
throw new HttpError(StatusCodes.BAD_REQUEST, 'Invalid resource Id');
22+
}
23+
24+
try {
25+
// get object by id
26+
const object = await repository.get(+id);
27+
28+
// assign object to request for using in the next handlers
29+
Object.assign(req, { [ENTITY]: object });
30+
31+
next();
32+
} catch (err) {
33+
throw new HttpError(StatusCodes.NOT_FOUND, 'Resource not found');
34+
}
35+
} catch (err) {
36+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
37+
}
38+
};
39+
40+
const list = async (
41+
_: Request,
42+
res: Response,
43+
next: NextFunction,
44+
): Promise<void> => {
45+
try {
46+
// list objects
47+
const objects = await repository.list();
48+
49+
res.send(objects);
50+
} catch (err) {
51+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
52+
}
53+
};
54+
55+
const create = async (
56+
req: Request,
57+
res: Response,
58+
next: NextFunction,
59+
): Promise<void> => {
60+
try {
61+
// create object
62+
const object = await repository.create(req.body);
63+
64+
res.send(object);
65+
} catch (err) {
66+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
67+
}
68+
};
69+
70+
const get = (req: Request, res: Response, next: NextFunction): void => {
71+
try {
72+
// get object
73+
res.send((req as MyRequest)[ENTITY]);
74+
} catch (err) {
75+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
76+
}
77+
};
78+
79+
const update = async (
80+
req: Request,
81+
res: Response,
82+
next: NextFunction,
83+
): Promise<void> => {
84+
try {
85+
// update object
86+
const object = await repository.update(
87+
(req as MyRequest)[ENTITY].id,
88+
req.body,
89+
);
90+
91+
res.send(object);
92+
} catch (err) {
93+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
94+
}
95+
};
96+
97+
const del = async (
98+
req: Request,
99+
res: Response,
100+
next: NextFunction,
101+
): Promise<void> => {
102+
try {
103+
// delete object
104+
const object = await repository.del((req as MyRequest)[ENTITY].id);
105+
106+
res.send(object);
107+
} catch (err) {
108+
next(new HttpError(err.code ?? StatusCodes.INTERNAL_SERVER_ERROR, err));
109+
}
110+
};
111+
112+
export default { getById, list, create, get, update, del };
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Router } from 'express';
2+
3+
import { RouteOptions } from '../types';
4+
import { RESOURCE } from './constants';
5+
import controller from './controller';
6+
7+
const path = `/${RESOURCE}`;
8+
9+
const routes = (_: RouteOptions): Router => {
10+
const router = Router();
11+
12+
router.param('id', controller.getById);
13+
14+
router.route('/').get(controller.list).post(controller.create);
15+
16+
router
17+
.route('/:id')
18+
.get(controller.get)
19+
.patch(controller.update)
20+
.delete(controller.del);
21+
22+
return router;
23+
};
24+
25+
export default { path, routes };
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
2+
3+
import { <%= compNamePascalCase %> } from './types';
4+
import { ENTITY } from './constants';
5+
6+
@Entity(ENTITY)
7+
class Model implements <%= compNamePascalCase %> {
8+
@PrimaryGeneratedColumn()
9+
id!: number;
10+
11+
@Column()
12+
name!: string;
13+
14+
// TODO: add more fields
15+
}
16+
17+
export default Model;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { getRepository } from 'typeorm';
2+
import { MyError } from '@boringcodes/utils/error';
3+
4+
import { <%= compNamePascalCase %> } from './types';
5+
import Model from './model';
6+
7+
const list = async (): Promise<<%= compNamePascalCase %>[]> => {
8+
// list documents
9+
const documents = await getRepository(Model).find();
10+
11+
return documents.map(transform);
12+
};
13+
14+
const create = async (object: Omit<<%= compNamePascalCase %>, 'id'>): Promise<<%= compNamePascalCase %>> => {
15+
// create document
16+
const document = getRepository(Model).create(object);
17+
await getRepository(Model).save(document);
18+
19+
return transform(document);
20+
};
21+
22+
const get = async (id: number): Promise<<%= compNamePascalCase %>> => {
23+
// get document
24+
const document = await getRepository(Model).findOne(id);
25+
if (document === undefined) {
26+
throw new MyError('Row not found');
27+
}
28+
29+
return transform(document);
30+
};
31+
32+
const update = async (
33+
id: number,
34+
object: Omit<<%= compNamePascalCase %>, 'id'>,
35+
): Promise<<%= compNamePascalCase %>> => {
36+
// get document
37+
const document = await getRepository(Model).findOne(id);
38+
if (document === undefined) {
39+
throw new MyError('Row not found');
40+
}
41+
42+
// update document
43+
getRepository(Model).merge(document, object);
44+
await getRepository(Model).save(document);
45+
46+
return transform(document);
47+
};
48+
49+
const del = async (id: number): Promise<<%= compNamePascalCase %>> => {
50+
// get document
51+
const document = await getRepository(Model).findOne(id);
52+
if (document === undefined) {
53+
throw new MyError('Row not found');
54+
}
55+
56+
// delete document
57+
await getRepository(Model).remove(document);
58+
59+
return transform(document);
60+
};
61+
62+
// transform document to <%= compNamePascalCase %>
63+
const transform = (document: Model): <%= compNamePascalCase %> => {
64+
return document;
65+
};
66+
67+
export default { list, create, get, update, del };
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
interface <%= compNamePascalCase %> {
2+
readonly id: number;
3+
readonly name: string;
4+
// TODO: add more props here
5+
}
6+
7+
export { <%= compNamePascalCase %> };

0 commit comments

Comments
 (0)