Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Atualmente, a API REST se tornou um padrão para desenvolvimento de aplicações web, permitindo que o desenvolvimento seja dividido em partes independentes. Várias estruturas populares, como Angular, React, Vue e outras, são usadas atualmente para UI. Os desenvolvedores de back-end podem escolher entre uma ampla variedade de linguagens e estruturas. Hoje eu gostaria de falar sobre uma estrutura como NestJSGenericName. Nós estamos em TestMace Nós o usamos ativamente para projetos internos. Usando ninho e pacote @nestjsx/crud, criaremos um aplicativo CRUD simples.

Por que NestJS

Recentemente, muitas estruturas de back-end apareceram na comunidade JavaScript. E se em termos de funcionalidade eles fornecem recursos semelhantes ao Nest, então em uma coisa ele definitivamente vence - esta é a arquitetura. Os seguintes recursos do NestJS permitem criar aplicações industriais e dimensionar o desenvolvimento para grandes equipes:

  • usando TypeScript como principal linguagem de desenvolvimento. Embora NestJS suporte JavaScript, algumas funcionalidades podem não funcionar, especialmente se estivermos falando de pacotes de terceiros;
  • a presença de um contêiner DI, que permite criar componentes fracamente acoplados;
  • A funcionalidade da própria estrutura é dividida em componentes independentes e intercambiáveis. Por exemplo, sob o capô como estrutura, pode ser usado como expressoE fastificar, para trabalhar com o banco de dados, o aninhamento pronto para uso fornece ligações para tipo, mangusto, sequenciar;
  • NestJS é independente de plataforma e oferece suporte a REST, GraphQL, Websockets, gRPC, etc.

A estrutura em si é inspirada na estrutura de frontend Angular e conceitualmente tem muito em comum com ela.

Instalando NestJS e implantando o projeto

Nest contém um pacote ninho/cli, que permite implantar rapidamente uma estrutura de aplicativo básica. Vamos instalar este pacote globalmente:

npm install --global @nest/cli

Após a instalação, iremos gerar o framework básico da nossa aplicação com o nome ninho-rest. Isso é feito usando o comando nest new nest-rest.

ninho novo ninho-descanso

dmitrii@dmitrii-HP-ZBook-17-G3:~/projects $ nest new nest-rest
  We will scaffold your app in a few seconds..

CREATE /nest-rest/.prettierrc (51 bytes)
CREATE /nest-rest/README.md (3370 bytes)
CREATE /nest-rest/nest-cli.json (84 bytes)
CREATE /nest-rest/nodemon-debug.json (163 bytes)
CREATE /nest-rest/nodemon.json (67 bytes)
CREATE /nest-rest/package.json (1805 bytes)
CREATE /nest-rest/tsconfig.build.json (97 bytes)
CREATE /nest-rest/tsconfig.json (325 bytes)
CREATE /nest-rest/tslint.json (426 bytes)
CREATE /nest-rest/src/app.controller.spec.ts (617 bytes)
CREATE /nest-rest/src/app.controller.ts (274 bytes)
CREATE /nest-rest/src/app.module.ts (249 bytes)
CREATE /nest-rest/src/app.service.ts (142 bytes)
CREATE /nest-rest/src/main.ts (208 bytes)
CREATE /nest-rest/test/app.e2e-spec.ts (561 bytes)
CREATE /nest-rest/test/jest-e2e.json (183 bytes)

? Which package manager would you ️ to use? yarn
 Installation in progress... 

  Successfully created project nest-rest
  Get started with the following commands:

$ cd nest-rest
$ yarn run start

                          Thanks for installing Nest 
                 Please consider donating to our open collective
                        to help us maintain this package.

                 Donate: https://opencollective.com/nest

Escolheremos o fio como nosso gerenciador de pacotes.
Neste ponto você pode iniciar o servidor com o comando npm start e indo para o endereço http://localhost:3000 você pode ver a página principal. Contudo, não é por isso que nos reunimos aqui e seguimos em frente.

Configurando o trabalho com o banco de dados

Escolhi PostrgreSQL como SGBD para este artigo. Não há disputa de gostos, na minha opinião este é o SGBD mais maduro, com todas as capacidades necessárias. Como já mencionado, o Nest oferece integração com diversos pacotes para trabalhar com bancos de dados. Porque Como minha escolha recaiu sobre o PostgreSQL, seria lógico escolher TypeORM como ORM. Vamos instalar os pacotes necessários para integração com o banco de dados:

yarn add typeorm @nestjs/typeorm pg

Em ordem, para que serve cada pacote:

  1. typeorm - um pacote diretamente do próprio ORM;
  2. @nestjs/typeorm - pacote TypeORM para NestJS. Adiciona módulos para importação para módulos de projeto, bem como um conjunto de decoradores auxiliares;
  3. pg - driver para trabalhar com PostgreSQL.

Ok, os pacotes estão instalados, agora você precisa iniciar o próprio banco de dados. Para implantar o banco de dados, usarei docker-compose.yml com o seguinte conteúdo:

docker-compose.yml

version: '3.1'

services:
  db:
    image: postgres:11.2
    restart: always
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - ../db:/var/lib/postgresql/data
      - ./postgresql.conf:/etc/postgresql/postgresql.conf
    ports:
      - 5432:5432
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

Como você pode ver, este arquivo configura o lançamento de 2 containers:

  1. db é um contêiner que contém diretamente o banco de dados. No nosso caso, o postgresql versão 11.2 é usado;
  2. administrador – gerenciador de banco de dados. Fornece uma interface web para visualizar e gerenciar o banco de dados.

Para trabalhar com conexões TCP, adicionei a seguinte configuração.

postgresql.conf

# -----------------------------
# PostgreSQL configuration file
# -----------------------------
#
# This file consists of lines of the form:
#
#   name = value
#
# (The "=" is optional.)  Whitespace may be used.  Comments are introduced with
# "#" anywhere on a line.  The complete list of parameter names and allowed
# values can be found in the PostgreSQL documentation.
#
# The commented-out settings shown in this file represent the default values.
# Re-commenting a setting is NOT sufficient to revert it to the default value;
# you need to reload the server.
#
# This file is read on server startup and when the server receives a SIGHUP
# signal.  If you edit the file on a running system, you have to SIGHUP the
# server for the changes to take effect, run "pg_ctl reload", or execute
# "SELECT pg_reload_conf()".  Some parameters, which are marked below,
# require a server shutdown and restart to take effect.
#
# Any parameter can also be given as a command-line option to the server, e.g.,
# "postgres -c log_connections=on".  Some parameters can be changed at run time
# with the "SET" SQL command.
#
# Memory units:  kB = kilobytes        Time units:  ms  = milliseconds
#                MB = megabytes                     s   = seconds
#                GB = gigabytes                     min = minutes
#                TB = terabytes                     h   = hours
#                                                   d   = days
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
# The default values of these variables are driven from the -D command-line
# option or PGDATA environment variable, represented here as ConfigDir.
#data_directory = 'ConfigDir'       # use data in another directory
# (change requires restart)
#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
# (change requires restart)
#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
# (change requires restart)
# If external_pid_file is not explicitly set, no extra PID file is written.
#external_pid_file = ''         # write an extra PID file
# (change requires restart)
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*'
#listen_addresses = 'localhost'     # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
#port = 5432                # (change requires restart)
#max_connections = 100          # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/tmp'   # comma-separated list of directories
# (change requires restart)
#unix_socket_group = ''         # (change requires restart)
#unix_socket_permissions = 0777     # begin with 0 to use octal notation
# (change requires restart)
#bonjour = off              # advertise server via Bonjour
# (change requires restart)
#bonjour_name = ''          # defaults to the computer name
# (change requires restart)
# - TCP Keepalives -
# see "man 7 tcp" for details
#tcp_keepalives_idle = 0        # TCP_KEEPIDLE, in seconds;
# 0 selects the system default
#tcp_keepalives_interval = 0        # TCP_KEEPINTVL, in seconds;
# 0 selects the system default
#tcp_keepalives_count = 0       # TCP_KEEPCNT;
# 0 selects the system default
# - Authentication -
#authentication_timeout = 1min      # 1s-600s
#password_encryption = md5      # md5 or scram-sha-256
#db_user_namespace = off
# GSSAPI using Kerberos
#krb_server_keyfile = ''
#krb_caseins_users = off
# - SSL -
#ssl = off
#ssl_ca_file = ''
#ssl_cert_file = 'server.crt'
#ssl_crl_file = ''
#ssl_key_file = 'server.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
# - Memory -
#shared_buffers = 32MB          # min 128kB
# (change requires restart)
#huge_pages = try           # on, off, or try
# (change requires restart)
#temp_buffers = 8MB         # min 800kB
#max_prepared_transactions = 0      # zero disables the feature
# (change requires restart)
# Caution: it is not advisable to set max_prepared_transactions nonzero unless
# you actively intend to use prepared transactions.
#work_mem = 4MB             # min 64kB
#maintenance_work_mem = 64MB        # min 1MB
#autovacuum_work_mem = -1       # min 1MB, or -1 to use maintenance_work_mem
#max_stack_depth = 2MB          # min 100kB
#shared_memory_type = mmap      # the default is the first option
# supported by the operating system:
#   mmap
#   sysv
#   windows
# (change requires restart)
#dynamic_shared_memory_type = posix # the default is the first option
# supported by the operating system:
#   posix
#   sysv
#   windows
#   mmap
# (change requires restart)
# - Disk -
#temp_file_limit = -1           # limits per-process temp file space
# in kB, or -1 for no limit
# - Kernel Resources -
#max_files_per_process = 1000       # min 25
# (change requires restart)
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0          # 0-100 milliseconds (0 disables)
#vacuum_cost_page_hit = 1       # 0-10000 credits
#vacuum_cost_page_miss = 10     # 0-10000 credits
#vacuum_cost_page_dirty = 20        # 0-10000 credits
#vacuum_cost_limit = 200        # 1-10000 credits
# - Background Writer -
#bgwriter_delay = 200ms         # 10-10000ms between rounds
#bgwriter_lru_maxpages = 100        # max buffers written/round, 0 disables
#bgwriter_lru_multiplier = 2.0      # 0-10.0 multiplier on buffers scanned/round
#bgwriter_flush_after = 0       # measured in pages, 0 disables
# - Asynchronous Behavior -
#effective_io_concurrency = 1       # 1-1000; 0 disables prefetching
#max_worker_processes = 8       # (change requires restart)
#max_parallel_maintenance_workers = 2   # taken from max_parallel_workers
#max_parallel_workers_per_gather = 2    # taken from max_parallel_workers
#parallel_leader_participation = on
#max_parallel_workers = 8       # maximum number of max_worker_processes that
# can be used in parallel operations
#old_snapshot_threshold = -1        # 1min-60d; -1 disables; 0 is immediate
# (change requires restart)
#backend_flush_after = 0        # measured in pages, 0 disables
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------
# - Settings -
#wal_level = replica            # minimal, replica, or logical
# (change requires restart)
#fsync = on             # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
#synchronous_commit = on        # synchronization level;
# off, local, remote_write, remote_apply, or on
#wal_sync_method = fsync        # the default is the first option
# supported by the operating system:
#   open_datasync
#   fdatasync (default on Linux)
#   fsync
#   fsync_writethrough
#   open_sync
#full_page_writes = on          # recover from partial page writes
#wal_compression = off          # enable compression of full-page writes
#wal_log_hints = off            # also do full page writes of non-critical updates
# (change requires restart)
#wal_buffers = -1           # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
#wal_writer_delay = 200ms       # 1-10000 milliseconds
#wal_writer_flush_after = 1MB       # measured in pages, 0 disables
#commit_delay = 0           # range 0-100000, in microseconds
#commit_siblings = 5            # range 1-1000
# - Checkpoints -
#checkpoint_timeout = 5min      # range 30s-1d
#max_wal_size = 1GB
#min_wal_size = 80MB
#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 0     # measured in pages, 0 disables
#checkpoint_warning = 30s       # 0 disables
# - Archiving -
#archive_mode = off     # enables archiving; off, on, or always
# (change requires restart)
#archive_command = ''       # command to use to archive a logfile segment
# placeholders: %p = path of file to archive
#               %f = file name only
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
#archive_timeout = 0        # force a logfile segment switch after this
# number of seconds; 0 disables
# - Archive Recovery -
# These are only used in recovery mode.
#restore_command = ''       # command to use to restore an archived logfile segment
# placeholders: %p = path of file to restore
#               %f = file name only
# e.g. 'cp /mnt/server/archivedir/%f %p'
# (change requires restart)
#archive_cleanup_command = ''   # command to execute at every restartpoint
#recovery_end_command = ''  # command to execute at completion of recovery
# - Recovery Target -
# Set these only when performing a targeted recovery.
#recovery_target = ''       # 'immediate' to end recovery as soon as a
# consistent state is reached
# (change requires restart)
#recovery_target_name = ''  # the named restore point to which recovery will proceed
# (change requires restart)
#recovery_target_time = ''  # the time stamp up to which recovery will proceed
# (change requires restart)
#recovery_target_xid = ''   # the transaction ID up to which recovery will proceed
# (change requires restart)
#recovery_target_lsn = ''   # the WAL LSN up to which recovery will proceed
# (change requires restart)
#recovery_target_inclusive = on # Specifies whether to stop:
# just after the specified recovery target (on)
# just before the recovery target (off)
# (change requires restart)
#recovery_target_timeline = 'latest'    # 'current', 'latest', or timeline ID
# (change requires restart)
#recovery_target_action = 'pause'   # 'pause', 'promote', 'shutdown'
# (change requires restart)
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Sending Servers -
# Set these on the master and on any standby that will send replication data.
#max_wal_senders = 10       # max number of walsender processes
# (change requires restart)
#wal_keep_segments = 0      # in logfile segments; 0 disables
#wal_sender_timeout = 60s   # in milliseconds; 0 disables
#max_replication_slots = 10 # max number of replication slots
# (change requires restart)
#track_commit_timestamp = off   # collect timestamp of transaction commit
# (change requires restart)
# - Master Server -
# These settings are ignored on a standby server.
#synchronous_standby_names = '' # standby servers that provide sync rep
# method to choose sync standbys, number of sync standbys,
# and comma-separated list of application_name
# from standby(s); '*' = all
#vacuum_defer_cleanup_age = 0   # number of xacts by which cleanup is delayed
# - Standby Servers -
# These settings are ignored on a master server.
#primary_conninfo = ''          # connection string to sending server
# (change requires restart)
#primary_slot_name = ''         # replication slot on sending server
# (change requires restart)
#promote_trigger_file = ''      # file name whose presence ends recovery
#hot_standby = on           # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s    # max delay before canceling queries
# when reading WAL from archive;
# -1 allows indefinite delay
#max_standby_streaming_delay = 30s  # max delay before canceling queries
# when reading streaming WAL;
# -1 allows indefinite delay
#wal_receiver_status_interval = 10s # send replies at least this often
# 0 disables
#hot_standby_feedback = off     # send info from standby to prevent
# query conflicts
#wal_receiver_timeout = 60s     # time that receiver waits for
# communication from master
# in milliseconds; 0 disables
#wal_retrieve_retry_interval = 5s   # time to wait before retrying to
# retrieve WAL after a failed attempt
#recovery_min_apply_delay = 0       # minimum delay for applying changes during recovery
# - Subscribers -
# These settings are ignored on a publisher.
#max_logical_replication_workers = 4    # taken from max_worker_processes
# (change requires restart)
#max_sync_workers_per_subscription = 2  # taken from max_logical_replication_workers
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
# - Planner Method Configuration -
#enable_bitmapscan = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_indexscan = on
#enable_indexonlyscan = on
#enable_material = on
#enable_mergejoin = on
#enable_nestloop = on
#enable_parallel_append = on
#enable_seqscan = on
#enable_sort = on
#enable_tidscan = on
#enable_partitionwise_join = off
#enable_partitionwise_aggregate = off
#enable_parallel_hash = on
#enable_partition_pruning = on
# - Planner Cost Constants -
#seq_page_cost = 1.0            # measured on an arbitrary scale
#random_page_cost = 4.0         # same scale as above
#cpu_tuple_cost = 0.01          # same scale as above
#cpu_index_tuple_cost = 0.005       # same scale as above
#cpu_operator_cost = 0.0025     # same scale as above
#parallel_tuple_cost = 0.1      # same scale as above
#parallel_setup_cost = 1000.0   # same scale as above
#jit_above_cost = 100000        # perform JIT compilation if available
# and query more expensive than this;
# -1 disables
#jit_inline_above_cost = 500000     # inline small functions if query is
# more expensive than this; -1 disables
#jit_optimize_above_cost = 500000   # use expensive JIT optimizations if
# query is more expensive than this;
# -1 disables
#min_parallel_table_scan_size = 8MB
#min_parallel_index_scan_size = 512kB
#effective_cache_size = 4GB
# - Genetic Query Optimizer -
#geqo = on
#geqo_threshold = 12
#geqo_effort = 5            # range 1-10
#geqo_pool_size = 0         # selects default based on effort
#geqo_generations = 0           # selects default based on effort
#geqo_selection_bias = 2.0      # range 1.5-2.0
#geqo_seed = 0.0            # range 0.0-1.0
# - Other Planner Options -
#default_statistics_target = 100    # range 1-10000
#constraint_exclusion = partition   # on, off, or partition
#cursor_tuple_fraction = 0.1        # range 0.0-1.0
#from_collapse_limit = 8
#join_collapse_limit = 8        # 1 disables collapsing of explicit
# JOIN clauses
#force_parallel_mode = off
#jit = on               # allow JIT compilation
#plan_cache_mode = auto         # auto, force_generic_plan or
# force_custom_plan
#------------------------------------------------------------------------------
# REPORTING AND LOGGING
#------------------------------------------------------------------------------
# - Where to Log -
#log_destination = 'stderr'     # Valid values are combinations of
# stderr, csvlog, syslog, and eventlog,
# depending on platform.  csvlog
# requires logging_collector to be on.
# This is used when logging to stderr:
#logging_collector = off        # Enable capturing of stderr and csvlog
# into log files. Required to be on for
# csvlogs.
# (change requires restart)
# These are only used if logging_collector is on:
#log_directory = 'log'          # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'    # log file name pattern,
# can include strftime() escapes
#log_file_mode = 0600           # creation mode for log files,
# begin with 0 to use octal notation
#log_truncate_on_rotation = off     # If on, an existing log file with the
# same name as the new log file will be
# truncated rather than appended to.
# But such truncation only occurs on
# time-driven rotation, not on restarts
# or size-driven rotation.  Default is
# off, meaning append to existing files
# in all cases.
#log_rotation_age = 1d          # Automatic rotation of logfiles will
# happen after that time.  0 disables.
#log_rotation_size = 10MB       # Automatic rotation of logfiles will
# happen after that much log output.
# 0 disables.
# These are relevant when logging to syslog:
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
#syslog_sequence_numbers = on
#syslog_split_messages = on
# This is only relevant when logging to eventlog (win32):
# (change requires restart)
#event_source = 'PostgreSQL'
# - When to Log -
#log_min_messages = warning     # values in order of decreasing detail:
#   debug5
#   debug4
#   debug3
#   debug2
#   debug1
#   info
#   notice
#   warning
#   error
#   log
#   fatal
#   panic
#log_min_error_statement = error    # values in order of decreasing detail:
#   debug5
#   debug4
#   debug3
#   debug2
#   debug1
#   info
#   notice
#   warning
#   error
#   log
#   fatal
#   panic (effectively off)
#log_min_duration_statement = -1    # logs statements and their durations
# according to log_statement_sample_rate. -1 is disabled,
# 0 logs all statement, > 0 logs only statements running at
# least this number of milliseconds.
#log_statement_sample_rate = 1  # Fraction of logged statements over
# log_min_duration_statement. 1.0 logs all statements,
# 0 never logs.
# - What to Log -
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_checkpoints = off
#log_connections = off
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default      # terse, default, or verbose messages
#log_hostname = off
#log_line_prefix = '%m [%p] '       # special values:
#   %a = application name
#   %u = user name
#   %d = database name
#   %r = remote host and port
#   %h = remote host
#   %p = process ID
#   %t = timestamp without milliseconds
#   %m = timestamp with milliseconds
#   %n = timestamp with milliseconds (as a Unix epoch)
#   %i = command tag
#   %e = SQL state
#   %c = session ID
#   %l = session line number
#   %s = session start timestamp
#   %v = virtual transaction ID
#   %x = transaction ID (0 if none)
#   %q = stop here in non-session
#        processes
#   %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off           # log lock waits >= deadlock_timeout
#log_statement = 'none'         # none, ddl, mod, all
#log_replication_commands = off
#log_temp_files = -1            # log temporary files equal or larger
# than the specified size in kilobytes;
# -1 disables, 0 logs all temp files
#log_timezone = 'GMT'
#------------------------------------------------------------------------------
# PROCESS TITLE
#------------------------------------------------------------------------------
#cluster_name = ''          # added to process titles if nonempty
# (change requires restart)
#update_process_title = on
#------------------------------------------------------------------------------
# STATISTICS
#------------------------------------------------------------------------------
# - Query and Index Statistics Collector -
#track_activities = on
#track_counts = on
#track_io_timing = off
#track_functions = none         # none, pl, all
#track_activity_query_size = 1024   # (change requires restart)
#stats_temp_directory = 'pg_stat_tmp'
# - Monitoring -
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
#log_statement_stats = off
#------------------------------------------------------------------------------
# AUTOVACUUM
#------------------------------------------------------------------------------
#autovacuum = on            # Enable autovacuum subprocess?  'on'
# requires track_counts to also be on.
#log_autovacuum_min_duration = -1   # -1 disables, 0 logs all actions and
# their durations, > 0 logs only
# actions running at least this number
# of milliseconds.
#autovacuum_max_workers = 3     # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min      # time between autovacuum runs
#autovacuum_vacuum_threshold = 50   # min number of row updates before
# vacuum
#autovacuum_analyze_threshold = 50  # min number of row updates before
# analyze
#autovacuum_vacuum_scale_factor = 0.2   # fraction of table size before vacuum
#autovacuum_analyze_scale_factor = 0.1  # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000  # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_multixact_freeze_max_age = 400000000    # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1  # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
# - Statement Behavior -
#client_min_messages = notice       # values in order of decreasing detail:
#   debug5
#   debug4
#   debug3
#   debug2
#   debug1
#   log
#   notice
#   warning
#   error
#search_path = '"$user", public'    # schema names
#row_security = on
#default_tablespace = ''        # a tablespace name, '' uses the default
#temp_tablespaces = ''          # a list of tablespace names, '' uses
# only default tablespace
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'
#statement_timeout = 0          # in milliseconds, 0 is disabled
#lock_timeout = 0           # in milliseconds, 0 is disabled
#idle_in_transaction_session_timeout = 0    # in milliseconds, 0 is disabled
#vacuum_freeze_min_age = 50000000
#vacuum_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_cleanup_index_scale_factor = 0.1    # fraction of total number of tuples
# before index cleanup, 0 always performs
# index cleanup
#bytea_output = 'hex'           # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_fuzzy_search_limit = 0
#gin_pending_list_limit = 4MB
# - Locale and Formatting -
#datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
#timezone = 'GMT'
#timezone_abbreviations = 'Default'     # Select the set of available time zone
# abbreviations.  Currently, there are
#   Default
#   Australia (historical usage)
#   India
# You can create your own file in
# share/timezonesets/.
#extra_float_digits = 1         # min -15, max 3; any value >0 actually
# selects precise output mode
#client_encoding = sql_ascii        # actually, defaults to database
# encoding
# These settings are initialized by initdb, but they can be changed.
#lc_messages = 'C'          # locale for system error message
# strings
#lc_monetary = 'C'          # locale for monetary formatting
#lc_numeric = 'C'           # locale for number formatting
#lc_time = 'C'              # locale for time formatting
# default configuration for text search
#default_text_search_config = 'pg_catalog.simple'
# - Shared Library Preloading -
#shared_preload_libraries = ''  # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
#jit_provider = 'llvmjit'       # JIT library to use
# - Other Defaults -
#dynamic_library_path = '$libdir'
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
#max_locks_per_transaction = 64     # min 10
# (change requires restart)
#max_pred_locks_per_transaction = 64    # min 10
# (change requires restart)
#max_pred_locks_per_relation = -2   # negative values mean
# (max_pred_locks_per_transaction
#  / -max_pred_locks_per_relation) - 1
#max_pred_locks_per_page = 2            # min 0
#------------------------------------------------------------------------------
# VERSION AND PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
# - Previous PostgreSQL Versions -
#array_nulls = on
#backslash_quote = safe_encoding    # on, off, or safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
#operator_precedence_warning = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
# - Other Platforms and Clients -
#transform_null_equals = off
#------------------------------------------------------------------------------
# ERROR HANDLING
#------------------------------------------------------------------------------
#exit_on_error = off            # terminate session on any error?
#restart_after_crash = on       # reinitialize after backend crash?
#data_sync_retry = off          # retry or panic on failure to fsync
# data?
# (change requires restart)
#------------------------------------------------------------------------------
# CONFIG FILE INCLUDES
#------------------------------------------------------------------------------
# These options allow settings to be loaded from files other than the
# default postgresql.conf.
#include_dir = 'conf.d'         # include files ending in '.conf' from
# directory 'conf.d'
#include_if_exists = 'exists.conf'  # include file only if it exists
#include = 'special.conf'       # include file
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# Add settings for extensions here

Isso é tudo, você pode iniciar contêineres com o comando docker-compose up -d. Ou em um console separado com o comando docker-compose up.

Então, os pacotes estão instalados, o banco de dados foi lançado, só falta torná-los amigos. Para fazer isso, você precisa adicionar o seguinte arquivo à raiz do projeto: ormconfig.js:

ormconfig.js

const process = require('process');
const username = process.env.POSTGRES_USER || "postgres";
const password = process.env.POSTGRES_PASSWORD || "example";
module.exports = {
"type": "postgres",
"host": "localhost",
"port": 5432,
username,
password,
"database": "postgres",
"synchronize": true,
"dropSchema": false,
"logging": true,
"entities": [__dirname + "/src/**/*.entity.ts", __dirname + "/dist/**/*.entity.js"],
"migrations": ["migrations/**/*.ts"],
"subscribers": ["subscriber/**/*.ts", "dist/subscriber/**/.js"],
"cli": {
"entitiesDir": "src",
"migrationsDir": "migrations",
"subscribersDir": "subscriber"
}
}

Esta configuração será usada para cli typeorm.

Vejamos essa configuração com mais detalhes. Nas linhas 3 e 4 obtemos o nome de usuário e a senha das variáveis ​​de ambiente. Isso é conveniente quando você tem vários ambientes (dev, stage, prod, etc). Por padrão, o nome de usuário é postgres e a senha é exemplo. O resto da configuração é trivial, então focaremos apenas nos parâmetros mais interessantes:

  • sincronizar - Indica se o esquema do banco de dados deve ser criado automaticamente quando o aplicativo for iniciado. Tenha cuidado com esta opção e não a utilize em produção, caso contrário você perderá dados. Esta opção é conveniente ao desenvolver e depurar um aplicativo. Como alternativa a esta opção, você pode usar o comando schema:sync da CLI TypeORM.
  • dropSchema - redefine o esquema sempre que uma conexão é estabelecida. Assim como a anterior, esta opção deve ser utilizada apenas durante o desenvolvimento e depuração da aplicação.
  • entidades - quais caminhos procurar para descrições de modelos. Observe que a pesquisa por máscara é suportada.
  • cli.entitiesDir é o diretório onde os modelos criados a partir da CLI TypeORM devem ser armazenados por padrão.

Para que possamos usar todos os recursos do TypeORM em nosso aplicativo Nest, precisamos importar o módulo TypeOrmModule в AppModule. Aqueles. seu AppModule ficará assim:

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import * as process from "process";
const username = process.env.POSTGRES_USER || 'postgres';
const password = process.env.POSTGRES_PASSWORD || 'example';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username,
password,
database: 'postgres',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Como você deve ter notado, o método forRoot a mesma configuração para trabalhar com o banco de dados é transferida como no arquivo ormconfig.ts

O toque final permanece - adicione várias tarefas para trabalhar com TypeORM em package.json. O fato é que a CLI é escrita em javascript e roda no ambiente nodejs. No entanto, todos os nossos modelos e migrações serão escritos em texto datilografado. Portanto, é necessário transpilar nossas migrações e modelos antes de utilizar a CLI. Para isso precisamos do pacote ts-node:

yarn add -D ts-node

Depois disso, adicione os comandos necessários ao package.json:

"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"migration:generate": "yarn run typeorm migration:generate -n",
"migration:create": "yarn run typeorm migration:create -n",
"migration:run": "yarn run typeorm migration:run"

O primeiro comando, typeorm, adiciona um wrapper ts-node para executar o TypeORM cli. Os comandos restantes são atalhos convenientes que você, como desenvolvedor, usará quase todos os dias:
migration:generate — criando migrações com base em mudanças em seus modelos.
migration:create — criando uma migração vazia.
migration:run — lançamento de migrações.
Bom, agora é isso, adicionamos os pacotes necessários, configuramos a aplicação para funcionar com o banco de dados tanto do CLI quanto da própria aplicação, e também lançamos o SGBD. É hora de adicionar lógica à nossa aplicação.

Instalando pacotes para criação de CRUD

Usando apenas o Nest, você pode criar uma API que permite criar, ler, atualizar e excluir uma entidade. Esta solução será tão flexível quanto possível, mas em alguns casos será redundante. Por exemplo, se você precisar criar um protótipo rapidamente, muitas vezes poderá sacrificar a flexibilidade pela velocidade do desenvolvimento. Muitos frameworks fornecem funcionalidade para geração de CRUD descrevendo o modelo de dados de uma determinada entidade. E a Nest não é exceção! Esta funcionalidade é fornecida pelo pacote @nestjsx/crud. Suas capacidades são muito interessantes:

  • fácil instalação e configuração;
  • Independência do SGBD;
  • linguagem de consulta poderosa com capacidade de filtrar, paginar, classificar, carregar relacionamentos e entidades aninhadas, armazenamento em cache, etc.;
  • pacote para geração de solicitações no front-end;
  • fácil substituição de métodos de controlador;
  • configuração pequena;
  • suporte à documentação swagger.

A funcionalidade está dividida em vários pacotes:

  • @nestjsx/crud - o pacote básico que o decorador fornece Bruto() para geração, configuração e validação de rotas;
  • @nestjsx/pedido-crud — um pacote que fornece um construtor/analisador de consultas para uso no frontend;
  • @nestjsx/crud-typeorm — um pacote para integração com TypeORM, fornecendo o serviço básico TypeOrmCrudService com métodos CRUD para trabalhar com entidades no banco de dados.

Neste tutorial precisaremos de pacotes ninhojsx/crud e ninhojsx/crud-typeorm. Primeiro, vamos colocá-los

yarn add @nestjsx/crud class-transformer class-validator

Pacotes transformador de classe и validador de classe nesta aplicação são necessários para uma descrição declarativa das regras para transformar instâncias de modelo e validar solicitações recebidas, respectivamente. Esses pacotes são do mesmo autor, portanto as interfaces são semelhantes.

Implementação direta de CRUD

Tomaremos uma lista de usuários como modelo de exemplo. Os usuários terão os seguintes campos: id, username, displayName, email. id - campo de incremento automático, email и username - campos exclusivos. É simples! Resta implementar nossa ideia na forma de um aplicativo Nest.
Primeiro você precisa criar um módulo users, que será responsável por trabalhar com os usuários. Vamos usar o cli do NestJS e executar o comando no diretório raiz do nosso projeto nest g module users.

usuários do módulo ninho g

dmitrii@dmitrii-HP-ZBook-17-G3:~/projects/nest-rest git:(master*)$ nest g module users
CREATE /src/users/users.module.ts (82 bytes)
UPDATE /src/app.module.ts (312 bytes)

Neste módulo adicionaremos uma pasta de entidades, onde teremos os modelos deste módulo. Em particular, vamos adicionar aqui o arquivo user.entity.ts com uma descrição do modelo do usuário:

usuário.entidade.ts

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: string;
@Column({unique: true})
email: string;
@Column({unique: true})
username: string;
@Column({nullable: true})
displayName: string;
}

Para que este modelo seja “visto” pela nossa aplicação é necessário no módulo UsersModule importar TypeOrmModule o seguinte conteúdo:

usuários.module.ts

import { Module } from '@nestjs/common';
import { UsersController } from './controllers/users/users.controller';
import { UsersService } from './services/users/users.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Module({
controllers: [UsersController],
providers: [UsersService],
imports: [
TypeOrmModule.forFeature([User])
]
})
export class UsersModule {}

Ou seja, aqui importamos TypeOrmModule, onde como parâmetro do método forFeature Indicamos uma lista de modelos relacionados a este módulo.

Resta criar a entidade correspondente no banco de dados. O mecanismo de migração serve para esses fins. Para criar uma migração baseada em alterações nos modelos, você precisa executar o comando npm run migration:generate -- CreateUserTable:

Título do spoiler

$ npm run migration:generate -- CreateUserTable
Migration /home/dmitrii/projects/nest-rest/migrations/1563346135367-CreateUserTable.ts has been generated successfully.
Done in 1.96s.

Não tivemos que escrever a migração manualmente, tudo aconteceu magicamente. Isso não é um milagre! No entanto, isso não é tudo. Vamos dar uma olhada no arquivo de migração criado:

1563346135367-CreateUserTable.ts

import {MigrationInterface, QueryRunner} from "typeorm";
export class CreateUserTable1563346816726 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "email" character varying NOT NULL, "username" character varying NOT NULL, "displayName" character varying, CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP TABLE "user"`);
}
}

Como você pode ver, não apenas o método para iniciar a migração foi gerado automaticamente, mas também o método para revertê-la. Fantástico!
Resta apenas implementar esta migração. Isso é feito com o seguinte comando:

npm run migration:run.

É isso, agora as alterações do esquema migraram para o banco de dados.
A seguir, criaremos um serviço que será responsável por trabalhar com os usuários e herdá-lo de TypeOrmCrudService. O repositório da entidade de interesse deve ser passado para o parâmetro do construtor pai, no nosso caso User repositório.

usuários.serviço.ts

import { Injectable } from '@nestjs/common';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { User } from '../../entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class UsersService extends TypeOrmCrudService<User>{
constructor(@InjectRepository(User) usersRepository: Repository<User>){
super(usersRepository);
}
}

Precisaremos deste serviço no controlador users. Para criar um controlador, digite no console nest g controller users/controllers/users

usuários/controladores/usuários do controlador nest g

dmitrii@dmitrii-HP-ZBook-17-G3:~/projects/nest-rest git:(master*)$ nest g controller users/controllers/users
CREATE /src/users/controllers/users/users.controller.spec.ts (486 bytes)
CREATE /src/users/controllers/users/users.controller.ts (99 bytes)
UPDATE /src/users/users.module.ts (188 bytes)

Vamos abrir este controlador e editá-lo para adicionar um pouco de magia ninhojsx/crud. Por aula UsersController Vamos adicionar um decorador assim:

@Crud({
model: {
type: User
}
})

Bruto é um decorador que adiciona ao controlador os métodos necessários para trabalhar com o modelo. O tipo de modelo é indicado no campo model.type configurações do decorador.
O segundo passo é implementar a interface CrudController<User>. O código do controlador “montado” é assim:

import { Controller } from '@nestjs/common';
import { Crud, CrudController } from '@nestjsx/crud';
import { User } from '../../entities/user.entity';
import { UsersService } from '../../services/users/users.service';
@Crud({
model: {
type: User
}
})
@Controller('users')
export class UsersController implements CrudController<User>{
constructor(public service: UsersService){}
}

E é tudo! Agora o controlador suporta todo o conjunto de operações com o modelo! Não acredite em mim? Vamos testar nosso aplicativo em ação!

Criando um script de consulta no TestMace

Para testar nosso serviço usaremos um IDE para trabalhar com a API TestMace. Por que TestMace? Comparado com produtos similares, apresenta as seguintes vantagens:

  • trabalho poderoso com variáveis. No momento, existem vários tipos de variáveis, cada uma das quais desempenha uma função específica: variáveis ​​integradas, variáveis ​​dinâmicas, variáveis ​​de ambiente. Cada variável pertence a um nó com suporte ao mecanismo de herança;
  • Crie scripts facilmente sem programação. Isso será discutido abaixo;
  • um formato legível que permite salvar o projeto em sistemas de controle de versão;
  • preenchimento automático, destaque de sintaxe, destaque de valor variável;
  • Suporte à descrição da API com capacidade de importação do Swagger.

Vamos iniciar nosso servidor com o comando npm start e tente acessar a lista de usuários. A lista de usuários, a julgar pela configuração do nosso controlador, pode ser obtida na url localhost:3000/users. Vamos fazer uma solicitação para este URL.
Depois de executar o TestMace você poderá ver uma interface como esta:

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

No canto superior esquerdo está uma árvore de projeto com um nó raiz Projeto. Vamos tentar criar a primeira solicitação para obter uma lista de usuários. Para isso vamos criar Etapa de solicitação nó Isso é feito no menu de contexto do nó Projeto Adicionar nó -> RequestStep.

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

No campo URL, cole localhost:3000/users e execute a solicitação. Receberemos o código 200 com um array vazio no corpo da resposta. É compreensível, ainda não adicionamos ninguém.
Vamos criar um script que incluirá as seguintes etapas:

  1. criando um usuário;
  2. solicitação do id do usuário recém-criado;
  3. exclusão por ID de usuário criado na etapa 1.

Então vamos. Por conveniência, vamos criar um nó como Pasta. Essencialmente, esta é apenas uma pasta na qual salvaremos todo o script. Para criar um nó Pasta, selecione Projeto no menu de contexto do nó Adicionar nó -> Pasta. Vamos chamar o nó verificar-criar. Dentro de um nó verificar-criar Vamos criar nossa primeira solicitação para criar um usuário. Vamos chamar o nó recém-criado criar usuário. Ou seja, no momento a hierarquia de nós ficará assim:

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Vamos para a aba aberta criar usuário nó. Vamos inserir os seguintes parâmetros para a solicitação:

  • Tipo de solicitação - POST
  • URL - localhost:3000/usuários
  • Corpo - JSON com valor {"email": "[email protected]", "displayName": "New user", "username": "user"}

Vamos atender esse pedido. Nosso aplicativo diz que o registro foi criado.

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Bem, vamos verificar esse fato. Para poder operar com o id do usuário criado nas etapas subsequentes, este parâmetro deve ser salvo. O mecanismo é perfeito para isso. variáveis ​​dinâmicas. Vamos usar nosso exemplo para ver como trabalhar com eles. Na guia de resposta analisada, próximo ao nó id no menu de contexto, selecione o item Atribuir à variável. Na caixa de diálogo você deve definir os seguintes parâmetros:

  • Node — em qual dos ancestrais criar uma variável dinâmica. Vamos escolher verificar-criar
  • Nome variável - o nome desta variável. Vamos ligar userId.

Esta é a aparência do processo de criação de uma variável dinâmica:

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Agora, cada vez que esta consulta for executada, o valor da variável dinâmica será atualizado. E porque variáveis ​​dinâmicas suportam o mecanismo de herança hierárquica, variável userId estará disponível em descendentes verificar-criar nó de qualquer nível de aninhamento.
Esta variável será útil para nós na próxima solicitação. Ou seja, solicitaremos o usuário recém-criado. Como filho de um nó verificar-criar vamos criar uma solicitação verificar se existe com parâmetro url igual localhost:3000/users/${$dynamicVar.userId}. Ver projeto ${variable_name} isso está obtendo o valor de uma variável. Porque Temos uma variável dinâmica, então para obtê-la você precisa acessar o objeto $dynamicVar, ou seja, acessando completamente uma variável dinâmica userId vai ficar assim ${$dynamicVar.userId}. Vamos executar a solicitação e verificar se os dados foram solicitados corretamente.
A última etapa que resta é solicitar a exclusão. Precisamos dele não apenas para verificar o funcionamento da exclusão, mas também, por assim dizer, para limpar o banco de dados, porque Os campos de e-mail e nome de usuário são exclusivos. Portanto, no nó check-create criaremos uma solicitação de exclusão de usuário com os seguintes parâmetros

  • Tipo de solicitação - DELETE
  • URL - localhost:3000/users/${$dynamicVar.userId}

Vamos lançar. Nós esperamos. Gostamos do resultado)

Bem, agora podemos executar todo esse script a qualquer momento. Para executar o script você precisa selecionar no menu de contexto verificar-criar item de nó Execute.

Criação rápida de CRUD com nest, @nestjsx/crud e TestMace

Os nós no script serão executados um após o outro
Você pode salvar este script em seu projeto executando Arquivo -> Salvar projeto.

Conclusão

Todos os recursos das ferramentas utilizadas simplesmente não cabiam no formato deste artigo. Quanto ao principal culpado – o pacote ninhojsx/crud - os seguintes tópicos permanecem descobertos:

  • validação customizada e transformação de modelos;
  • linguagem de consulta poderosa e seu uso conveniente na frente;
  • redefinindo e adicionando novos métodos aos controladores brutos;
  • suporte de arrogância;
  • gerenciamento de cache.

No entanto, mesmo o que está descrito no artigo é suficiente para entender que mesmo uma estrutura empresarial como o NestJS possui ferramentas para prototipagem rápida de aplicativos. E um IDE tão legal como TestMace permite que você mantenha um determinado ritmo.

Código fonte deste artigo, junto com o projeto TestMace, disponível no repositório https://github.com/TestMace/nest-rest. Para abrir um projeto TestMace basta fazer isso no aplicativo Arquivo -> Abrir projeto.

Fonte: habr.com

Adicionar um comentário