Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

У цяперашні час REST API стаў стандартам распрацоўкі web-прыкладанняў, дазваляючы разбіць распрацоўку на незалежныя часткі. Для UI на дадзены момант выкарыстоўваюцца розныя папулярныя фрэймворкі тыпу Angular, React, Vue і іншыя. Распрацоўнікі ж backend могуць абраць з вялікай разнастайнасці моў і фрэймворкаў. Сёння я б хацеў пагаварыць аб такім фрэймворку як NestJS. Мы ў TestMace актыўна выкарыстоўваем яго для ўнутраных праектаў. Выкарыстоўваючы nest і пакет @nestjsx/crud, Мы створым простае CRUD прыкладанне.

Чаму NestJS

У апошні час у JavaScript супольнасці з'явілася дастаткова шмат backend фрэймворкаў. І калі ў плане функцыяналу яны падаюць падобныя з Nest магчымасці, то ў адным ён сапраўды выйграе – гэта архітэктура. Наступныя магчымасці NestJS дазваляюць ствараць прамысловыя прыкладанні і маштабаваць распрацоўку на вялікія каманды:

  • выкарыстанне TypeScript у якасці асноўнай мовы распрацоўкі. Хоць NestJS і падтрымлівае JavaScript, частка функцыяналу можа не працаваць, асабліва калі гаворка ідзе аб іншых пакетах;
  • наяўнасць DI кантэйнера, што дазваляе ствараць слабазлучаныя кампаненты;
  • функцыянал самога фрэймворка разбіты на незалежныя ўзаемазаменныя кампаненты. Напрыклад, пад капотам у якасці фрэймворка можа выкарыстоўвацца як экспрэс, так і fastify, для працы з БД nest са скрынкі дае біндынгі да друкарская клавіятура, мангуст, sequelize;
  • NestJS не залежыць ад платформы і падтрымлівае REST, GraphQL, Websockets, gRPC і т.д.

Сам фрэймворк натхнёны frontend фрэймворкам Angular і канцэптуальна мае шмат агульнага з ім.

Ўстаноўка NestJS і разгортванне праекта

Nest змяшчае пакет гняздо/cli, які дазваляе хутка разгарнуць базавы каркас прыкладання. Усталюем глабальна дадзены пакет:

npm install --global @nest/cli

Пасля ўстаноўкі згенеруем базавы каркас нашага прыкладання з імем nest-rest. Робіцца гэта з выкарыстаннем каманды nest new nest-rest.

nest new nest-rest

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

У якасці пакетнага мэнэджара мы абярэм yarn.
На дадзены момант вы можаце запусціць сервер камандай npm start і прайшоўшы па адрасе http://localhost:3000 можаце бачыць галоўную старонку. Аднак мы не для гэтага тут сабраліся і рухаемся далей.

Наладжваем працу з базай

У якасці СКБД для дадзенага артыкула я абраў PostrgreSQL. Аб густах не спрачаюцца, па маім меркаванні, гэта найболей спелая СКБД, якая валодае ўсімі неабходнымі магчымасцямі. Як ужо было сказанае, для працы з базамі дадзеных Nest падае інтэграцыю з рознымі пакетамі. Т.к. мой выбар упаў на PostgreSQL, то лагічна будзе абраць TypeORM у якасці ORM. Усталюем неабходныя пакеты для інтэграцыі з базай дадзеных:

yarn add typeorm @nestjs/typeorm pg

Па парадку, для чаго патрэбен кожны пакет:

  1. typeorm - пакет непасрэдна з самой ORM;
  2. @nestjs/typeorm - TypeORM пакет для NestJS. Дадае модулі для імпартавання ў модулі праекта, а таксама набор дэкаратараў-хелпераў;
  3. pg - драйвер для працы з PostgreSQL.

Окей, пакеты ўсталяваныя, зараз неабходна запусціць саму базу. Для разгортвання базы я буду выкарыстоўваць docker-compose.yml наступнага зместу:

докер-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

Як мага бачыць, дадзены файл канфігуруе запуск 2 кантэйнераў:

  1. db - гэта кантэйнер непасрэдна з базай дадзеных. У нашым выпадку выкарыстоўваецца postgresql версіі 11.2/XNUMX;
  2. adminer - менеджэр працы з базай дадзеных. Дае web-інтэрфейс для прагляду і кіравання базай.

Для працы з падлучэнням па tcp я дадаў канфіг наступнага зместу.

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

На гэтым усё, можна запусціць кантэйнеры камандай docker-compose up -d. Або ў асобнай кансолі камандай docker-compose up.

Такім чынам, пакеты ўсталявалі, базу запусцілі, засталося іх сябар з сябрам пасябраваць. Для гэтага трэба ў корань праекта дадаць файл 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"
}
}

Дадзеная канфігурацыя будзе выкарыстоўвацца для cli typeorm.

Спынімся на дадзенай канфігурацыі падрабязней. У радках 3 і 4 мы атрымліваем імя карыстальніка і пароль са зменных асяроддзя. Гэта зручна, калі ў вас есць некалькі акружэнняў (dev, stage, prod, etc). Па змаўчанні імя карыстальніка postgres, пароль - example. У астатнім канфіг трывіяльны, таму спынімся толькі на самых цікавых параметрах:

  • synchronize - паказвае, ці павінна схема базы дадзеных аўтаматычна стварацца пры запуску прыкладання. Будзьце ўважлівыя з дадзенай опцыяй і не выкарыстоўвайце яе ў production, у адваротным выпадку вы страціце дадзеныя. Дадзеная опцыя зручная пры распрацоўцы і адладцы дадатку. Як альтэрнатыва дадзенай опцыі, вы можаце выкарыстоўваць каманду schema:sync з CLI TypeORM.
  • dropSchema - скідаць схему кожны раз, калі ўсталёўваецца злучэнне. Таксама, як і папярэднюю, дадзеную опцыю варта выкарыстоўваць толькі ў працэсе распрацоўкі і адладкі дадатку.
  • entities - па якіх шляхах шукаць апісанне мадэляў. Звярніце ўвагу, што падтрымліваецца пошук па масцы.
  • cli.entitiesDir - дырэкторыя, куды па змаўчанні павінны складацца мадэлі, створаныя з CLI TypeORM.

Для таго, каб мы маглі выкарыстоўваць усе магчымасці TypeORM у нашым Nest дадатку, неабходна імпартаваць модуль TypeOrmModule в AppModule. Г.зн. ваш AppModule будзе выглядаць наступным чынам:

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 {}

Як вы паспелі заўважыць, у метад forRoot перадаецца тая ж канфігурацыя для працы з базай, што і ў файле ormconfig.ts

Застаўся фінальны рыска - дадаць некалькі цягачаў для працы з TypeORM у package.json. Справа ў тым, што CLI напісана на JavaScript і запускаецца ў асяроддзі nodejs. Аднак усе нашы мадэлі і міграцыі будуць напісаны на typescript. Таму неабходна правесці транспіляцыю нашых міграцый і мадэляў да выкарыстання CLI. Для гэтага нам спатрэбіцца пакет ts-node:

yarn add -D ts-node

Пасля гэтага дадамо неабходныя каманды ў 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"

Першая каманда, typeorm, дадае абгортку ў выглядзе ts-node для запуску cli TypeORM. Астатнія каманды - гэта зручныя скарачэнні, якімі вы як распрацоўшчык будзеце карыстацца практычна кожны дзень:
migration:generate - стварэнне міграцыі на аснове змен у вашых мадэлях.
migration:create - стварэнне пустой міграцыі.
migration:run - запуск міграцый.
Ну зараз сапраўды ўсё, мы дадалі неабходныя пакеты, сканфігуравалі прыкладанне для працы з базай як з cli, так і з самага прыкладання а таксама запусцілі СКБД. Прыйшоў час дадаць логіку ў наша дадатак.

Ўстаноўка пакетаў для стварэння CRUD

Выкарыстоўваючы толькі Nest можна стварыць API, які дазваляе ствараць, чытаць, абнаўляць і выдаляць сутнасць. Такое рашэнне будзе максімальна гнуткім, аднак для некаторых выпадкаў залішнім. Да прыкладу, калі вам трэба хутка стварыць прататып, то часцяком можна ахвяраваць гнуткасцю дзеля хуткасці распрацоўкі. Многія фрэймворкі падаюць функцыянал генерацыі CRUD па апісанні мадэлі дадзеных вызначанай сутнасці. І Nest не выключэнне! Дадзеную функцыянальнасць падае пакет @nestjsx/crud. Магчымасці яго вельмі цікавыя:

  • лёгкая ўстаноўка і настройка;
  • незалежнасць ад СКБД;
  • магутны мову запытаў з магчымасцю фільтрацыі, пагінацыі, сартавання, загрузкі сувязяў і ўкладзеных сутнасцяў, кэшаванне і г.д.;
  • пакет для фармавання запытаў на front-end;
  • лёгкае перавызначэнне метадаў кантролера;
  • невялікі канфіг;
  • падтрымка swagger дакументацыі.

Функцыянальнасць разбіта на некалькі пакетаў:

  • @nestjsx/crud - базавы пакет, які падае дэкаратар Crud() для генерацыі роўтаў, канфігуравання і валідацыі;
  • @nestjsx/crud-request - пакет, які прадстаўляе білдэр/парсер запытаў для выкарыстання на баку frontend;
  • @nestjsx/crud-typeorm - пакет для інтэграцыі з TypeORM, які прадстаўляе базавы сэрвіс TypeOrmCrudService з CRUD метадамі працы з сутнасцямі ў БД.

У дадзеным кіраўніцтве нам спатрэбяцца пакеты гняздоjsx/crud і гняздоjsx/crud-typeorm. Для пачатку, паставім іх

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

пакеты class-transformer и class-validator у дадзеным дадатку патрабуюцца для дэкларатыўнага апісання правіл трансфармавання экзэмпляраў мадэляў і валідацыі ўваходзяць запытаў адпаведна. Гэтыя пакеты ад аднаго аўтара, таму інтэрфейсы падобныя.

Непасрэдная рэалізацыя CRUD

У якасці прыкладу мадэлі мы возьмем спіс карыстальнікаў. У карыстальнікаў будуць наступныя палі: id, username, displayName, email. id - аўтаінкрыментнае поле, email и username - Унікальныя палі. Усё проста! Засталося ўвасобіць нашу задумку ў выглядзе Nest прыкладання.
Для пачатку неабходна стварыць модуль users, які будзе адказваць за працу з карыстальнікамі. Скарыстаемся cli ад NestJS, і ў каранёвай дырэкторыі нашага праекту выканальны каманду nest g module users.

nest g module users

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)

У дадзеным модулі дадамо тэчку entities, дзе ў нас будуць ляжаць мадэлі дадзенага модуля. У прыватнасці, дадамо сюды файл user.entity.ts з апісаннем мадэлі карыстальнікаў:

user.entity.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;
}

Каб дадзеную мадэль "убачыла" наша дадатак, неабходна ў модуль UsersModule імпартаваць TypeOrmModule наступнага зместу:

users.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 {}

Г.зн. тут мы імпартуем TypeOrmModule, дзе ў якасці параметру метаду forFeature паказваем спіс мадэляў, якія адносяцца да дадзенага модуля.

Застаецца стварыць адпаведную сутнасць у базе даных. Для гэтых мэт служыць механізм міграцый. Каб стварыць міграцыю на аснове змен у мадэлях, неабходна выканаць каманду npm run migration:generate -- CreateUserTable:

Загаловак спойлера

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

Нам не прыйшлося пісаць міграцыю ўручную, усё адбылося магічным чынам. Ці гэта не цуд! Аднак і гэта яшчэ не ўсё. Зірнем на створаны файл з міграцыяй:

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"`);
}
}

Як можна заўважыць, быў аўтаматычна згенераваны не толькі метад для запуску міграцыі, але і метад для яе адкату. Фантастыка!
Застаецца толькі накаціць дадзеную міграцыю. Робіцца гэта наступнай камандай:

npm run migration:run.

Усё, зараз змены схемы перавандравалі ў базу дадзеных.
Далей створым сэрвіс, які будзе адказваць за працу з карыстальнікамі і атрымаем у спадчыну яго ад TypeOrmCrudService. У параметр бацькоўскага канструктара неабходна перадаць рэпазітар якая цікавіць сутнасці, у нашым выпадку User рэпазітар.

users.service.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);
}
}

Дадзены сэрвіс нам спатрэбіцца ў кантролеры users. Для стварэння кантролера набярыце ў кансолі nest g controller users/controllers/users

nest g controller users/controllers/users

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)

Адкрыем дадзены кантролер і адрэдагуем, каб дадаць трохі магіі гняздоjsx/crud. На клас UsersController дадамо дэкаратар наступнага выгляду:

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

Crud - Гэта дэкаратар, які дадае ў кантролер неабходныя метады для працы з мадэллю. Тып мадэлі паказваецца ў полі model.type канфігурацыі дэкаратара.
Другі крок - неабходна рэалізаваць інтэрфейс CrudController<User>. "У зборы" код кантролера выглядае наступным чынам:

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){}
}

І гэта ўсё! Цяпер кантролер падтрымлівае ўвесь набор аперацый з мадэллю! Не верыце? Давайце паспрабуем наша дадатак у справе!

Стварэнне сцэнара запытаў у TestMace

Для тэставання нашага сэрвісу мы будзем выкарыстоўваць IDE для працы з API TestMace. Чаму TestMace? У параўнанні з аналагічнымі прадуктамі, ён мае наступныя перавагі:

  • магутная праца са зменнымі. На дадзены момант існуе некалькі відаў зменных, кожны з якіх выконвае пэўную ролю: убудаваныя зменныя, дынамічныя зменныя, зменныя асяроддзі. Кожная зменная прыналежыць які-небудзь вузлу з падтрымкай механізму ўспадкоўвання;
  • лёгкае стварэнне сцэнарыяў без праграмавання. Пра гэта гаворка пойдзе ніжэй;
  • чалавекачытальны фармат, які дазваляе захоўваць праект у сістэмах кантролю версій;
  • аўтадапаўненне, падсвятленне сінтаксісу, падсвятленне значэнняў зменных;
  • падтрымка апісання API з магчымасцю імпарту з Swagger.

Давайце запусцім наш сервер камандай npm start і паспрабуем звярнуцца да спісу карыстальнікаў. Спіс карыстачоў, судзячы па нашай канфігурацыі кантролера, можна атрымаць па url localhost:3000/users. Зробім запыт на дадзены url.
Пасля запуску TestMace вы можаце ўбачыць такі інтэрфейс:

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Злева зверху знаходзіцца дрэва праектаў з каранёвым вузлом. Праект. Давайце паспрабуем стварыць першы запыт на атрыманне спіса карыстальнікаў. Для гэтага створым RequestStep вузел. Робіцца гэта ў кантэкстным меню Project вузла Add node -> RequestStep.

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

У поле URL устаўце localhost:3000/users і выканайце запыт. Атрымаем 200 код з пустым масівам у целе адказу. Яно і зразумела, мы яшчэ нікога не дабаўлялі.
Давайце створым сцэнар які будзе складацца з наступныя крокі:

  1. стварэнне карыстальніка;
  2. запыт па id толькі што створанага карыстальніка;
  3. выдаленне па id карыстальніка, створанага на кроку 1.

Такім чынам, паехалі. Для зручнасці створым вузел тыпу Тэчка. У сутнасці гэта проста тэчка, у якой захаваем увесь сцэнар. Для стварэння Folder вузла неабходна ў кантэкстным меню Project вузла абраць Add node -> Folder. Назавем вузел check-create. Унутры вузла check-create створым наш першы запыт на стварэнне карыстальніка. Назавем ізноў створаны вузел create-user. Гэта значыць, на дадзены момант іерархія вузлоў будзе выглядаць наступным чынам:

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Давайце пяройдзем да ўкладкі адкрытага create-user вузла. Увядзем наступныя параметры для запыту:

  • Тып запыту - POST
  • URL - localhost:3000/users
  • Body - JSON са значэннем {"email": "[email protected]", "displayName": "New user", "username": "user"}

Выканаем дадзены запыт. Наша дадатак кажа, што запіс створана.

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Што ж, давайце праверым гэты факт. Каб у наступных кроках апераваць з id створанага карыстача, дадзены параметр неабходна захаваць. Для гэтага выдатна падыходзіць механізм дынамічных зменных. Давайце на нашым прыкладзе разгледзім, як адбываецца праца з імі. Ва ўкладцы parsed адказу ў вузла id у кантэкстным меню неабходна абраць пункт Assign to variable. У дыялогавым акне неабходна задаць наступныя параметры:

  • вузел - У якім з продкаў ствараць дынамічную зменную. Выберам check-create
  • Імя зменнай - назва гэтай зменнай. Назавем userId.

Вось як выглядае працэс стварэння дынамічнай зменнай:

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Цяпер пры кожным выкананні дадзенага запыту значэнне дынамічнай зменнай будзе абнаўляцца. А т.я. дынамічныя зменныя падтрымліваюць механізм іерархічнага ўспадкоўвання, зменная userId будзе даступная ў нашчадках check-create вузла любога ўзроўню ўкладзенасці.
У наступным запыце дадзеная зменная нам спатрэбіцца. У прыватнасці, мы запытаем ізноў створанага карыстальніка. У якасці нашчадка вузла check-create мы створым запыт check-if exists з параметрам url роўным localhost:3000/users/${$dynamicVar.userId}. Канструкцыя выгляду ${variable_name} гэта атрыманне значэння зменнай. Т.к. у нас дынамічная пераменная, то каб атрымаць яе неабходна звярнуцца да аб'екта $dynamicVar, т.е цалкам зварот да дынамічнай зменнай userId будзе выглядаць наступным чынам ${$dynamicVar.userId}. Выканаем запыт і пераканаемся, што дадзеныя запытваюцца карэктна.
Застаўся апошні штрых - зрабіць запыт на выдаленне. Ён нам патрэбен не толькі для таго, каб праверыць працу выдалення, але і, так бы мовіць, падчысціць за сабой у базе, т.я. палі email і username унікальныя. Такім чынам, у вузле check-create створым запыт delete-user са наступнымі параметрамі

  • Тып запыту - DELETE
  • URL - localhost:3000/users/${$dynamicVar.userId}

Запускаем. Чакаем. Атрымліваем асалоду ад вынікам)

Ну і зараз мы можам у любы момант запусціць цалкам дадзены сцэнар. Каб запусціць сцэнар неабходна абраць у кантэкстным меню check-create вузла пункт прагон.

Хуткае стварэнне CRUD з nest, @nestjsx/crud і TestMace

Вузлы ў сцэнары выканаюцца сябар за сябрам
Дадзены сцэнар вы можаце захаваць да сябе ў праект, выканаўшы File -> Save project.

Заключэнне

У фармат дадзенага артыкула проста не змаглі змясціцца ўсе фішкі скарыстаных прылад. Што тычыцца асноўнага віноўніка - пакета гняздоjsx/crud – неасветленымі засталіся наступныя тэмы:

  • кастамная валідацыя і трансфармацыя мадэлей;
  • магутная мова запытаў і зручнае яе выкарыстанне на фронце;
  • пераазначэнне і даданне новых метадаў у crud-кантролеры;
  • падтрымка swagger;
  • кіраванне кэшаваннем.

Аднак нават апісанага ў артыкуле дастаткова, каб зразумець, што нават такі інтэрпрайсны фрэймворк як NestJS мае ў загашніку прылады для хуткага прататыпавання прыкладанняў. А такая класная IDE як TestMace дазваляе падтрымаць зададзены тэмп.

Зыходны код дадзенага артыкула, разам з праектам TestMace, даступны ў рэпазітары https://github.com/TestMace/nest-rest. Для адкрыцця праекта TestMace дастаткова ў дадатку выканаць File -> Open project.

Крыніца: habr.com

Дадаць каментар