├── .gitignore ├── README.md ├── nest-cli.json ├── nodemon-debug.json ├── nodemon.json ├── package.json ├── serverless.yml ├── src ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── config │ └── database.ts ├── entity │ ├── author.entity.ts │ ├── book.entity.ts │ └── index.ts ├── lambda.ts └── modules │ ├── author │ ├── author.controller.ts │ ├── author.module.ts │ └── author.service.ts │ └── book │ ├── book.controller.ts │ ├── book.module.ts │ └── book.service.ts ├── test ├── app.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .build 4 | dist 5 | yarn.lock 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-nestJS-typeORM-crud 2 | 3 | ##### This is example how to nestjs using the serverless framework 4 | - TypeORM 5 | - MySql 6 | - CRUD 7 | 8 | ## setup mysql connection in serverless.yml 9 | ``` 10 | # Custom Variables 11 | custom: 12 | ... 13 | mysqlHost: 14 | local: localhost 15 | mysqlUser: 16 | local: user 17 | mysqlPassword: 18 | local: password 19 | mysqlDatabase: 20 | local: dbname 21 | mysqlPort: 22 | local: '3306' 23 | ``` 24 | ## How to prepare 25 | ``` 26 | $ npm install serverless -g 27 | $ git clone https://github.com/kop7/serverless-nestjs-typeorm.git 【projectName】 28 | $ cd 【projectName】 29 | $ npm install 30 | ``` 31 | 32 | ## Development 33 | 34 | ``` 35 | $ npm run sls:offline 36 | Serverless: Typescript compiled. 37 | Serverless: Watching typescript files... 38 | Serverless: Starting Offline: undefined/undefined. 39 | 40 | Serverless: Routes for author: 41 | Serverless: ANY /api/author 42 | 43 | Serverless: Routes for book: 44 | Serverless: ANY /api/book 45 | 46 | Serverless: Offline listening on http://localhost:3000 47 | ``` 48 | 49 | The logs should be : 50 | 51 | ``` 52 | Serverless: ANY /api/book (λ: book) 53 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [NestFactory] Starting Nest application... 54 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] TypeOrmModule dependencies initialized +34ms 55 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] AppModule dependencies initialized +43ms 56 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] ConfigModule dependencies initialized +5ms 57 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] TypeOrmCoreModule dependencies initialized +168ms 58 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] TypeOrmModule dependencies initialized +1ms 59 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] TypeOrmModule dependencies initialized +0ms 60 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] BookModule dependencies initialized +3ms 61 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [InstanceLoader] AuthorModule dependencies initialized +0ms 62 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RoutesResolver] AppController {/}: +10ms 63 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RoutesResolver] BookController {/api/book}: +1ms 64 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/, GET} route +6ms 65 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, GET} route +3ms 66 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/, POST} route +2ms 67 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/bulk, POST} route +4ms 68 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, PATCH} route +4ms 69 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, PUT} route +2ms 70 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, DELETE} route +2ms 71 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RoutesResolver] AuthorController {/api/author}: +1ms 72 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/, GET} route +2ms 73 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, GET} route +2ms 74 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/, POST} route +3ms 75 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/bulk, POST} route +2ms 76 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, PATCH} route +2ms 77 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, PUT} route +2ms 78 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [RouterExplorer] Mapped {/:id, DELETE} route +2ms 79 | [Nest] 7980 - 09/02/2019, 6:33:47 PM [NestApplication] Nest application successfully started +6ms 80 | ``` 81 | 82 | 83 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/mkop) 84 | 85 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /nodemon-debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts" 6 | } 7 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "ts-node -r tsconfig-paths/register src/main.ts" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-nestjs-typeorm-crud", 3 | "version": "1.0.0", 4 | "description": "Example how to nestjs using the serverless framework with TypeORM", 5 | "keywords": [ 6 | "nestjs", 7 | "mysql", 8 | "crud", 9 | "typeorm", 10 | "serverless", 11 | "lambda", 12 | "aws", 13 | "typescript" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/kop7/serverless-nestjs-typeorm" 18 | }, 19 | "author": "Matija Kop", 20 | "license": "MIT", 21 | "scripts": { 22 | "build": "tsc -p tsconfig.build.json", 23 | "format": "prettier --write \"src/**/*.ts\"", 24 | "sls:offline": "sls offline start", 25 | "lint": "tslint -p tsconfig.json -c tslint.json", 26 | "test": "jest", 27 | "test:watch": "jest --watch", 28 | "test:cov": "jest --coverage", 29 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 30 | "test:e2e": "jest --config ./test/jest-e2e.json" 31 | }, 32 | "dependencies": { 33 | "@nestjs/common": "^6.8.0", 34 | "@nestjs/core": "^6.8.0", 35 | "@nestjs/platform-express": "^6.8.0", 36 | "@nestjs/typeorm": "^7.0.0", 37 | "@nestjsx/crud": "^4.4.0", 38 | "@nestjsx/crud-request": "^4.4.0", 39 | "@nestjsx/crud-typeorm": "^4.4.0", 40 | "aws-serverless-express": "^3.3.5", 41 | "class-transformer": "^0.2.3", 42 | "class-validator": "^0.10.0", 43 | "mysql": "^2.17.1", 44 | "nestjs-config": "^1.4.4", 45 | "reflect-metadata": "^0.1.12", 46 | "rimraf": "^2.6.2", 47 | "rxjs": "^6.2.2", 48 | "typeorm": "^0.2.25" 49 | }, 50 | "devDependencies": { 51 | "@hewmen/serverless-plugin-typescript": "^1.1.17", 52 | "@types/aws-lambda": "^8.10.15", 53 | "@types/aws-serverless-express": "^3.3.1", 54 | "@types/express": "^4.16.0", 55 | "@types/node": "^10.7.1", 56 | "nodemon": "^1.18.3", 57 | "prettier": "^1.14.2", 58 | "serverless-offline": "^3.31.3", 59 | "serverless-plugin-optimize": "^4.0.2-rc.1", 60 | "ts-loader": "^4.4.2", 61 | "ts-node": "^7.0.1", 62 | "tsconfig-paths": "^3.5.0", 63 | "tslint": "5.11.0", 64 | "typescript": "^3.7.1" 65 | }, 66 | "jest": { 67 | "moduleFileExtensions": [ 68 | "js", 69 | "json", 70 | "ts" 71 | ], 72 | "rootDir": "src", 73 | "testRegex": ".spec.ts$", 74 | "transform": { 75 | "^.+\\.(t|j)s$": "ts-jest" 76 | }, 77 | "coverageDirectory": "../coverage", 78 | "testEnvironment": "node" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: 2 | name: serverless-nestjs-crud 3 | 4 | plugins: 5 | - '@hewmen/serverless-plugin-typescript' 6 | - serverless-offline 7 | 8 | provider: 9 | name: aws 10 | runtime: nodejs8.10 11 | stage: ${opt:stage,'local'} 12 | 13 | # Environment Variables 14 | environment: 15 | MYSQL_HOST: ${self:custom.mysqlHost.${self:provider.stage}} 16 | MYSQL_USER: ${self:custom.mysqlUser.${self:provider.stage}} 17 | MYSQL_PASSWORD: ${self:custom.mysqlPassword.${self:provider.stage}} 18 | MYSQL_DATABASE: ${self:custom.mysqlDatabase.${self:provider.stage}} 19 | MYSQL_PORT: ${self:custom.mysqlPort.${self:provider.stage}} 20 | 21 | # Custom Variables 22 | custom: 23 | stages: 24 | - local 25 | - dev 26 | mysqlHost: 27 | local: localhost 28 | mysqlUser: 29 | local: user 30 | mysqlPassword: 31 | local: password 32 | mysqlDatabase: 33 | local: dbname 34 | mysqlPort: 35 | local: '3306' 36 | package: 37 | individually: true 38 | 39 | functions: 40 | 41 | author: 42 | handler: src/lambda.handler 43 | events: 44 | - http: 45 | method: any 46 | path: /api/author 47 | cors: true 48 | 49 | book: 50 | handler: src/lambda.handler 51 | events: 52 | - http: 53 | method: any 54 | path: /api/book 55 | cors: true 56 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | @Controller() 3 | export class AppController {} 4 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import {TypeOrmModule} from '@nestjs/typeorm'; 5 | import { ConfigModule } from 'nestjs-config'; 6 | import { TypeOrmConfigService } from './config/database'; 7 | import * as path from 'path'; 8 | import { BookModule } from './modules/book/book.module'; 9 | import { AuthorModule } from './modules/author/author.module'; 10 | 11 | @Module({ 12 | imports: [ 13 | ConfigModule.load(path.resolve(__dirname, 'config', '**', '!(*.d).{ts,js}')), 14 | TypeOrmModule.forRootAsync( 15 | { 16 | inject: [ConfigModule], 17 | useClass: TypeOrmConfigService, 18 | }), 19 | BookModule, 20 | AuthorModule, 21 | ], 22 | controllers: [AppController], 23 | providers: [AppService], 24 | }) 25 | export class AppModule {} 26 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService {} 5 | -------------------------------------------------------------------------------- /src/config/database.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm'; 3 | import { ConnectionManager, getConnectionManager } from 'typeorm'; 4 | 5 | @Injectable() 6 | export class TypeOrmConfigService implements TypeOrmOptionsFactory { 7 | async createTypeOrmOptions(): Promise { 8 | const connectionManager: ConnectionManager = getConnectionManager(); 9 | let options: any; 10 | 11 | if (connectionManager.has('default')) { 12 | options = connectionManager.get('default').options; 13 | await connectionManager.get('default').close(); 14 | } else { 15 | options = { 16 | type: 'mysql', 17 | host: process.env.MYSQL_HOST, 18 | username: process.env.MYSQL_USER, 19 | password: process.env.MYSQL_PASSWORD, 20 | database: process.env.MYSQL_DATABASE, 21 | port: parseInt(process.env.MYSQL_PORT, 10), 22 | entities: [__dirname + '/../entity/**.entity{.ts,.js}'], 23 | synchronize: true, 24 | } as TypeOrmModuleOptions; 25 | } 26 | return options; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/entity/author.entity.ts: -------------------------------------------------------------------------------- 1 | import {Entity, Column, PrimaryGeneratedColumn} from 'typeorm'; 2 | 3 | @Entity('author') 4 | export class Author { 5 | @PrimaryGeneratedColumn() 6 | id: number; 7 | 8 | @Column() 9 | name: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/entity/book.entity.ts: -------------------------------------------------------------------------------- 1 | import {Entity, Column, PrimaryGeneratedColumn} from 'typeorm'; 2 | 3 | @Entity('book') 4 | export class Book { 5 | @PrimaryGeneratedColumn() 6 | id: number; 7 | 8 | @Column() 9 | name: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/entity/index.ts: -------------------------------------------------------------------------------- 1 | export * from './author.entity'; 2 | export * from './book.entity'; -------------------------------------------------------------------------------- /src/lambda.ts: -------------------------------------------------------------------------------- 1 | import { Handler, Context } from 'aws-lambda'; 2 | import { Server } from 'http'; 3 | import { NestFactory } from '@nestjs/core'; 4 | import { AppModule } from './app.module'; 5 | import {ExpressAdapter} from '@nestjs/platform-express'; 6 | import * as serverless from 'aws-serverless-express'; 7 | import {proxy} from 'aws-serverless-express'; 8 | 9 | let cachedServer: Server; 10 | 11 | process.on('unhandledRejection', (reason) => { 12 | // tslint:disable-next-line:no-console 13 | console.error(reason); 14 | }); 15 | 16 | process.on('uncaughtException', (reason) => { 17 | // tslint:disable-next-line:no-console 18 | console.error(reason); 19 | }); 20 | 21 | function bootstrapServer(): Promise { 22 | const expressApp = require('express')(); 23 | const adapter = new ExpressAdapter(expressApp); 24 | return NestFactory.create(AppModule, adapter, { logger: false }) 25 | .then(app => app.init()) 26 | .then(() => serverless.createServer(expressApp)); 27 | } 28 | 29 | export const handler: Handler = async (event: any, context: Context) => { 30 | cachedServer = await bootstrapServer(); 31 | return proxy(cachedServer, event, context, 'PROMISE').promise; 32 | }; 33 | -------------------------------------------------------------------------------- /src/modules/author/author.controller.ts: -------------------------------------------------------------------------------- 1 | import {Controller } from '@nestjs/common'; 2 | import {AuthorService} from './author.service'; 3 | import {Crud, CrudController} from '@nestjsx/crud'; 4 | import { Author } from '../../entity'; 5 | 6 | @Crud({ model: {type: Author }}) 7 | @Controller('api/author') 8 | export class AuthorController implements CrudController { 9 | constructor(public readonly service: AuthorService) {} 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/author/author.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AuthorService } from './author.service'; 3 | import { AuthorController } from './author.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { Author } from '../../entity'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Author])], 9 | providers: [AuthorService], 10 | controllers: [AuthorController], 11 | exports: [AuthorService], 12 | }) 13 | export class AuthorModule {} 14 | -------------------------------------------------------------------------------- /src/modules/author/author.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import {InjectRepository} from '@nestjs/typeorm'; 3 | import {Author} from '../../entity'; 4 | import {TypeOrmCrudService} from '@nestjsx/crud-typeorm'; 5 | 6 | @Injectable() 7 | export class AuthorService extends TypeOrmCrudService { 8 | constructor(@InjectRepository(Author) repo) { 9 | super (repo); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/book/book.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import {Crud, CrudController} from '@nestjsx/crud'; 3 | import { Book} from '../../entity'; 4 | import { BookService} from './book.service'; 5 | 6 | @Crud({model: { 7 | type: Book, 8 | }}) 9 | @Controller('api/book') 10 | export class BookController implements CrudController { 11 | constructor(public readonly service: BookService) {} 12 | } 13 | -------------------------------------------------------------------------------- /src/modules/book/book.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import {TypeOrmModule} from '@nestjs/typeorm'; 4 | import { Book} from '../../entity'; 5 | import { BookController } from './book.controller'; 6 | import { BookService } from './book.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Book])], 10 | controllers: [BookController], 11 | providers: [BookService], 12 | exports: [BookService], 13 | }) 14 | export class BookModule {} 15 | -------------------------------------------------------------------------------- /src/modules/book/book.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import {TypeOrmCrudService} from '@nestjsx/crud-typeorm'; 3 | import {InjectRepository} from '@nestjs/typeorm'; 4 | import { Book } from '../../entity'; 5 | 6 | @Injectable() 7 | export class BookService extends TypeOrmCrudService { 8 | constructor(@InjectRepository(Book) repo) { 9 | super (repo); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication } from '@nestjs/common'; 2 | import { Test } from '@nestjs/testing'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeAll(async () => { 10 | const moduleFixture = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/hello (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/hello') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*"], 4 | "exclude": ["node_modules", "**/*.spec.ts"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "lib": [ "es2015", "dom" ], 8 | "noLib": false, 9 | "allowSyntheticDefaultImports": true, 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es6", 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "baseUrl": "./", 16 | "skipLibCheck": true 17 | }, 18 | "exclude": ["node_modules"], 19 | "paths": { 20 | "@db-config/*": [ 21 | "src/config/*" 22 | ], 23 | "@modules/*":[ 24 | "src/modules/*" 25 | ], 26 | "@entities/*": [ 27 | "src/entity/*" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["jest", "node"] 5 | }, 6 | "include": ["**/*.spec.ts", "**/*.d.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": { 7 | "no-unused-expression": true 8 | }, 9 | "rules": { 10 | "eofline": false, 11 | "quotemark": [ 12 | true, 13 | "single" 14 | ], 15 | "no-shadowed-variable": false, 16 | "indent": false, 17 | "member-access": [ 18 | false 19 | ], 20 | "ordered-imports": [ 21 | false 22 | ], 23 | "max-line-length": [ 24 | true, 25 | 150 26 | ], 27 | "member-ordering": [ 28 | false 29 | ], 30 | "curly": false, 31 | "interface-name": [ 32 | false 33 | ], 34 | "array-type": [ 35 | false 36 | ], 37 | "no-empty-interface": false, 38 | "no-empty": false, 39 | "arrow-parens": false, 40 | "object-literal-sort-keys": false, 41 | "no-unused-expression": false, 42 | "max-classes-per-file": [ 43 | false 44 | ], 45 | "variable-name": [ 46 | false 47 | ], 48 | "one-line": [ 49 | false 50 | ], 51 | "one-variable-per-declaration": [ 52 | false 53 | ] 54 | }, 55 | "rulesDirectory": [] 56 | } 57 | --------------------------------------------------------------------------------