diff --git a/documentation/.vscode/settings.json b/documentation/.vscode/settings.json deleted file mode 100644 index 965de21f..00000000 --- a/documentation/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "spellchecker.ignoreFileExtensions": [".md"] -} diff --git a/documentation/Home.asciidoc b/documentation/Home.asciidoc deleted file mode 100644 index 02ae6687..00000000 --- a/documentation/Home.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= devon4node Wiki - -- link:devon4node-introduction.asciidoc[Introduction] -- link:devon4node-architecture.asciidoc[Architecture overview] - -== Layers - -- link:layer-controller.asciidoc[Controller] -- link:layer-service.asciidoc[Service] -- link:layer-dataaccess.asciidoc[Data Access] - -== Guides - -- link:guides-key-principles.asciidoc[Key Principles] -- link:guides-code-generation.asciidoc[Code Generation] -- link:guides-coding-conventions.asciidoc[Coding Conventions] -- link:guides-dependency-injection.asciidoc[Dependency Injection] -- link:guides-configuration-module.asciidoc[Configuration Module] -- link:guides-auth-jwt.asciidoc[Auth JWT Module] -- link:guides-swagger.asciidoc[Swagger] -- link:guides-typeorm.asciidoc[TypeORM] -- link:guides-serializer.asciidoc[Serializer] -- link:guides-validation.asciidoc[Validation] -- link:guides-logger.asciidoc[Logger] -- link:guides-mailer.asciidoc[Mailer Module] -- link:guides-eslint-sonarqube-config[ESLint SonarQube Configuration] -- link:guides-graphql[GraphQL] - -== devon4node applications - -- link:samples.asciidoc[devon4node Samples] -- link:samples-step-by-step.asciidoc[devon4node Sample Step by Step] - diff --git a/documentation/_Footer.md b/documentation/_Footer.md deleted file mode 100644 index 53c0700a..00000000 --- a/documentation/_Footer.md +++ /dev/null @@ -1 +0,0 @@ -Creative Commons License Agreement
This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). diff --git a/documentation/_Sidebar.asciidoc b/documentation/_Sidebar.asciidoc deleted file mode 100644 index 915b35e3..00000000 --- a/documentation/_Sidebar.asciidoc +++ /dev/null @@ -1,26 +0,0 @@ -* link:Home.asciidoc[devon4node Wiki] -** link:devon4node-introduction.asciidoc[Introduction] -** link:devon4node-architecture.asciidoc[Architecture overview] -** Layers -*** link:layer-controller.asciidoc[Controller] -*** link:layer-service.asciidoc[Service] -*** link:layer-dataaccess.asciidoc[Data Access] -** Guides -*** link:guides-key-principles.asciidoc[Key Principles] -*** link:guides-cli.asciidoc[devon4node CLI] -*** link:guides-code-generation.asciidoc[Code Generation] -*** link:guides-coding-conventions.asciidoc[Coding Conventions] -*** link:guides-dependency-injection.asciidoc[Dependency Injection] -*** link:guides-configuration-module.asciidoc[Configuration Module] -*** link:guides-auth-jwt.asciidoc[Auth JWT Module] -*** link:guides-swagger.asciidoc[Swagger] -*** link:guides-typeorm.asciidoc[TypeORM] -*** link:guides-serializer.asciidoc[Serializer] -*** link:guides-validation.asciidoc[Validation] -*** link:guides-logger.asciidoc[Logger] -*** link:guides-mailer.asciidoc[Mailer Module] -*** link:guides-eslint-sonarqube-config[ESLint SonarQube Configuration] -** devon4node applications -*** link:samples.asciidoc[devon4node Samples] -*** link:samples-step-by-step.asciidoc[devon4node Sample Step by Step] - diff --git a/documentation/devon4node-architecture.asciidoc b/documentation/devon4node-architecture.asciidoc deleted file mode 100644 index 052af473..00000000 --- a/documentation/devon4node-architecture.asciidoc +++ /dev/null @@ -1,148 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= devon4node Architecture - -As we have mentioned in the introduction, devon4node is based on link:https://nestjs.com/[NestJS]. Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. - -== HTTP layer - -By using link:https://nestjs.com/[NestJS], devon4node is a platform-agnostic framework. NestJS focuses only on the logical layer, and delegates the transport layer to another framework, such as ExpressJS. You can see it in the following diagram: - -image::images/devon4node-architecture.png[] - -As you can see, NestJS do not listen directly for incoming request. It has an adapter to communicate with ExpressJS and ExpressJS is the responsible for that. ExpressJS is only one of the frameworks that NestJS can work with. We have also another adapter available out-of-the-box: the link:https://www.fastify.io/[Fastify] adapter. With that, you can replace ExpressJS for Fastify But you can still use all your NestJS components. You can also create your own adapter to make NestJS work with other HTTP framework. - -At this point, you may think: why is NestJS (and devon4node) using ExpressJS by default instead of Fastify? Because, as you can see in the previous diagram, there is a component that is dependent on the HTTP framework: the middleware. As ExpressJS is the most widely used framework, there exists a lot of middleware for it, so, in order to reuse them in our NestJS applications, NestJS use ExpressJS by default. Anyway, you may think which HTTP framework best fits your requirements. - -== devon4node layers - -As other devonfw technologies, devon4node separates the application into layers. - -Those layers are: - -- link:layer-controller[Controller layer] -- link:layer-service[Service layer] -- link:layer-dataaccess[Data Access layer] - -image::images/plantuml/layers.png[] - -== devon4node application structure - -Although there are many frameworks to create backend applications in NodeJS, none of them effectively solve the main problem of - Architecture. This is the main reason we have chosen NestJS for the devon4node applications. Besides, NestJS is highly inspired by link:https://angular.io/[Angular], therefore a developer who knows Angular can use his already acquired knowledge to write devon4node applications. - -NestJS adopts various Angular concepts, such as dependency injection, piping, interceptors and modularity, among others. By using modularity we can reuse some of our modules between applications. One example that devon4node provide is the link:guides-mailer[mailer module]. - -=== Modules - -Create a application module is simple, you only need to create an empty class with the decorator `Module`: - -[source,typescript] ----- -@Module({}) -export class AppModule {} ----- - -In the module you can define: - -- Imports: the list of imported modules that export the providers which are required in this module -- Controllers: the set of controllers defined in this module which have to be instantiated -- Providers: the providers that will be instantiated by the Nest injector and that may be shared at least across this module -- Exports: the subset of providers that are provided by this module and should be available in other modules which import this module - -The main difference between Angular and NestJS is NestJS modules encapsulates providers by default. This means that it's impossible to inject providers that are neither directly part of the current module nor exported from the imported modules. Thus, you may consider the exported providers from a module as the module's public interface, or API. Example of modules graph: - -image::images/plantuml/modules.png[] - -In devon4node we three different kind of modules: - -- `AppModule`: this is the root module. Everything that our application need must be imported here. -- Global Modules: this is a special kind of modules. When you make a module global, it's accessible for every module in your application. Your can see it in the next diagram. It's the same as the previous one, but now the `CoreModule` is global: -+ -image::images/plantuml/module2.png[] -+ -One example of global module is the `CoreModule`. In the `CoreModule` you must import every module which have providers that needs to be accessible in all modules of you application -- Feature (or application) modules: modules which contains the logic of our application. We must import it in the `AppModule`. - -For more information about modules, see link:https://docs.nestjs.com/modules[NestJS documentation page] - -=== Folder structure - -devon4node defines a folder structure that every devon4node application must follow. The folder structure is: - ----- -├───src -│ ├───app -│ │ ├───core -│ │ │ ├───auth -│ │ │ ├───configuration -│ │ │ ├───user -│ │ │ └───core.module.ts -│ │ ├───shared -│ │ └───feature -│ │ ├───sub-module -│ │ │ ├───controllers -│ │ │ ├───... -│ │ │ ├───services -│ │ │ └───sub-module.module.ts -│ │ ├───controllers -│ │ ├───interceptors -│ │ ├───pipes -│ │ ├───guards -│ │ ├───filters -│ │ ├───middlewares -│ │ ├───model -│ │ │ ├───dto -│ │ │ └───entities -│ │ ├───services -│ │ └───feature.module.ts -│ ├───config -│ └───migration -├───test -└───package.json - ----- - -link:guides-code-generation[devon4node schematics] ensures this folder structure so, please, do not create files by your own, use the link:guides-code-generation[devon4node schematics]. - -=== NestJS components - -NestJS provides several components that you can use in your application: - -- link:https://docs.nestjs.com/controllers[Controllers] -- link:https://docs.nestjs.com/providers[Providers] -- link:https://docs.nestjs.com/middleware[Middleware] -- link:https://docs.nestjs.com/guards[Guards] -- link:https://docs.nestjs.com/interceptors[Interceptors] -- link:https://docs.nestjs.com/pipes[Pipes] -- link:https://docs.nestjs.com/exception-filters[Exception filters] - -In the link:https://docs.nestjs.com[NestJS documentation] you can find all information about each component. But, something that is missing in the documentation is the execution order. Every component can be defined in different levels: globally, in the controller or in the handler. As middleware is part of the HTTP server we can define it in a different way: globally or in the module. - -image::images/plantuml/components.png[] - -It is not necessary to have defined components in every level. For example, you can have defined a interceptor globally but you do not have any other in the controller or handler level. If nothing is defined in some level, the request will continue to the next component. - -As you can see in the previous image, the first component which receive the request is the global defined middleware. Then, it send the request to the module middleware. Each of them can return a response to the client, without passing the request to the next level. - -Then, the request continue to the guards: first the global guard, next to controller guard and finally to the handler guard. At this point, we can throw an exception in all components and the exception filter will catch it and send a proper error message to the client. We do not paint the filters in the graphic in order to simplify it. - -After the guards, is time to interceptors: global interceptors, controller interceptors and handler interceptors. And last, before arrive to the handler inside the controller, the request pass through the pipes. - -When the handler has the response ready to send to the client, it does not go directly to the client. It come again to the interceptors, so we can also intercept the response. The order this time is the reverse: handler interceptors, controller interceptors and global interceptors. After that, we can finally send the response to the client. - -Now, with this in mind, you are able to create the components in a better way. diff --git a/documentation/devon4node-introduction.asciidoc b/documentation/devon4node-introduction.asciidoc deleted file mode 100644 index 262e854d..00000000 --- a/documentation/devon4node-introduction.asciidoc +++ /dev/null @@ -1,27 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= devon4node - -link:https://www.devonfw.com/[devonfw] is a platform which provides solutions to building business applications which combine best-in-class frameworks and libraries as well as industry proven practices and code conventions. devonfw is 100% Open Source (Apache License version 2.0) since the beginning of 2018. - -devon4node is the NodeJS stack of devonfw. It allows you to build business applications (backends) using NodeJS technology in standardized way based on established best-practices. - -devon4node is based on link:https://nestjs.com/[NestJS]. Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming). - -In this wiki you can find all documentation related with devon4node. See choose the wiki page at the side-bar. - diff --git a/documentation/guides-auth-jwt.asciidoc b/documentation/guides-auth-jwt.asciidoc deleted file mode 100644 index 0e1721dd..00000000 --- a/documentation/guides-auth-jwt.asciidoc +++ /dev/null @@ -1,66 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Auth JWT module - -devon4node provides a way to generate a default authentication module using JWT (JSON Web Token). It uses the `@nestjs/passport` library describe link:https://docs.nestjs.com/techniques/authentication[here]. - -To generate the devon4node `auth-jwt` module you only need to execute the command: `nest generate -c @devon4node/schematics auth-jwt`. We generate this module inside the applications instead of distributing a npm package because this module is prone to be modified depending on the requirements. It also generate a basic user module. - -In this page we will explain the default implementation provided by devon4node. For more information about authentication, JWT, passport and other you can see: - -- link:https://jwt.io/introduction/[JWT] -- link:https://docs.nestjs.com/techniques/authentication[NestJS authentication] -- link:https://www.npmjs.com/package/passport[Passport] -- link:https://www.npmjs.com/package/passport-jwt[Passport JWT] - -== Auth JWT endpoints - -In order to execute authentication operations, the `auth-jwt` module exposes the following endpoints: - -- `POST /auth/login`: receive an username and a password and return the token in the header if the combination of username and password is correct. -- `POST /auth/register`: register a new user. -- `GET /auth/currentuser`: return the user data if he is authenticated. - -== Protect endpoints with auth-jwt - -In order to protect your endpoints with `auth-jwt` module you only need to add the `AuthGuard()` in the `UseGuards` decorator. Example: - -[source,typescript] ----- -@Get('currentuser') -@UseGuards(AuthGuard()) -currentUser(@Request() req: UserRequest) { - return req.user; -} ----- - -Now, all request to `currentuser` are protected by the `AuthGuard`. - -== Role based Access Control - -The `auth-jwt` module provides also a way to control the access to some endpoints by using roles. For example, if you want to grant access to a endpoint only to admins, you only need to add the `Roles` decorator to those endpoints with the roles allowed. Example: - -[source,typescript] ----- -@Get('currentuser') -@UseGuards(AuthGuard()) -@Roles(roles.ADMIN) -currentUser(@Request() req: UserRequest) { - return req.user; -} ----- diff --git a/documentation/guides-code-generation.asciidoc b/documentation/guides-code-generation.asciidoc deleted file mode 100644 index 642af260..00000000 --- a/documentation/guides-code-generation.asciidoc +++ /dev/null @@ -1,132 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Code Generation - -As we mention in the page link:guide-key-principles[key principles], one of our key principles is Productivity. In order to provide that productivity, we have some tools to generate code. These tools will help you generate the common parts of the application so that you can focus only on the specific functionality. - -Those tools are: - -- link:https://www.npmjs.com/package/@devon4node/schematics[devon4node schematics through Nest CLI] -- link:https://github.com/devonfw/cobigen[CobiGen] - -== Nest CLI and Devon4node schematics - -We are going to use the Nest CLI to generate code of our application, you can know more about NodeJs CLI in the official link:https://docs.nestjs.com/cli/overview:[documentation]. - -=== Install devon4node schematics - -First of all, you need to install Nest CLI - -Execute the command `yarn global add @nestjs/cli`. -You can also use npm: `npm install -g @nestjs/cli` - -And then Devon4node schematics globally with the following command: - -`yarn global add @devon4node/schematics` or `npm install -g @devon4node/schematics` - -[NOTE] -==== -If you get an error trying execute any devon4node schematic related to collection not found, try to reinstall devon4node/schematics on the project folder or be sure that schematics folder is inside @devon4node in node_modules. -`yarn add @devon4node/schematics` -==== - -=== Generate new devon4node application - - -To start creating a devon4node application, execute the command: - -`nest g -c @devon4node/schematics application [application-name]` - -If you do not put a name, the command line will ask you for one. - -=== Generate code for TypeORM - -Initialize TypeORM into your current project in a correct way. - -`nest g -c @devon4node/schematics typeorm` - -Then, you will be asked about which DB you want to use. - -image:images/typeorm-schematic.PNG[] - -=== Generate Resource - -Same as NestJS Resource but using devon4node folder structure. - -=== Generate TypeORM entity - -Add a TypeORM entity to your project. Requires TypeORM installed in the project. - -Execute `nest g -c @devon4node/schematics entity` and you will be asked for an entity name. - -=== Add config-module - -Add the config module to the project. - -It will add the @devon4node/common module as a project dependency. Then, it will generate the configuration module into your project and add it in the core module. Also, it generates the config files for the most common environments. - -The command to execute will be `nest g -c @devon4node/schematics config-module` - -=== Add mailer module - -Add @devon4node/mailer module to project. - -It will add the @devon4node/mailer module as a project dependency. Also, it will add it to the core module and it will generate some email template examples. - -Write the command `nest g -c @devon4node/schematics mailer` - - -=== Add swagger module - -Add swagger module to project. - -It will add the `@nestjs/swagger` module as a project dependency. Also, it will update the main.ts file in order to expose the endpoint for swagger. The default endpoint is: `/v1/api` - -Execute the command `nest g -c @devon4node/schematics swagger` - -=== Add `auth-jwt` module - -Add the `auth JWT` module to the project. - -It will add to your project the `auth-jwt` and user module. Also, it will import those modules into the core module. - -Execute `nest g -c @devon4node/schematics auth-jwt` - -=== Add security - -Add CORS and helmet to your project. - -It will add helmet package as project dependency and update the main.ts file in order to enable the CORS and helmet in your application. - -Execute `nest g -c @devon4node/schematics security` - -=== Generate database migrations - -. Generate database migrations -.. In order to create migration scripts with TypeORM, you need to install ts-node: `yarn global add ts-node` or `npm i -g ts-node` -.. Generate the tables creation migration: `yarn run typeorm migration:generate -n CreateTables` -+ -image::images/insert-data.PNG[] -It will connect to the database, read all entities and then it will generate a migration file with all SQL queries need to transform the current status of the database to the status defined by the entities. If the database is empty, it will generate all SQL queries need to create all tables defined in the entities. You can find a example in the `ToDo example` - -As TypeORM is the tool used for DB. You can check official documentation for more information. -See link:https://typeorm.io/#/using-cli[TypeORM CLI documentation]. - -== CobiGen - -Currently, we do not have templates to generate devon4node code. Instead, we have templates that read the code of a devon4node application and generate a devon4ng application. Visit the link:https://github.com/devonfw/cobigen[CobiGen] page for more information. diff --git a/documentation/guides-coding-conventions.asciidoc b/documentation/guides-coding-conventions.asciidoc deleted file mode 100644 index 97fe2b69..00000000 --- a/documentation/guides-coding-conventions.asciidoc +++ /dev/null @@ -1,160 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Coding Conventions - -devon4node defines some coding conventions in order to improve the readability, reduce the merge conflicts and be able to develop applications in an industrialized way. - -In order to ensure that you are following the devon4node coding conventions, you can use the following tools: - -- `ESLint`: link:https://eslint.org/[ESLint] ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs. We recommend to use the link:https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint[ESLint VSCode extension] (included in the devonfw Platform Extension Pack) in order to be able to see the linting errors while you are developing. -- `Prettier`: link:https://prettier.io/[Prettier] is a code formatter. We recommend to use the Prettier VSCode extension (included in the devonfw Platform Extension Pack) and enable the `editor.formatOnSave` option. -- `devon4node application schematic`: this tool will generate code following the devon4node coding conventions. Also, when you generate a new project using the devon4node application schematic, it generates the configuration files for TSLint and Prettier that satisfy the devon4node coding conventions. - -When you combine all tools, you can be sure that you follow the devon4node coding conventions. - -== Detailed devon4node Coding Conventions - -Here we will detail some of most important devon4node coding conventions. To be sure that you follows all devon4node coding conventions use the tools described before. - -=== Indentation - -All devon4node code files must be indented using spaces. The indentation with must be 2 spaces. - -=== White space - -In order to improve the readability of your code, you must introduce whitespaces. Example: - -[source,typescript] ----- -if(condition){ ----- - -must be - -[source,typescript] ----- -if (condition) { ----- - -=== Naming conventions - -==== File naming - -The file name must follow the pattern: (name in kebab case).(kind of component).(extension) -The test file name must follow the pattern: (name in kebab case).(kind of component).spec.(extension) - -Example: - ----- -auth-jwt.service.ts -auth-jwt.service.spec.ts ----- - -==== Interface naming - -The interface names must be in pascal case, and must start with I. There is some controversy in starting the interface names with an I, but we decided to do it because is most of cases you will have an interface and a class with the same name, so, to differentiate them, we decided to start the interfaces with I. Other devonfw stacks solves it by adding the suffix `Impl` in the class implementations. - -Example: - ----- -interface ICoffee {} ----- - -==== Class naming - -The class names must be in pascal case. - -Example: - ----- -class Coffee {} ----- - -==== Variable naming - -All variable names must be in camel case. ----- -const coffeeList: Coffe[]; ----- - -=== Declarations - -For all variable declarations we must use `const` or `let`. `var` is forbidden. We prefer to use `const` when possible. - -=== Programming practices - -==== Trailing comma - -All statements must end with a trailing comma. Example: - -[source,typescript] ----- -{ - one: 'one', - two: 'two' // bad -} -{ - one: 'one', - two: 'two', // good -} ----- - -==== Arrow functions - -All anonymous functions must be defined with the arrow function notation. In most of cases it's not a problem, but sometimes, when you do not want to bind `this` when you define the function, you can use the other function definition. In this special cases you must disable the linter for those sentence. - -==== Comments - -Comments must start with a whitespace. Example: - -[source,typescript] ----- -//This is a bad comment -// This is OK ----- - -==== Quotemarks - -For string definitions, we must use single quotes. - -==== if statements - -In all if statements you always must use brackets. Example: - -[source,typescript] ----- -// Bad if statement -if (condition) - return true; - -// Good if statement -if (condition) { - return true; -} ----- - -== Pre-commit hooks - -In order to ensure that your new code follows the coding conventions, devon4node uses by default husky. Husky is a tool that allows you to configure git hooks easily in your project. When you make a `git commit` in your devon4node project, it will execute two actions: - -* Prettify the staged files -* Execute the linter in the staged files - -If any action fails, you won't be able to commit your new changes. - -NOTE: If you want to skip the git hooks, you can do a commit passing the `--no-verify` flag. diff --git a/documentation/guides-configuration-module.asciidoc b/documentation/guides-configuration-module.asciidoc deleted file mode 100644 index ea2f94f9..00000000 --- a/documentation/guides-configuration-module.asciidoc +++ /dev/null @@ -1,133 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Configuration Module - -Devon4node configuration module allows you to manage the multiple configurations that your application can have. Its a built in package that you can install in your project: `@devon4node/config`. - -The main difference with other configuration modules is that `@devon4node/config` stores all configuration in typescript files. With that you will be able to configure different values depending on the environment, but also you can configure functions, arrays, objects and so on. As some configurations must secret and that configuration will be known only at runtime, it also provides a mechanism to override your configuration by using environment variables. - -With `@devon4node/config` you will be able to validate your configuration based on class-validator. - -== Include Configuration Module in your project - -devon4node provides a way to generate a configuration module inside your application. To generate it you only need to execute the command `nest g -c @devon4node/schematics config-module`. This command will generate inside your application: - -* Imports configuration module in the core module. -* config folder where all environment configuration are stored. -** default configuration: configuration for your local development environment. -** develop environment configuration for the develop environment. -** UAT environment configuration for the UAT environment. -** production environment configuration for the production environment. -** test environment configuration used by test. - -NOTE: some code generators will add some properties to this module, so, be sure that the config module is the first module that you generate in your application. - -== Configure the `ConfigModule` - -When you register the `ConfigModule` in your application you can configure it. As the `ConfigModule` should be a module that you will need in all modules in your application, it is registered by default in the `CoreModule` (remember that `CoreModule` is global). - -Example: - -[source,typescript] ----- -@Global() -@Module({ - imports: [ - ConfigModule.register({ - configDir: './dist/config', - configType: Config, - }), - ], - controllers: [], - providers: [], - exports: [ConfigModule], -}) -export class CoreModule {} ----- - -The values that you can provide to the `ConfigModule` are: - -- `configDir`: path to the directory where all config files are stored. This is the path to the compiled JavaScript files, not the path that include the TypeScript files. The default value is: './dist/config' -- `configType`: the type of the config files. You can use the default type generated at **shared/config/config.model.ts** or generate you own type. In this type is where you can define the validation by using the **class-validator** decorators. - -== Use the configuration service - -To use the configuration service, you only need to inject it as dependency. As configuration module is defined in the core module, it will be available everywhere in your application. Example: - -[source,typescript] ----- -export class MyProvider { - constructor(public readonly configService: ConfigService) {} - - myMethod() { - return this.confiService.values.isDev; - } -} ----- - -The generic type is required in order to provide the `intellisense` to `configService.values`. - -== Choose an environment file - -By default, when you use the configuration service it will take the properties defined in the default.ts file. If you want to change the configuration file, you only need to set the NODE_ENV environment property with the name of the desired environment. Examples: in windows execute `set NODE_ENV=develop` before executing the application, in Linux execute `NODE_ENV=develop` before executing the application or `NODE_ENV=develop yarn start`. - -In most scenarios, when you want to use a environment file you want to use the same values as the default.ts file and override some properties. For that, what @devon4node/config do is a merge of the default configuration file with the selected configuration file, so you don't need to repeat all configuration values in all configuration files, you need to put only the values that are different to the default one. - -== Override configuration properties - -Sometimes, you want to keep some configuration property secure, and you do not want to publish it to the repository, or you want to reuse some configuration file but you need to change some properties. For those scenarios, you can override configuration properties by defining a environment variable. The pattern for the environment variables name is: the property name in upper case, if it is a nested object you must separate the properties with underscores (`_`). For example, if you want to override the property `host`, you can do: `export HOST="newhost"` (in windows: `set HOST="newhost"`). It also works with objects. For example, if you want to change the value of secret in the property `jwtConfig` for link:https://github.com/devonfw/devon4node/blob/develop/samples/employee/src/config/develop.ts[this example], you can set a environment variable like this: `export JWTCONFIG_SECRET="newsecret"`. If you want to override `jwtConfig` totally (or partially) you can set a JSON string. It will take object and merge the `jwtConfig` property with the properties defined inside the environment variable. The other properties maintain their value. The behavior is the same for the nested objects. Example: JWTCONFIG='{"secret":"mysecret"}' will override the `jwtConfig`.secret value, but the other values will remain with the value defined at configuration file. - -NOTE: you can only override properties that exists in the config file. If you want to use a secret configuration that will be setted at runtime, first you must define it in the config file and set a fake value or just an empty string. - -== Add a configuration property - -In order to add a new property to the configuration module, you need to follow some steps: - -- Add the property to Config class at `src/app/shared/configuration/model/config/config.model.ts` file (or your custom Config class). -- Add the validation decorators (only if you want to use the validation feature) -- Add the property the config files inside the `src/config` folder. If you want to use the same value for all environments, just put the value inside default.ts file. - -Example: - -We want to add the property `devonfwUrl` to our `ConfigService`, so: - -We add the following code in Config class: - -[source,typescript] ----- -@IsString() -@IsDefined() -devonfwUrl!: string; ----- - -Then, we add the definition the config files: - -[source,typescript] ----- -devonfwUrl: 'https://devonfw.com', ----- - -== Validate your configuration - -In order to make you able to validate your configuration, this package includes a feature to do that by using the `class-decorator` package. - -To configure your validations you only need to add the `class-validator` decorators to your Config class. - -To enable the validation you only must set the `VALIDATE_CONFIG` environment variable to `true`. - -When enable it will execute the config validation when the application starts. If the validation fail, the application will not start. diff --git a/documentation/guides-dependency-injection.asciidoc b/documentation/guides-dependency-injection.asciidoc deleted file mode 100644 index 292a279b..00000000 --- a/documentation/guides-dependency-injection.asciidoc +++ /dev/null @@ -1,101 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Dependency Injection - -The link:https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] is a well-known common design pattern applied by frameworks in all languages, like link:https://spring.io/[Spring] in Java, link:https://angular.io/[Angular] and others. The intention of this page is not to explain how dependency injection works, but instead how it is addressed by NestJS. - -NestJS resolve the dependency injection in their modules. When you define a provider in a module, it can be injected in all components of the module. By default, those providers are only available in the module where it is defined. The only way to export a module provider to other modules which import it is adding those provider to the export array. You can also reexport modules. - -== Inject dependencies in NestJS - -In order to inject a dependency in a NestJS component, you need to declare it in the component constructor. Example: - -[source,typescript] ----- -export class CoffeeController { - constructor(public readonly conffeeService: CoffeeService) {} -} ----- - -NestJS can resolve all dependencies that are defined in the module as provider, and also the dependencies exported by the modules imported. Example: - -[source,typescript] ----- -@Module({ - controllers: [CoffeeController], - providers: [CoffeeService], -}) -export class CoffeeModule {} ----- - -Inject dependencies in the constructor is the is the preferred choice, but, sometimes it is not possible. For example, when you are extending another class and want to keep the constructor definition. In this specific cases we can inject dependencies in the class properties. Example: - -[source,typescript] ----- -export class CoffeeController { - @Inject(CoffeeService) - private readonly conffeeService: CoffeeService; -} ----- - -== Dependency Graph - -image::images/plantuml/dependency-injection1.png[] - -In the previous image, the Module A can inject dependencies exported by Module B, Module E and Module F. If module B reexport Module C and Module D, they are also accessible by Module A. - -If there is a conflict with the injection token, it resolves the provider with less distance with the module. For example: if the modules C and F exports a `UserService` provider, the Module A will resolve the `UserService` exported by the Module F, because the distance from Module A to Module F is 1, and the distance from Module A to Module C is 2. - -When you define a module as global, the dependency injection system is the same. The only difference is now all modules as a link to the global module. For example, if we make the Module C as global the dependency graph will be: - -image::images/plantuml/dependency-injection2.png[] - -== Custom providers - -When you want to change the provider name, you can use a NestJS feature called link:https://docs.nestjs.com/fundamentals/custom-providers[custom providers]. For example, if you want to define a provider called `MockUserService` with the provider token `UserService` you can define it like: - -[source,typescript] ----- -@Module({ - providers: [{ - provide: UserService, - useValue: MockUserService, - }], -}) ----- - -With this, when you inject want to inject `UserService` as dependency, the `MockUserService` will be injected. - -Custom provider token can be also a string: - -[source,typescript] ----- -@Module({ - providers: [{ - provide: 'USER_SERVICE', - useValue: MockUserService, - }], -}) ----- - -but now, when you want to inject it as dependency you need to use the @Inject decorator. - -[source,typescript] ----- -constructor(@Inject('USER_SERVICE') userService: any) {} ----- diff --git a/documentation/guides-eslint-sonarqube-config.asciidoc b/documentation/guides-eslint-sonarqube-config.asciidoc deleted file mode 100644 index f9b15c16..00000000 --- a/documentation/guides-eslint-sonarqube-config.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Importing your ESLint reports into SonarQube - -This guide covers the import of ESLint reports into SonarQube instances in CI environments, as this is the recommended way of using ESLint and SonarQube for devon4node projects. The prerequisites for this process are a CI environment, preferably a link:https://github.com/devonfw/production-line[Production Line] instance, and the link:https://eslint.org/docs/user-guide/command-line-interface[ESLint CLI], which is already included when generating a new devon4node project. - -=== Configuring the ESLint analysis - -You can configure the ESLint analysis parameters in the `.eslintrc.js` file inside the top-level directory of your project. If you created your node project using the devon4node application schematic, this file will already exist. If you want to make further adjustments to it, have a look at the link:https://eslint.org/docs/user-guide/configuring[ESLint documentation]. - -The ESLint analysis script `lint` is already configured in the `scripts` part of your `package.json`. Simply add `-f json > report.json`, so that the output of the analysis is saved in a .json file. Additional information to customization options for the ESLint CLI can be found link:https://eslint.org/docs/user-guide/command-line-interface#options[here]. - -To run the analysis, execute the script with `npm run lint` inside the base directory of your project. - -=== Configuring SonarQube - -If you haven't already generated your CICD-related files, follow the tutorial on the link:https://github.com/devonfw/cicdgen/wiki/devon4node-schematic[devon4node schematic] of our CICDGEN project, as you will need a Jenkinsfile configured in your project to proceed. - -Inside the script for the SonarQube code analysis in your Jenkinsfile, add the parameter `-Dsonar.eslint.reportPaths=report.json`. Now, whenever a SonarQube analysis is triggered by your CI environment, the generated report will be loaded into your SonarQube instance. -To avoid duplicated issues, you can associate an empty TypeScript quality profile with your project in its server configurations. diff --git a/documentation/guides-grapql.asciidoc b/documentation/guides-grapql.asciidoc deleted file mode 100644 index 567cbdbd..00000000 --- a/documentation/guides-grapql.asciidoc +++ /dev/null @@ -1,285 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= GraphQL on Devon4Node - -GraphQL is a query language that gets exactly the data that we ask for instead of static predefined responses. - -For example, on a regular API a get by id method would return something like: - -[source, json] ----- -{ - "location": { - "lon": 00.14, - "lat": 54.11 - }, - "station": "dsrEE3Sg", - "visibility": 5000, - "wind":{ - "speed": 6.2, - "deg": 78 - }, - "logs": [...] - ... -} ----- -But if we want to get *only* the wind data we have to create another endpoint that returns the specified data. - -But instead with GraphQL we can get different information without creating new endpoints, in this case we only want the wind data so it would return: - -[source, json] ----- -{ - "wind":{ - "speed": 6.2, - "deg": 78 - } -} ----- - -To install it: - -[source,bash] ----- -yarn add @nestjs/graphql graphql-tools graphql apollo-server-express ----- - -== Schema first - -[NOTE] -==== -This tutorial uses the schema first method. - -We assume you have already a functioning TODO module / app. - -If not you can use https://github.com/devonfw-sample/devon4node-samples/tree/develop/graphql[Devon4node GraphQL sample] -==== - -First we need to import `GraphQLModule` to our `app.module.ts`. - -[source,typescript] ----- -... -import { GraphQLModule } from '@nestjs/graphql'; -import { join } from 'path'; - -@Module({ - imports: [ - // Your module import - GraphQLModule.forRoot({ - typePaths: ['./**/*.graphql'], - definitions: { - path: join(process.cwd(), 'src/graphql.ts'), - outputAs: 'class', - }, - }), - ], -}) -export class AppModule {} ----- - -The `typePaths` indicates the location of the schema definition files. - -The `definitions` indicates the file where the typescript definitions will automatically save, adding the `outputAs: 'class'` saves those definitions as classes. - -=== Schema - -GraphQL is a typed language with `object types`, `scalars`, and `enums`. - -We use `query` to define the methods we are going to use for fetching data, and `mutations` are used for modifying this data, similar to how `GET` and `POST` work. - -Let's define the elements, queries and mutations that our module is going to have. - -For that we have to create a GraphQL file on our module, on this case we are going to name it `schema.graphql`. - -[source,typescript] ----- -type Todo { - id: ID - task: String -} - -type Query { - todos: [Todo] - todoById: Todo -} - -type Mutation { - createTodo(task: String): Todo - deleteTodo(id: String): Todo -} ----- - -For more information about Types go to the official https://graphql.org/learn/schema/[GraphQL documentation] - - -=== Resolver - -Resolver has the instructions to turn GraphQL orders into the data requested. - -To create a resolver we go to our module and then create a new `todo.resolver.ts` file, import the decorators needed and set the resolver. - -[source,typescript] ----- -import { Resolver, Args, Mutation, Query } from '@nestjs/graphql'; -import { TodoService } from '../services/todo.service'; -import { Todo } from '../schemas/todo.schema'; - -@Resolver() -export class TodoResolver { - constructor(private readonly todoService: TodoService) {} - - @Query('todos') - findAll(): Promise { - return this.todoService.findAll(); - } - - @Query('todoById') - findOneById(@Args('id') id: string): Promise { - return this.todoService.findOneById(id); - } - - @Mutation() - createTodo(@Args('task') task: string): Promise { - return this.todoService.create(task); - } - - @Mutation() - deleteTodo(@Args('id') id: string): Promise { - return this.todoService.delete(id); - } -} ----- - -`@Resolver()` indicates that the next class is a resolver. - -`@Query` is used to get data. - -`@Mutation` is used to create or modify data. - -Here we have also an argument decorator `@Args` which is an object with the arguments passed into the field in the query. - -By default we can access the query or mutation using the method's name, for example: - -For the `deleteTodo` mutation. - -[source,typescript] ----- -mutation { - deleteTodo( id: "6f7ed2q8" ){ - id, - task - } -} ----- - -But if we write something different on the decorator, we change the name, for example: - -For the `findAll` query, we named it `todos`. -[source,typescript] ----- -{ - todos{ - id, - task - } -} ----- -Also if we go back to the `schema.graphql`, we will see how we define the query with `todos`. - -Learn more about Resolvers, mutations and their argument decorators on the https://docs.nestjs.com/graphql/resolvers#schema-first[NestJS documentation]. - - -=== Playground - -To test our backend we can use tools as Postman, but GraphQL already gives us a playground to test our Resolvers, we can access by default on `http://localhost:3000/graphql`. - -We can call a query, or several queries this way: - -[source,typescript] ----- -{ - findAll{ - id, - task - } -} ----- - -And the output will look something like: -[source,typescript] ----- -{ - "data": { - "findAll": [ - { - "id": "5fb54b30e686cb49500b6728", - "task": "clean dishes" - }, - { - "id": "5fb54b3be686cb49500b672a", - "task": "burn house" - } - ] - } -} ----- - -As we can see, we get a json "data" with an array of results. - -And for our mutations it's very similar, in this case we create a todo with task "rebuild house" and we are going to ask on the response just for the task data, we don't want the id. - -[source,typescript] ----- -mutation{ - createTodo ( - task: "rebuild house" - ){ - task - } -} ----- - -And the output - -[source,json] ----- -{ - "data": { - "createTodo": { - "task": "rebuild house" - } - } -} ----- - -In this case we return just one item so there is no array, we also got just the `task data` but if we want the `id` too, we just have to add it on the request. - -To make the playground unavailable we can add an option to the app.module import: - -[source,typescript] ----- -... -GraphQLModule.forRoot({ - ... - playground: false, -}), -... ----- - -For further information go to the official https://docs.nestjs.com/graphql/quick-start[NestJS documentation] diff --git a/documentation/guides-key-principles.asciidoc b/documentation/guides-key-principles.asciidoc deleted file mode 100644 index 229e88f7..00000000 --- a/documentation/guides-key-principles.asciidoc +++ /dev/null @@ -1,88 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Key Principles - -devon4node is built following some basic principles like: - -- link:https://en.wikipedia.org/wiki/SOLID[SOLID] -- link:https://en.wikipedia.org/wiki/Software_design_pattern[Patterns] -- link:https://en.wikipedia.org/wiki/Open-source_model[Open] - -But key principles that best define devon4node (and are inherited from NestJS) are: - -- Simplicity (aka link:https://en.wikipedia.org/wiki/KISS_principle[KISS]) -- Reusability -- Productivity - -== Simplicity - -In devon4node we tried to do everything as simple as possible. Following this principle we will be able to do easy to maintain applications. - -For example, in order to expose all CRUD operations for an entity, you only need to create a controller like: - -[source,typescript] ----- -@Crud({ - model: { - type: Employee, - }, -}) -@CrudType(Employee) -@Controller('employee/employees') -export class EmployeeCrudController { - constructor(public service: EmployeeCrudService) {} -} ----- - -You can find this code in the link:samples[employee example]. Only with this code your exposing the full CRUD operations for the employee entity. As you can see, it's an empty class with some decorators and the `EmployeeCrudService` injected as dependency. Simple, isn't it? The `EmployeeCrudService` is also simple: - -[source,typescript] ----- -@Injectable() -export class EmployeeCrudService extends TypeOrmCrudService { - constructor(@InjectRepository(Employee) repo: Repository) { - super(repo); - } -} ----- - -Another empty class which extends from `TypeOrmCrudService` and injects the Employee Repository as dependency. Nothing else. - -With these examples you can get an idea of how simple it can be to code a devon4node application . - -== Reusability - -NestJS (and devon4node) applications are designed in a modular way. This allows you to isolate some functionality in a module, and then reuse it in every application that you need. This is the same behavior that Angular has. You can see it in the NestJS modules like link:https://github.com/nestjs/typeorm[TypeORM], link:https://github.com/nestjs/swagger[Swagger] and others. Also, in devon4node we have the link:https://www.npmjs.com/package/@devon4node/mailer[Mailer module]. - -In your applications, you only need to import those modules and then you will be able to use the functionality that they implement. Example - -[source,typescript] ----- -@Module({ - imports: [ AuthModule, ConfigurationModule ], -}) -export class SomeModule {} ----- - -== Productivity - -devon4node is designed to create secure enterprise applications. But also, it allow you to do it in a fast way. To increase the productivity devon4node, devon4node provide schematics in order to generate some boilerplate code. - -For example, to create a module you need to create a new file for a module (or copy it) and write the code, then you need to import it in the `AppModule`. This is a easy example, but you can introduce some errors: forget to import it in the `AppModule`, introduce errors with the copy/paste and so on. By using the command `nest g module --name ` it will do everything for you. Just a simple command. In this specific case probably you do not see any advantage, but there are other complex cases where you can generate more complex code with nest and devon4node schematics command. - -See link:guides-code-generation[code generation] in order to know how to increase your productivity creating devon4node applications. diff --git a/documentation/guides-logger.asciidoc b/documentation/guides-logger.asciidoc deleted file mode 100644 index 02a683d8..00000000 --- a/documentation/guides-logger.asciidoc +++ /dev/null @@ -1,45 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Logger - -When you create a new devon4node application, it already has a logger: `src/app/shared/logger/winston.logger.ts`. This logger provide the methods `log`, `error` and `warn`. All of those methods will write a log message, but with a different log level. - -The Winston logger has two transports: one to log everything inside the file logs/general.log and the other to log only the error logs inside the file logs/error.log. In addition, it uses the default NestJS logger in order to show the logs in the console. - -As you can see it is a simple example about how to use logger in a devon4node application. It will be update to a complex one in the next versions. - -== How to use logger - -In order to use the logger you only need to inject the logger as a dependency: - -[source,typescript] ----- -constructor(logger: WinstonLogger){} ----- - -and then use it - -[source,typescript] ----- -async getAll() { - this.service.getAll(); - this.logger.log('Returning all data'); -} ----- - - diff --git a/documentation/guides-mailer.asciidoc b/documentation/guides-mailer.asciidoc deleted file mode 100644 index bd4e1e45..00000000 --- a/documentation/guides-mailer.asciidoc +++ /dev/null @@ -1,279 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Mailer Module - -This module enables you to send emails in devon4node. It also provides a template engine using Handlebars. - -It is a NestJS module that inject into your application a `MailerService`, which is the responsible to send the emails using the `nodemailer` library. - -== Installing - -Execute the following command in a devon4node project: - -[source,shell] ----- -yarn add @devon4node/mailer ----- - -== Configuring - -To configure the mailer module, you only need to import it in your application into another module. Example: - -[source,typescript] ----- -@Module({ - ... - imports: [ - MailerModule.forRoot(), - ], - ... -}) ----- - -Your must pass the configuration using the `forRoot` or `forRootAsync` methods. - -=== `forRoot()` - -The `forRoot` method receives an `MailerModuleOptions` object as parameter. It configures the `MailerModule` using the input `MailerModuleOptions` object. - -The structure of `MailerModuleOptions` is: - -[source,typescript] ----- -{ - hbsOptions?: { - templatesDir: string; - extension?: string; - partialsDir?: string; - helpers?: IHelperFunction[]; - compilerOptions?: ICompileOptions; - }, - mailOptions?: nodemailerSmtpTransportOptions; - emailFrom: string; -} ----- - -Here, you need to specify the link:https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options[Handlebars compile options], the link:https://nodemailer.com/smtp[nodemailer transport options] and the email address which will send the emails. -Then, you need to call to `forRoot` function in the module imports. Example: - -[source,typescript] ----- -@Module({ - ... - imports: [ - MailerModule.forRoot({ - mailOptions: { - host: 'localhost', - port: 1025, - secure: false, - tls: { - rejectUnauthorized: false, - }, - }, - emailFrom: 'noreply@capgemini.com', - hbsOptions: { - templatesDir: join(__dirname, '../..', 'templates/views'), - partialsDir: join(__dirname, '../..', 'templates/partials'), - helpers: [{ - name: 'fullname', - func: person => `${person.name} ${person.surname}`,s - }], - }, - }), - ... -}) ----- - -=== `forRootAsync()` - -The method `forRootAsync` enables you to get the mailer configuration in a asynchronous way. It is useful when you need to get the configuration using, for example, a service (e.g. `ConfigurationService`). - -Example: - -[source,typescript] ----- -@Module({ - ... - imports: [ - MailerModule.forRootAsync({ - imports: [ConfigurationModule], - useFactory: (config: ConfigurationService) => { - return config.mailerConfig; - }, - inject: [ConfigurationService], - }), - ... -}) ----- - -In this example, we use the `ConfigurationService` in order to get the `MailerModuleOptions` (the same as `forRoot`) - -== Usage - -In order to use, you only need to inject using the dependency injection the `MailerService`. - -Example: - -[source,typescript] ----- -@Injectable() -export class CatsService { - constructor(private readonly mailer: MailerService) {} -} ----- - -Then, you only need to use the methods provided by the `MailerService` in your service. Take into account that you can inject it in every place that support NestJS dependency injection. - -=== `MailerService` methods - -==== `sendPlainMail` - -The method `sendPlainMail` receive a string sends a email. - -The method signatures are: - -[source,typescript] ----- -sendPlainMail(emailOptions: SendMailOptions): Promise; -sendPlainMail(to: string, subject: string, mail: string): Promise; ----- - -Examples: - -[source,typescript] ----- -this.mailer.sendPlainMail({ - to: 'example@example.com', - subject: 'This is a subject', - html: '

Hello world

' -}); -this.mailer.sendPlainMail('example@example.com', 'This is a subject', '

Hello world

'); ----- - -==== `sendTemplateMail` - -The method `sendTemplateMail` sends a email based on a Handlebars template. The templates are registered using the `templatesDir` option or using the `addTemplate` method. -The template name is the name of the template (without extension) or the first parameter of the method `addTemplate`. - -The method signatures are: - -[source,typescript] ----- -sendTemplateMail(emailOptions: SendMailOptions, templateName: string, emailData: any, hbsOptions?: RuntimeOptions): Promise; -sendTemplateMail(to: string, subject: string, templateName: string, emailData: any, hbsOptions?: RuntimeOptions): Promise; ----- - -Examples: - -[source,typescript] ----- -this.mailer.sendTemplateMail({ - to: 'example@example.com', - subject: 'This is a subject', - html: '

Hello world

' -}, 'template1', { person: {name: 'Dario', surname: 'Rodriguez'}}); -this.mailer.sendTemplateMail('example@example.com', 'This is a subject', 'template1', { person: {name: 'Dario', surname: 'Rodriguez'}}); ----- - -==== `addTemplate` - -Adds a new template to the `MailerService`. - -Method signature: - -[source,typescript] ----- -addTemplate(name: string, template: string, options?: CompileOptions): void; ----- - -Example: - -[source,typescript] ----- -this.mailer.addTemplate('newTemplate', '{{>partial1}}') ----- - -==== `registerPartial` - -Register a new partial in Handlebars. - -Method signature: - -[source,typescript] ----- -registerPartial(name: string, partial: Handlebars.Template): void; ----- - -Example: - -[source,typescript] ----- -this.mailer.registerPartial('partial', '

Hello World

') ----- - -==== `registerHelper` - -Register a new helper in Handlebars. - -Method signature: - -[source,typescript] ----- -registerHelper(name: string, helper: Handlebars.HelperDelegate): void; ----- - -Example: - -[source,typescript] ----- -this.mailer.registerHelper('fullname', person => `${person.name} ${person.surname}`) ----- - -== Handlebars templates - -As mentioned above, this module allow you to use Handlebars as template engine, but it is optional. If you do not need the Handlebars, you just need to keep the `hbsOptions` undefined. - -In order to get the templates form the file system, you can specify the template folder, the partials folder and the helpers. -At the moment of module initialization, it will read the content of the template folder, and will register every file with the name (without extension) and the content as Handlebars template. It will do the same for the partials. - -You can specify the extension of template files using the `extension` parameter. The default value is `.handlebars` - -== Local development - -If you want to work with this module but you don't have a SMTP server, you can use the `streamTransport`. Example: - -[source,typescript] ----- -{ - mailOptions: { - streamTransport: true, - newline: 'windows', - }, - emailFrom: ... - hbsOptions: ... -} ----- - -Then, you need to get the `sendPlainMail` or `sendTemplateMail` result, and print the email to the standard output (`STDOUT`). Example: - -[source,typescript] ----- -const mail = await this.mailer.sendTemplateMail(...); - -mail.message.pipe(process.stdout); ----- diff --git a/documentation/guides-serializer.asciidoc b/documentation/guides-serializer.asciidoc deleted file mode 100644 index 4206a2bd..00000000 --- a/documentation/guides-serializer.asciidoc +++ /dev/null @@ -1,45 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Serializer - -Serialization is the process of translating data structures or object state into a format that can be transmitted across network and reconstructed later. - -NestJS by default serialize all data to JSON (`JSON.stringify`). Sometimes this is not enough. In some situations you need to exclude some property (e.g password). Instead doing it manually, devon4node provides an interceptor (`ClassSerializerInterceptor`) that will do it for you. You only need to return a class instance as always and the interceptor will transform those class to the expected data. - -The `ClassSerializerInterceptor` takes the link:https://github.com/typestack/class-transformer[class-transformer] decorators in order to know how to transform the class and then send the result to the client. - -Some of class-transformer decorators are: - -- Expose -- Exclude -- Type -- Transform - -And methods to transform data: - -- plainToClass -- plainToClassFromExist -- classToPlain -- classToClass -- serialize -- deserialize -- deserializeArray - -See the link:https://github.com/typestack/class-transformer[class-transformer] page for more information. - -See link:https://docs.nestjs.com/techniques/serialization[NestJS serialization page] for more information about `ClassSerializerInterceptor`. diff --git a/documentation/guides-swagger.asciidoc b/documentation/guides-swagger.asciidoc deleted file mode 100644 index cfc48212..00000000 --- a/documentation/guides-swagger.asciidoc +++ /dev/null @@ -1,30 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Swagger - -We can use swagger (OpenAPI) in order to describe the endpoints that our application exposes. - -NestJS provides a module which will read the code of our application and will expose one endpoint where we can see the swagger. - -Add swagger to a devon4node application is simple, you only need to execute the command `nest g -c @devon4node/schematics swagger` and it will do everything for you. The next time that you start your application, you will be able to see the swagger at `/v1/api` endpoint. - -The swagger module can read your code in order to create the swagger definition, but sometimes you need to help him by decorating your handlers. - -For more information about decorators and other behavior about swagger module, you can see the link:https://docs.nestjs.com/recipes/swagger[NestJS swagger documentation page] - -NOTE: the OpenAPI specification that this module supports is v2.0. The OpenAPI v3.0 is not available yet by using this module. diff --git a/documentation/guides-typeorm.asciidoc b/documentation/guides-typeorm.asciidoc deleted file mode 100644 index 5dc4ac7a..00000000 --- a/documentation/guides-typeorm.asciidoc +++ /dev/null @@ -1,96 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= TypeORM - -TypeORM is the default ORM provided by devon4node. It supports MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, Microsoft SQL Server, Oracle and also supports MongoDB NoSQL database. - -Add TypeORM support to a devon4node application is very easy: you only need to execute the command `nest g -c @devon4node/schematics typeorm` and it will add all required dependencies to the project and also imports the `@nestjs/typeorm` module. - -For more information about TypeORM and the integration with NestJS you can visit link:https://typeorm.io[TypeORM webpage], link:https://github.com/typeorm/typeorm[TypeORM GitHub repository] and link:https://docs.nestjs.com/recipes/sql-typeorm[NestJS TypeORM documentation page] - -== Configuration - -When you have the configuration module, the TypeORM generator will add one property in order to be able to configure the database depending on the environment. Example: - -[source,typescript] ----- -database: { - type: 'sqlite', - database: ':memory:', - synchronize: false, - migrationsRun: true, - logging: true, - entities: ['dist/**/*.entity.js'], - migrations: ['dist/migration/**/*.js'], - subscribers: ['dist/subscriber/**/*.js'], - cli: { - entitiesDir: 'src/entity', - migrationsDir: 'src/migration', - subscribersDir: 'src/subscriber', - }, -}, ----- - -This object is a TypeORM `ConnectionOptions`. For fore information about it visit the link:https://typeorm.io/#/connection-options/[TypeORM Connection Options page]. - -There is also a special case: the default configuration. As the devon4node CLI need the database configuration when you use the `devon4node db` command, we also provide the ormconfig.json file. In this file you must put the configuration for you local environment. In order to do not have duplicated the configuration for local environment, in the default config file the database property is set-up like: - -[source,typescript] ----- -database: require('../../ormconfig.json'), ----- - -So, you only need to maintain the ormconfig.json file for the local environment. - -== Entity - -Entity is a class that maps to a database table. The devon4node schematics has a generator to create new entities. You only need to execute the command `nest g -c @devon4node/schematics entity ` and it generate the entity. - -In the entity, you must define all columns, relations, primary keys of your database table. By default, devon4node provides a class named `BaseEntity`. All entities created with the devon4node schematics will extends the `BaseEntity`. This entity provides you some common columns: - -- `id`: the primary key of you table -- `version`: the version of the entry (used for auditing purposes) -- `createdAt`: creation date of the entry (used for auditing purposes) -- `updatedAt`: last update date of the entry (used for auditing purposes) - -For more information about Entities, please visit the link:https://typeorm.io/#/entities[TypeORM entities page] - -== Repository - -With repositories, you can manage (insert, update, delete, load, etc.) a concrete entity. Using this pattern, we have separated the data (Entities) from the methods to manage it (Repositories). - -To use a repository you only need to: - -- Import it in the module as follows: -+ -[source,typescript] ----- -@Module({ - imports: [TypeOrmModule.forFeature([Employee])], -}) ----- -+ -NOTE: if you generate the entities with the devon4node schematic, this step is not necessary, devon4node schematic will do it for you. -+ -- Inject the repository as dependency in your service: -+ ----- -constructor(@InjectRepository(Employee) employeeRepository: Repository) {} ----- - -You can see more details in the link:https://docs.nestjs.com/techniques/database[NestJS database] and link:https://docs.nestjs.com/recipes/sql-typeorm[NestJS TypeORM] documentation pages. diff --git a/documentation/guides-validation.asciidoc b/documentation/guides-validation.asciidoc deleted file mode 100644 index 46298de3..00000000 --- a/documentation/guides-validation.asciidoc +++ /dev/null @@ -1,53 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Validation - -To be sure that your application will works well, you must validate any input data. devon4node by default provides a `ValidationPipe`. This `ValidationPipe` is the responsible of validate the request input and, if the input do not pass the validation process, it returns a `400 Bad Request` error. - -== Defining Validators - -The `ValidationPipe` needs to know how to validate the input. For that purpose we use the `class-validator` package. This package allows you to define the validation of a class by using decorators. - -For example: - -[source,typescript] ----- -export class Coffee { - @IsDefined() - @IsString() - @MaxLength(255) - name: string; - - @IsDefined() - @IsString() - @MaxLength(25) - type: string; - - @IsDefined() - @IsNumber() - quantity: number; -} ----- - -As you can see in the previous example, we used some decorators in order to define the validators for every property of the Coffee class. You can find all decorators in the link:https://github.com/typestack/class-validator[class-validator github repository]. - -Now, when you want to receive a Coffee as input in some endpoint, it will execute the validations before executing the handler function. - -NOTE: In order to be able to use the class-validator package, you must use classes instead of interfaces. As you know interfaces disappear at compiling time, and class-validator need to know the metadata of the properties in order to be able to validate. - -NOTE: The `ValidationPipe` only works if you put a specific type in the handler definition. For example, if you define a handler like `getCoffee(@Body() coffee: any): Coffee {}` the ValidationPipe will not do anything. You must specify the type of the input: `getCoffee(@Body() coffee: Coffee): Coffee {}` diff --git a/documentation/images/crud-schematic.PNG b/documentation/images/crud-schematic.PNG deleted file mode 100644 index f1b94f8d..00000000 Binary files a/documentation/images/crud-schematic.PNG and /dev/null differ diff --git a/documentation/images/devon4node-architechture.png b/documentation/images/devon4node-architechture.png deleted file mode 100644 index 653b0bbe..00000000 Binary files a/documentation/images/devon4node-architechture.png and /dev/null differ diff --git a/documentation/images/generate-interactive.jpg b/documentation/images/generate-interactive.jpg deleted file mode 100644 index 54e97b52..00000000 Binary files a/documentation/images/generate-interactive.jpg and /dev/null differ diff --git a/documentation/images/insert-data.PNG b/documentation/images/insert-data.PNG deleted file mode 100644 index 0b0dfd2a..00000000 Binary files a/documentation/images/insert-data.PNG and /dev/null differ diff --git a/documentation/images/new-app1.jpg b/documentation/images/new-app1.jpg deleted file mode 100644 index 40c150a0..00000000 Binary files a/documentation/images/new-app1.jpg and /dev/null differ diff --git a/documentation/images/new-app2.jpg b/documentation/images/new-app2.jpg deleted file mode 100644 index 67c86f2c..00000000 Binary files a/documentation/images/new-app2.jpg and /dev/null differ diff --git a/documentation/images/new-app3.jpg b/documentation/images/new-app3.jpg deleted file mode 100644 index 7b3f6021..00000000 Binary files a/documentation/images/new-app3.jpg and /dev/null differ diff --git a/documentation/images/new-app4.jpg b/documentation/images/new-app4.jpg deleted file mode 100644 index 27eccdef..00000000 Binary files a/documentation/images/new-app4.jpg and /dev/null differ diff --git a/documentation/images/new-app5.jpg b/documentation/images/new-app5.jpg deleted file mode 100644 index fe34d164..00000000 Binary files a/documentation/images/new-app5.jpg and /dev/null differ diff --git a/documentation/images/plantuml/components.png b/documentation/images/plantuml/components.png deleted file mode 100644 index ec0207dd..00000000 Binary files a/documentation/images/plantuml/components.png and /dev/null differ diff --git a/documentation/images/plantuml/dependency-injection1.png b/documentation/images/plantuml/dependency-injection1.png deleted file mode 100644 index e909d946..00000000 Binary files a/documentation/images/plantuml/dependency-injection1.png and /dev/null differ diff --git a/documentation/images/plantuml/dependency-injection2.png b/documentation/images/plantuml/dependency-injection2.png deleted file mode 100644 index e79d2401..00000000 Binary files a/documentation/images/plantuml/dependency-injection2.png and /dev/null differ diff --git a/documentation/images/plantuml/layers.png b/documentation/images/plantuml/layers.png deleted file mode 100644 index d464104f..00000000 Binary files a/documentation/images/plantuml/layers.png and /dev/null differ diff --git a/documentation/images/plantuml/module2.png b/documentation/images/plantuml/module2.png deleted file mode 100644 index bc1f31bc..00000000 Binary files a/documentation/images/plantuml/module2.png and /dev/null differ diff --git a/documentation/images/plantuml/modules.png b/documentation/images/plantuml/modules.png deleted file mode 100644 index ffb3653f..00000000 Binary files a/documentation/images/plantuml/modules.png and /dev/null differ diff --git a/documentation/images/sample/employees.png b/documentation/images/sample/employees.png deleted file mode 100644 index 1af817b0..00000000 Binary files a/documentation/images/sample/employees.png and /dev/null differ diff --git a/documentation/images/sample/generate-migrations.png b/documentation/images/sample/generate-migrations.png deleted file mode 100644 index a0414e9b..00000000 Binary files a/documentation/images/sample/generate-migrations.png and /dev/null differ diff --git a/documentation/images/sample/insert-data.png b/documentation/images/sample/insert-data.png deleted file mode 100644 index 0b0dfd2a..00000000 Binary files a/documentation/images/sample/insert-data.png and /dev/null differ diff --git a/documentation/images/sample/new-app.png b/documentation/images/sample/new-app.png deleted file mode 100644 index f2c3638c..00000000 Binary files a/documentation/images/sample/new-app.png and /dev/null differ diff --git a/documentation/images/sample/start-app.png b/documentation/images/sample/start-app.png deleted file mode 100644 index 90dd1a39..00000000 Binary files a/documentation/images/sample/start-app.png and /dev/null differ diff --git a/documentation/images/sample/swagger.png b/documentation/images/sample/swagger.png deleted file mode 100644 index 4453b595..00000000 Binary files a/documentation/images/sample/swagger.png and /dev/null differ diff --git a/documentation/images/sample/test.png b/documentation/images/sample/test.png deleted file mode 100644 index ba775b27..00000000 Binary files a/documentation/images/sample/test.png and /dev/null differ diff --git a/documentation/images/typeorm-schematic.PNG b/documentation/images/typeorm-schematic.PNG deleted file mode 100644 index 2a3d09b9..00000000 Binary files a/documentation/images/typeorm-schematic.PNG and /dev/null differ diff --git a/documentation/layer-controller.asciidoc b/documentation/layer-controller.asciidoc deleted file mode 100644 index 7c549ea0..00000000 --- a/documentation/layer-controller.asciidoc +++ /dev/null @@ -1,76 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Controller Layer - -The controller layer is responsible for handling the requests/responses to the client. This layer knows everything about the endpoints exposed, the expected input (and also link:guides-validation[validate] it), the response schema, the HTTP codes for the response and the HTTP errors that every endpoint can send. - -== How to implement the controller layer - -This layer is implemented by the link:https://docs.nestjs.com/controllers[NestJS controllers]. Let's see how it works with an example: - -[source,typescript] ----- -@Controller('coffee/coffees') -export class CoffeeController { - constructor(private readonly coffeeService: CoffeeService) {} - - @Post('search') - @HttpCode(200) - async searchCoffees(@Body() search: CoffeeSearch): Promise> { - try { - return await this.coffeeService.searchCoffees(search); - } catch (error) { - throw new BadRequestException(error.message, error); - } - } -} ----- - -As you can see in the example, to create a controller you only need to decorate a class with the `Controller` decorator. This example is handling all request to `coffee/coffees`. - -Also, you have defined one handler. This handler is listening to POST request for the route `coffee/coffees/search`. In addition, this handler is waiting for a `CoffeeSearch` object and returns an array of Coffee. In order to keep it simple, that's all that you need in order to define one route. - -One important thing that can be observed in this example is that there is no business logic. It delegates to the service layer and return the response to the client. At this point, transformations from the value that you receive from the service layer to the desired return type are also allowed. - -By default, every POST handler return an HTTP 204 response with the returned value as body, but you can change it in a easy way by using decorators. As you can see in the example, the handler will return a HTTP 200 response (`@HttpCode(200)`). - -Finally, if the service layer throws an error, this handler will catch it and return a HTTP 400 Bad Request response. The controller layer is the only one that knows about the answers to the client, therefore it is the only one that knows which error codes should be sent. - -== Validation - -In order to do not propagate errors in the incoming payload, we need to validate all data in the controller layer. See the link:guides-validation[validation guide] for more information. - -== Error handling - -In the previous example, we catch all errors using the try/catch statement. This is not the usual implementation. In order to catch properly the errors you must use the link:https://docs.nestjs.com/exception-filters[exception filters]. Example: - - -[source,typescript] ----- -@Controller('coffee/coffees') -export class CoffeeController { - constructor(private readonly coffeeService: CoffeeService) {} - - @Post('search') - @HttpCode(200) - @UseFilters(CaffeExceptionFilter) - async searchCoffees(@Body() search: CoffeeSearch): Promise> { - return await this.coffeeService.searchCoffees(search); - } -} ----- diff --git a/documentation/layer-dataaccess.asciidoc b/documentation/layer-dataaccess.asciidoc deleted file mode 100644 index 4795538f..00000000 --- a/documentation/layer-dataaccess.asciidoc +++ /dev/null @@ -1,32 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Data Access Layer - -The data access layer is responsible for all outgoing connections to access and process data. This is mainly about accessing data from a persistent data-store but also about invoking external services. - -This layer is implemented using providers. Those providers could be: services, repositories and others. Although services can be used for this layer, they should not be confused with the service layer. Services in this layer are responsible for data access, while services in the service layer are responsible for business logic. - -== Database - -We strongly recommend link:https://typeorm.io[TypeORM] for database management in devon4node applications. Although services can be used for this layer, they should not be confused with the service layer. Services in this layer are responsible for data access, while services in the service layer are responsible for business logic. TypeORM supports the most commonly used relational databases, link Oracle, MySQL, MariaDB, PostgreSQL, SQLite, MSSQL and others. Also, it supports no-relational databases like MongoDB. - -TypeORM supports link:https://en.wikipedia.org/wiki/Active_record_pattern[Active Record] and Repository patterns. We recommend to use the Repository pattern. This pattern allows you to separate the data objects from the methods to manipulate the database. - -== External APIs - -In order to manage the data in a external API, you need to create a service for that purpose. In order to manage the connections with the external API, we strongly recommend the link:https://docs.nestjs.com/techniques/http-module[NestJS HTTP module] diff --git a/documentation/layer-service.asciidoc b/documentation/layer-service.asciidoc deleted file mode 100644 index 7b6600d5..00000000 --- a/documentation/layer-service.asciidoc +++ /dev/null @@ -1,42 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Service Layer - -The logic layer is the heart of the application and contains the main business logic. It knows everything about the business logic, but it does not know about the response to the client and the HTTP errors. That's why this layer is separated from the controller layer. - -== How to implement the service layer - -This layer is implemented by services, a specific kind of link:https://docs.nestjs.com/providers[providers]. Let's see one example: - -[source,typescript] ----- -@Injectable() -export class CoffeeService { - constructor(private readonly coffeeService: CoffeeService) {} - - async searchCoffees(@InjectRepository(Coffee) coffeeRepository: Repository): Promise> { - const coffees = this.coffeeRepository.find(); - - return doSomeBusinessLogic(coffees); - } -} ----- - -This is the `CoffeeService` that we inject in the example of link:layer-controller[controller layer]. As you can see, a service is a regular class with the `Injectable` decorator. Also, it inject as dependency the data access layer (in this specific case, the Repository). - -The services expose methods in order to transform the input from the controllers by applying some business logic. They can also request data from the data access layer. And that's all. diff --git a/documentation/master-devon4node.asciidoc b/documentation/master-devon4node.asciidoc deleted file mode 100644 index e86990ea..00000000 --- a/documentation/master-devon4node.asciidoc +++ /dev/null @@ -1,36 +0,0 @@ -= NodeJS - -link:https://www.devonfw.com/[devonfw] is a platform which provides solutions to building business applications which combine best-in-class frameworks and libraries as well as industry proven practices and code conventions. devonfw is 100% Open Source (Apache License version 2.0) since the beginning of 2018. - -devon4node is the NodeJS stack of devonfw. It allows you to build business applications (backends) using NodeJS technology in standardized way based on established best-practices. - -devon4node is based on link:https://nestjs.com/[NestJS]. Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming). - -include::devon4node-architecture[leveloffset=1] - -== Layers - -include::layer-controller[leveloffset=2] -include::layer-service[leveloffset=2] -include::layer-dataaccess[leveloffset=2] - -== Guides - -include::guides-key-principles[leveloffset=2] -include::guides-code-generation[leveloffset=2] -include::guides-coding-conventions[leveloffset=2] -include::guides-dependency-injection[leveloffset=2] -include::guides-configuration-module[leveloffset=2] -include::guides-auth-jwt[leveloffset=2] -include::guides-swagger[leveloffset=2] -include::guides-typeorm[leveloffset=2] -include::guides-serializer[leveloffset=2] -include::guides-validation[leveloffset=2] -include::guides-logger[leveloffset=2] -include::guides-mailer[leveloffset=2] -include::guides-eslint-sonarqube-config[leveloffset=2] - -== devon4node applications - -include::samples[leveloffset=2] -include::samples-step-by-step[leveloffset=2] diff --git a/documentation/plantuml/components.puml b/documentation/plantuml/components.puml deleted file mode 100644 index a9520361..00000000 --- a/documentation/plantuml/components.puml +++ /dev/null @@ -1,48 +0,0 @@ -@startuml components -agent client as cli - -rectangle middleware [ - global middleware - ---- - module middleware -] - -rectangle guard [ - global guard - ---- - controller guard - ---- - handler guard -] - -rectangle interceptor [ - global interceptor - ---- - controller interceptor - ---- - handler interceptor -] - -rectangle pipe [ - global pipe - ---- - handler pipe -] - -agent "handler function" <> as handler -agent "provider" as provider - -cli ..> middleware -middleware ..> cli -middleware ..> guard -guard ..> interceptor -note left on link : intercept the request -interceptor ..> pipe -pipe ..> handler -handler ..> interceptor -note left on link: intercept the response -interceptor ..> cli -handler ..> provider -provider ..> handler - -@enduml diff --git a/documentation/plantuml/dependency-injection1.puml b/documentation/plantuml/dependency-injection1.puml deleted file mode 100644 index 3761b8eb..00000000 --- a/documentation/plantuml/dependency-injection1.puml +++ /dev/null @@ -1,39 +0,0 @@ -@startuml modules - -title Dependency Injection - -left to right direction -skinparam rectangle { - roundCorner<> 25 -} - -rectangle moduleA <> [ - Module A -] - -rectangle moduleB <> [ - Module B -] - -rectangle moduleC <> [ - Module C -] - -rectangle moduleD <> [ - Module D -] - -rectangle moduleE <> [ - Module E -] - -rectangle moduleF <> [ - Module F -] - -moduleA --> moduleB -moduleA --> moduleE -moduleA --> moduleF -moduleB --> moduleC -moduleB --> moduleD -@enduml diff --git a/documentation/plantuml/dependency-injection2.puml b/documentation/plantuml/dependency-injection2.puml deleted file mode 100644 index d603de37..00000000 --- a/documentation/plantuml/dependency-injection2.puml +++ /dev/null @@ -1,44 +0,0 @@ -@startuml modules - -title Dependency Injection wtih global module - -left to right direction -skinparam rectangle { - roundCorner<> 25 - roundCorner<> 25 -} - -rectangle moduleA <> [ - Module A -] - -rectangle moduleB <> [ - Module B -] - -rectangle moduleC <> [ - Module C -] - -rectangle moduleD <> [ - Module D -] - -rectangle moduleE <> [ - Module E -] - -rectangle moduleF <> [ - Module F -] - -moduleA --> moduleB -moduleA --> moduleE -moduleA --> moduleF -moduleB --> moduleD -moduleA --> moduleC -moduleB --> moduleC -moduleD --> moduleC -moduleE --> moduleC -moduleF --> moduleC -@enduml diff --git a/documentation/plantuml/layers.puml b/documentation/plantuml/layers.puml deleted file mode 100644 index 17d4cec6..00000000 --- a/documentation/plantuml/layers.puml +++ /dev/null @@ -1,25 +0,0 @@ -@startuml layers - -left to right direction - -agent client as cli - -node controller [ - controller layer -] - -node service [ - service layer -] - -node data [ - data access layer -] - -cli ..> controller -controller ..> service -service ..> data -data ..> service -service ..> controller -controller ..> cli -@enduml diff --git a/documentation/plantuml/module.puml b/documentation/plantuml/module.puml deleted file mode 100644 index 1523b09f..00000000 --- a/documentation/plantuml/module.puml +++ /dev/null @@ -1,36 +0,0 @@ -@startuml modules -left to right direction -skinparam rectangle { - roundCorner<> 25 -} - -rectangle app <> [ - AppModule -] - -rectangle core <> [ - CoreModule -] - -rectangle auth <> [ - AuthModule -] - -rectangle user <> [ - UserModule -] - -rectangle booking <> [ - BookingModule -] - -rectangle dish <> [ - DishModule -] - -app --> core -app --> booking -app --> dish -core --> auth -core --> user -@enduml diff --git a/documentation/plantuml/module2.puml b/documentation/plantuml/module2.puml deleted file mode 100644 index a531eedb..00000000 --- a/documentation/plantuml/module2.puml +++ /dev/null @@ -1,40 +0,0 @@ -@startuml module2 -left to right direction -skinparam rectangle { - roundCorner<> 25 -} - -rectangle app <> [ - AppModule -] - -rectangle core <> [ - CoreModule -] - -rectangle auth <> [ - AuthModule -] - -rectangle user <> [ - UserModule -] - -rectangle booking <> [ - BookingModule -] - -rectangle dish <> [ - DishModule -] - -app --> core -app --> booking -app --> dish -core --> auth -core --> user -booking --> core -dish --> core -auth --> core -user --> core -@enduml diff --git a/documentation/samples-step-by-step.asciidoc b/documentation/samples-step-by-step.asciidoc deleted file mode 100644 index aae913e5..00000000 --- a/documentation/samples-step-by-step.asciidoc +++ /dev/null @@ -1,556 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= Create the employee sample application step by step - -Here, you will learn step by step how to create a devon4node application. - -== Prerequisites - -=== Typescript - -In order to follow this guide, you must have a basic knowledge about typescript and its syntax. You can learn Typescript using these resources: - -* link:https://github.com/soyrochus/diggingjsandts/blob/master/Digging%20(in)%20JavaScript%20and%20TypeScript.pdf[Digging (in) Javascript and Typescript] -* link:https://www.typescriptlang.org/docs/handbook/intro.html[Typescript handbook] -* link:https://basarat.gitbook.io/typescript/[Typescript Deep Dive] - -=== Node.js - -Node.js is the runtime used in devon4node. It is required a basic knowledge about Node.js before starting to use devon4node. - -* link:https://nodejs.dev/learn/[Learn Node.js] - -=== NestJS - -devon4node is not a framework, is a set of good practices, modules and code generators to use with NestJS. In order to use devon4node, it requires NestJS basic knowledge. You can learn about NestJS in its link:https://docs.nestjs.com/[official documentation]. - -== Employee application - -The `employee` is an application that allows you to manage your employee's information. With this applacation you will be able to register employees with its details, update existing employees details, get the registered employees and delete exiting employees. - -In this guide, we will create the employee's application step by step, showing you how to create a devon4node application from scratch. - -=== Application requisites - -* A link:guides-configuration-module.asciidoc[configuration module], to manage different environments configuration properly. -* A SQLite in memory database managed by link:https://typeorm.io/[TypeORM]. -* Security: link:https://docs.nestjs.com/security/cors[CORS] + link:https://docs.nestjs.com/security/helmet[security headers]. -* link:https://docs.nestjs.com/openapi/introduction[OpenAPI] (aka Swagger) support following code-fist strategy. -* link:https://docs.nestjs.com/security/authentication#jwt-functionality[Authentication] using JWT. -* CRUD to manage employees. -** Create: only available for registered users. -** Read: available for everyone. -** Update: only available for registered users. -** Delete: only available for admins. -* Employees properties: -** name: mandatory string -** surname: mandatory string -** email: mandatory email string -** address: optional string - -== Create the application - -=== First steps (optional) - -If this is the first time that you are using `NestJS` or `devon4node`, you need some packages installed globally in your machine: - -* `Nest` CLI -+ -[source,bash] ----- -npm i -g @nestjs/cli ----- -+ -This will provide you the `nest` command. -+ -* `devon4node` schematics -+ -[source,bash] ----- -npm i -g @devon4node/schematics ----- -+ -This will provide you the `devon4node` code generators. - -`Yarn` is our preferred package manager. However, in this case we have used `npm` because the folder where npm global packages are installed is added to the PATH by default, while yarn's is not. You can use `yarn global add` if you add the yarn's bin folder to the PATH manually (use `yarn global bin` to get the bin folder path) - -=== Create the application skeleton - -To create a `devon4node` application, you must run the `nest new` command. If you use it without any extra parameter you will create a `NestJS` application. In order to generate a `devon4node` application, you need to run the following command: - -[source,bash] ----- -nest new -c @devon4node/schematics employee ----- - -When asked for package manager, you must choose `yarn`. - -The output should be something like: - -[source] ----- -❯ nest new -c @devon4node/schematics employee -⚡ We will scaffold your app in a few seconds.. - -CREATE employee/.prettierrc (228 bytes) -CREATE employee/README.md (3340 bytes) -CREATE employee/nest-cli.json (121 bytes) -CREATE employee/package.json (2243 bytes) -CREATE employee/tsconfig.build.json (97 bytes) -CREATE employee/tsconfig.json (657 bytes) -CREATE employee/src/main.ts (836 bytes) -CREATE employee/test/app.e2e-spec.ts (630 bytes) -CREATE employee/test/jest-e2e.json (183 bytes) -CREATE employee/src/app/app.controller.spec.ts (617 bytes) -CREATE employee/src/app/app.controller.ts (274 bytes) -CREATE employee/src/app/app.module.ts (308 bytes) -CREATE employee/src/app/app.service.ts (142 bytes) -CREATE employee/.eslintrc.js (859 bytes) -CREATE employee/.husky/pre-commit (88 bytes) -CREATE employee/.vscode/extensions.json (63 bytes) -CREATE employee/.vscode/settings.json (150 bytes) -CREATE employee/src/app/core/core.module.ts (401 bytes) -CREATE employee/src/app/shared/exceptions/entity-not-found.exception.ts (419 bytes) -CREATE employee/src/app/shared/filters/entity-not-found.filter.ts (711 bytes) -CREATE employee/src/app/shared/logger/winston.logger.ts (2665 bytes) - -? Which package manager would you ❤️ to use? yarn ----- - -Now you have a clean base application to start building. To do that, you can choose the pieces that you need, some provided by `devon4node` link:guides-code-generation.asciidoc[code generators], others not, but you must always follow the `devon4node` link:guides-key-principles.asciidoc[key principles] and link:guides-coding-conventions.asciidoc[coding conventions]. - -=== Configuration Module - -There are many options to manage the application configuration. `NestJS` provides a link:https://docs.nestjs.com/techniques/configuration[module], but out prefered way is to use our own link:guides-configuration-module.asciidoc[configuration module]. The fastest way to include the configuration module into your project is using the following command: - -[source] ----- -nest g config-module ----- - -The output: - -[source] ----- -❯ nest g config-module -CREATE src/app/shared/model/config/config.model.ts (720 bytes) -CREATE src/config/default.ts (391 bytes) -CREATE src/config/develop.ts (209 bytes) -CREATE src/config/production.ts (210 bytes) -CREATE src/config/test.ts (366 bytes) -CREATE src/config/uat.ts (210 bytes) -UPDATE package.json (2323 bytes) -UPDATE src/app/shared/logger/winston.logger.ts (3179 bytes) -UPDATE src/app/core/core.module.ts (593 bytes) -UPDATE src/main.ts (1074 bytes) -✔ Packages installed successfully. ----- - -=== Database - -In the `NodeJS` ecosystem there are many alternatives to connect to the database. You can use the drivers directly or use a ORM, but our recommendation is to use `TypeORM`. - -`devon4node` provides a generator to integrate into your project link:https://typeorm.io/[TypeORM]. You only need to execute the command: - -[source,bash] ----- -nest g typeorm ----- - -Then, you must select the database when asked. In this example we will use `SQLite`. - -The output: - -[source] ----- -❯ nest g typeorm -? What kind of database do you want to use? sqlite -CREATE docker-compose.yml (25 bytes) -CREATE ormconfig.json (467 bytes) -CREATE src/app/shared/model/entities/base-entity.entity.ts (484 bytes) -UPDATE package.json (2415 bytes) -UPDATE src/app/core/core.module.ts (863 bytes) -UPDATE src/app/shared/model/config/config.model.ts (863 bytes) -UPDATE src/config/default.ts (409 bytes) -UPDATE src/config/develop.ts (573 bytes) -UPDATE src/config/production.ts (574 bytes) -UPDATE src/config/test.ts (730 bytes) -UPDATE src/config/uat.ts (574 bytes) -✔ Packages installed successfully. ----- - -Now, you have your project already configured to work with `TypeORM`. - -=== Security - -To secure your application against common attacks, you only have to execute the following command: - -[source] ----- -nest g security ----- - -The output: - -[source] ----- -❯ nest g security -UPDATE package.json (2439 bytes) -UPDATE src/main.ts (1277 bytes) -✔ Packages installed successfully. ----- - -This installs in your application link:https://github.com/helmetjs/helmet[helmet], and enables link:https://docs.nestjs.com/security/helmet[helmet] and link:https://docs.nestjs.com/security/cors[CORS] - -=== OpenAPI - -To generate `OpenAPI` (aka `swagger`) documentation based in your code, you only need to execute: - -[source] ----- -nest g swagger ----- - -The output: - -[source] ----- -❯ nest g swagger -UPDATE package.json (2508 bytes) -UPDATE src/app/shared/model/config/config.model.ts (1162 bytes) -UPDATE src/config/default.ts (547 bytes) -UPDATE src/config/develop.ts (711 bytes) -UPDATE src/config/test.ts (868 bytes) -UPDATE src/main.ts (1876 bytes) -UPDATE nest-cli.json (196 bytes) -UPDATE src/app/shared/model/entities/base-entity.entity.ts (598 bytes) -✔ Packages installed successfully. ----- - -=== Authentication - -To know all the details about `authenticaiton` in a `devon4node` (or `NestJS`) application, you can follow this link:https://docs.nestjs.com/security/authentication[guide]. - -In order to improve the productivity, `devon4node` has a code generator to add into your project everything described in that link:https://docs.nestjs.com/security/authentication#jwt-functionality[guide]: - -[source] ----- -nest g auth-jwt ----- - -The output should something similar to: - -[source] ----- -❯ nest g auth-jwt -CREATE src/app/core/auth/auth.module.ts (850 bytes) -CREATE src/app/core/auth/controllers/auth.controller.spec.ts (3037 bytes) -CREATE src/app/core/auth/controllers/auth.controller.ts (1311 bytes) -CREATE src/app/core/auth/decorators/get-user.decorator.ts (331 bytes) -CREATE src/app/core/auth/decorators/roles.decorator.spec.ts (869 bytes) -CREATE src/app/core/auth/decorators/roles.decorator.ts (163 bytes) -CREATE src/app/core/auth/guards/roles.guard.spec.ts (2277 bytes) -CREATE src/app/core/auth/guards/roles.guard.ts (708 bytes) -CREATE src/app/core/auth/model/login.dto.ts (360 bytes) -CREATE src/app/core/auth/model/roles.enum.ts (39 bytes) -CREATE src/app/core/auth/model/user-request.interface.ts (133 bytes) -CREATE src/app/core/auth/services/auth.service.spec.ts (3373 bytes) -CREATE src/app/core/auth/services/auth.service.ts (1263 bytes) -CREATE src/app/core/auth/strategies/jwt.strategy.spec.ts (690 bytes) -CREATE src/app/core/auth/strategies/jwt.strategy.ts (669 bytes) -CREATE src/app/core/user/user.module.ts (359 bytes) -CREATE src/app/core/user/model/dto/create-user.dto.ts (325 bytes) -CREATE src/app/core/user/model/dto/user-payload.dto.ts (181 bytes) -CREATE src/app/core/user/model/entities/user.entity.ts (542 bytes) -CREATE src/app/core/user/services/user.service.spec.ts (2083 bytes) -CREATE src/app/core/user/services/user.service.ts (1088 bytes) -CREATE test/auth/auth.service.mock.ts (913 bytes) -CREATE test/user/user.repository.mock.ts (1024 bytes) -UPDATE package.json (2779 bytes) -UPDATE src/app/core/core.module.ts (1017 bytes) -UPDATE src/app/shared/model/config/config.model.ts (1279 bytes) -UPDATE src/config/default.ts (617 bytes) -UPDATE src/config/develop.ts (781 bytes) -UPDATE src/config/production.ts (644 bytes) -UPDATE src/config/test.ts (938 bytes) -UPDATE src/config/uat.ts (644 bytes) -✔ Packages installed successfully. ----- - -=== Create the employee CRUD - -Generate a CRUD into your project is fast and easy, your only need to execute the following command: - -[source] ----- -nest g resource employees ----- - -The output should something similar to: - -[source] ----- -❯ nest g resource employees -? What transport layer do you use? REST API -? Would you like to generate CRUD entry points? Yes -? Which ORM are you using? (No efect if you are not generating the CRUD) TypeORM -CREATE src/app/employees/employees.module.ts (458 bytes) -CREATE src/app/employees/controllers/employees.controller.spec.ts (616 bytes) -CREATE src/app/employees/controllers/employees.controller.ts (1224 bytes) -CREATE src/app/employees/services/employees.service.spec.ts (481 bytes) -CREATE src/app/employees/services/employees.service.ts (1999 bytes) -CREATE src/app/employees/model/dtos/create-employee.dto.ts (34 bytes) -CREATE src/app/employees/model/dtos/update-employee.dto.ts (180 bytes) -CREATE src/app/employees/model/entities/employee.entity.ts (169 bytes) -UPDATE src/app/app.module.ts (389 bytes) -UPDATE package.json (2817 bytes) -UPDATE test/app.e2e-spec.ts (609 bytes) -✔ Packages installed successfully. ----- - -This generate the entity, DTOs, controllers and services to expose the CRUD for an empty `entity`, so now we need to customize it to add the properties, secure the endpoints as described before and improve the default generated OpenAPI documentation. - -. Fill the link:https://typeorm.io/entities[entity] (`src/app/employees/model/entities/employee.entity.ts`) with the properties and ORM decorators. -+ -.`src/app/employees/model/entities/employee.entity.ts` -[source,typescript] ----- -@Entity() -export class Employee extends BaseEntity { - @Column({ length: 255, nullable: false }) - name!: string; - - @Column({ length: 255, nullable: false }) - surname!: string; - - @Column({ length: 255, nullable: false }) - email!: string; - - @Column({ length: 255, nullable: true }) - address?: string; -} ----- -+ -. Fill the `DTOs` with the properties, the validators and the transformations. -+ -.`src/app/employees/model/dtos/create-employee.dto.ts` -[source,typescript] ----- -export class CreateEmployeeDto { - @IsDefined() - @IsString() - @MaxLength(255) - @Expose() - name!: string; - - @IsDefined() - @IsString() - @MaxLength(255) - @Expose() - surname!: string; - - @IsDefined() - @IsString() - @IsEmail() - @MaxLength(255) - @Expose() - email!: string; - - @IsOptional() - @IsString() - @MaxLength(255) - @Expose() - address?: string; -} ----- -+ -The `@Expose()` decorator is important, if you forget it the link:https://docs.nestjs.com/pipes#transformation-use-case[transformation pipe] will ignore it. It works in that way in order to ignore extra fields that are not defined in the DTO. -+ -. secure endpoints: -+ -In order to require authentication to your enpoints, you only need to add the `AuthGuard` guard as shown below: -+ -.`src/app/employees/controllers/employees.controller.ts` -[source,typescript] ----- -@Post() -@UseGuards(AuthGuard()) -create(@Body() createEmployeeDto: CreateEmployeeDto): Promise { - return this.service.create(createEmployeeDto); -} ----- -+ -Now, you need to provide a valid `Bearer JWT token` in order to use the create endpoint. Also, to require a specific role to use an endpoint, you need to use the provided `RolesGuard` and specify the allowed roles by using the `@Roles` decorator: -+ -.`src/app/employees/controllers/employees.controller.ts` -[source,typescript] ----- -@Delete(':id') -@UseGuards(AuthGuard(), RolesGuard) -@Roles(roles.ADMIN) -remove(@Param('id') id: string): Promise { - return this.service.remove(+id); -} ----- -+ -. Add swagger metadata -+ -By default, devon4node installs the link:https://docs.nestjs.com/openapi/cli-plugin[`@nestjs/swagger` plugin]. With that plugin, most of the `OpenAPI` documentation will be generated automatically, but you can provide more information with decorators: -+ -.`src/app/employees/controllers/employees.controller.ts` -[source,typescript] ----- -@Controller('employees') -@ApiTags('employees') -export class EmployeesController { - ... -} ----- -+ -Also, you can add to the `OpenAPI` documentation the secured endpoints: -+ -.`src/app/employees/controllers/employees.controller.ts` -[source,typescript] ----- -@ApiBearerAuth() ----- -+ -As the `findAll` returns a complex type, the plugin can't infer the return type properly. To document properly the return type of the `findAll` you can use the `@nestjs/swagger` decorators: -+ -.`src/app/employees/controllers/employees.controller.ts` -[source,typescript] ----- -@Get() -@ApiQuery({ name: 'pageNumber', type: String, required: false }) -@ApiQuery({ name: 'pageSize', type: String, required: false }) -@ApiOkResponse({ - schema: { - oneOf: [ - { type: 'array', items: { $ref: getSchemaPath(Employee) } }, - { - type: 'object', - properties: { - content: { type: 'array', items: { $ref: getSchemaPath(Employee) } }, - page: { - type: 'object', - properties: { pageNumber: { type: 'number' }, total: { type: 'number' }, pageSize: { type: 'number' } }, - }, - }, - }, - ], - }, -}) -findAll(@GetPage() page?: PageRequest): Promise> { - return this.service.findAll(page); -} ----- -+ -. Generate database migrations -+ -As you're building a new application you also need to create the database tables. `TypeORM` provides a mechanism to generate the database migrations based on your entities: -+ -[source] ----- -yarn build -npx typeorm migration:generate -n CreateTables ----- -+ -The output will be something similar to: -+ -.`src/migration/1655725262254-CreateTables.ts` -[source,typescript] ----- -export class CreateTables1655725262254 implements MigrationInterface { - name = 'CreateTables1655725262254'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "employee" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "version" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "name" varchar(255) NOT NULL, "surname" varchar(255) NOT NULL, "email" varchar(255) NOT NULL, "address" varchar(255))`, - ); - await queryRunner.query( - `CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "version" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "username" varchar(255) NOT NULL, "password" varchar(255) NOT NULL, "role" integer NOT NULL DEFAULT (0))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "user"`); - await queryRunner.query(`DROP TABLE "employee"`); - } -} ----- -+ -The number in the name is a timestamp, so may change in your application. -+ -Now, you can populate the database with the initial data: -+ -[source] ----- -npx typeorm migration:create -n InsertData ----- -+ -and fill in with the following code: -+ -.`src/migration/1655725316517-CreateTables.ts` -[source,typescript] ----- -export class InsertData1655725316517 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `INSERT INTO EMPLOYEE(id, name, surname, email) VALUES(1, 'Santiago', 'Fowler', 'Santiago.Fowler@example.com');`, - ); - await queryRunner.query( - `INSERT INTO EMPLOYEE(id, name, surname, email) VALUES(2, 'Clinton', 'Thornton', 'Clinton.Thornton@example.com');`, - ); - await queryRunner.query( - `INSERT INTO EMPLOYEE(id, name, surname, email) VALUES(3, 'Lisa', 'Rodriquez', 'Lisa.Rodriquez@example.com');`, - ); - await queryRunner.query( - `INSERT INTO EMPLOYEE(id, name, surname, email) VALUES(4, 'Calvin', 'Becker', 'Calvin.Becker@example.com');`, - ); - await queryRunner.query(`INSERT INTO USER(id, username, password, role) VALUES(?, ?, ?, ?);`, [ - 1, - 'user', - await hash('password', await genSalt(12)), - roles.USER, - ]); - await queryRunner.query(`INSERT INTO USER(id, username, password, role) VALUES(?, ?, ?, ?);`, [ - 2, - 'admin', - await hash('admin', await genSalt(12)), - roles.ADMIN, - ]); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM EMPLOYEE`); - await queryRunner.query(`DELETE FROM USER`); - } -} ----- -+ -. Start the application: `yarn start:dev` -+ -image::images/sample/start-app.png[] -+ -. Check the swagger endpoint: `http://localhost:3000/v1/api` -+ -image::images/sample/swagger.png[] -+ -. Make petitions to the employee CRUD: `http://localhost:3000/v1/employees` -+ -image::images/sample/employees.png[] diff --git a/documentation/samples.asciidoc b/documentation/samples.asciidoc deleted file mode 100644 index 0da861dd..00000000 --- a/documentation/samples.asciidoc +++ /dev/null @@ -1,91 +0,0 @@ -:toc: macro - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -toc::[] -:idprefix: -:idseparator: - -:reproducible: -:source-highlighter: rouge -:listing-caption: Listing - -= devon4node Samples - -In the folder /samples, you can find some devon4node examples that could be useful for you in order to understand better the framework. - -The samples are: - -* link:https://github.com/devonfw-sample/devon4node-samples/tree/develop/employee[Employee] -* link:https://github.com/devonfw-sample/devon4node-samples/tree/develop/components-example[Components example] - -Also, we have another realistic example in the link:https://github.com/devonfw/my-thai-star/tree/develop/node[My Thai Star repository]. This example is the implementation of My Thai Star backend, which is compatible with the frontend made with Angular. To do that, this node implementation exposes the same API as Java backend. Take care with this example, as we need to follow the Java API, some components do not follow the devon4node patterns and code conventions. - -== Todo example - -This example is the backend part of an TO-DO application. It exposes and API where you can create, read, update and delete a TO-DO list. - -In order to start the application, run the following commands in the todo folder: - -[source,bash] ----- -$ yarn -$ yarn build -$ yarn start ----- - -Now, you can access to the application using the URL `http://localhost:3000/v1/todo/todos`. If you want to now all endpoints exposed, you can see the swagger at: `http://localhost:3000/v1/api`. - -Also, in this example we show you how to control the access to you application by implementing an authentication mechanism using JWT and rol based strategy. In order to access to the list of todos (`http://localhost:3000/v1/todo/todos`), first you need to call to `POST http://localhost:3000/v1/auth/login` and in the body you need to send the user information: - ----- -{ - "username": "user", - "password": "password" -} ----- - -It will return a JWT token for the user `user`. The rol of this user is USER, so you can only access to the methods GET, POST and DELETE of the endpoint `http://localhost:3000/v1/todo/todos`. If you login with the user admin/admin, you will be able to access to the methods UPDATE and PATCH. - -== Employee example - -This is an example of employee management application. With the application you can create, read, update and delete employees. - -In order to start the application, run the following commands in the todo folder: - -[source,bash] ----- -$ yarn -$ yarn build -$ yarn start ----- - -Now, you can access to the application using the URL `http://localhost:8081/v1/employee/employees`. If you want to now all endpoints exposed, you can see the swagger at: `http://localhost:8081/v1/api`. - -This is a simple example without authentication. With this example you can learn how to work with database migrations. You can find them in the folder /src/migrations. The TypeORM is configured in order to execute the migrations every time that you start this application at ormconfig.json with the following flag: -[source] ----- -"migrationsRun": true ----- - -You can also execute the migration manually by typing the command `devon4node db migration:run`, or revert executing `devon4node db migration:revert`. Take into account that the database that this application is using is an in-memory sqlite, so every time that you stop the application all data is lost. - -== Components example - -This example allow you to understand better the execution order of the components of a devon4node application (guards, pipes, interceptors, filters, middleware). - -In order to start the application, run the following commands in the todo folder: - -[source,bash] ----- -$ yarn -$ yarn build -$ yarn start ----- - -In order to see the execution order, you can call to `http://localhost:3000/v1`. It will show you the execution order of all components except the filters. If you want to know the execution order while a filter is applied, call to the endpoint with the following queries: `?hello=error`, `?hello=controller`, `?hello=global`.