Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Actualment, l'API REST s'ha convertit en un estàndard per al desenvolupament d'aplicacions web, la qual cosa permet dividir el desenvolupament en parts independents. Actualment s'utilitzen diversos marcs populars com Angular, React, Vue i altres per a la interfície d'usuari. Els desenvolupadors de backend poden triar entre una gran varietat d'idiomes i marcs. Avui m'agradaria parlar d'un marc com NestJS. Estem a TestMace El fem servir activament per a projectes interns. Utilitzant niu i paquet @nestjsx/crud, crearem una aplicació CRUD senzilla.

Per què NestJS

Recentment, han aparegut molts marcs de backend a la comunitat JavaScript. I si en termes de funcionalitat ofereixen capacitats similars a Nest, en una cosa definitivament guanya: aquesta és l'arquitectura. Les següents funcions de NestJS us permeten crear aplicacions industrials i escalar el desenvolupament a grans equips:

  • utilitzant TypeScript com a llenguatge de desenvolupament principal. Tot i que NestJS admet JavaScript, és possible que algunes funcionalitats no funcionin, sobretot si parlem de paquets de tercers;
  • la presència d'un contenidor DI, que permet crear components poc acoblats;
  • La funcionalitat del marc en si es divideix en components intercanviables independents. Per exemple, sota el capó com a marc es pot utilitzar com a exprésI accelerar, per treballar amb la base de dades, el niu fora de la caixa proporciona enllaços a tiporm, mangosta, seqüelar;
  • NestJS és independent de la plataforma i admet REST, GraphQL, Websockets, gRPC, etc.

El marc en si s'inspira en el marc frontal Angular i conceptualment té molt en comú amb ell.

Instal·lació de NestJS i desplegament del projecte

Nest conté un paquet niu/cli, que us permet desplegar ràpidament un marc d'aplicació bàsic. Instal·lem aquest paquet globalment:

npm install --global @nest/cli

Després de la instal·lació, generarem el marc bàsic de la nostra aplicació amb el nom niu-rest. Això es fa mitjançant l'ordre nest new nest-rest.

niu nou niu-repòs

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

Escollirem el fil com a gestor de paquets.
En aquest punt, podeu iniciar el servidor amb l'ordre npm start i anar a l'adreça http://localhost:3000 podeu veure la pàgina principal. Tanmateix, no és per això que ens hem reunit aquí i seguim endavant.

Configuració del treball amb la base de dades

Vaig triar PostrgreSQL com a SGBD per a aquest article. No hi ha cap disputa sobre els gustos; al meu entendre, aquest és el SGBD més madur, amb totes les capacitats necessàries. Com ja s'ha esmentat, Nest proporciona integració amb diversos paquets per treballar amb bases de dades. Perquè Com que la meva elecció va recaure en PostgreSQL, seria lògic triar TypeORM com a ORM. Instal·lem els paquets necessaris per a la integració amb la base de dades:

yarn add typeorm @nestjs/typeorm pg

Per ordre, per a què es necessita cada paquet:

  1. typeorm - un paquet directament des del mateix ORM;
  2. @nestjs/typeorm: paquet TypeORM per a NestJS. Afegeix mòduls per importar als mòduls del projecte, així com un conjunt de decoradors auxiliars;
  3. pg - controlador per treballar amb PostgreSQL.

D'acord, els paquets estan instal·lats, ara cal que engegueu la base de dades. Per desplegar la base de dades, utilitzaré docker-compose.yml amb el contingut següent:

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

Com podeu veure, aquest fitxer configura el llançament de 2 contenidors:

  1. db és un contenidor que conté directament la base de dades. En el nostre cas, s'utilitza la versió 11.2 de postgresql;
  2. administrador: gestor de bases de dades. Proporciona una interfície web per visualitzar i gestionar la base de dades.

Per treballar amb connexions tcp, he afegit la configuració següent.

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

Això és tot, podeu iniciar contenidors amb l'ordre docker-compose up -d. O en una consola independent amb l'ordre docker-compose up.

Així doncs, els paquets s'han instal·lat, s'ha posat en marxa la base de dades, només queda fer-los amics entre ells. Per fer-ho, heu d'afegir el fitxer següent a l'arrel del projecte: 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"
}
}

Aquesta configuració s'utilitzarà per a cli typeorm.

Vegem aquesta configuració amb més detall. A les línies 3 i 4 obtenim el nom d'usuari i la contrasenya de les variables d'entorn. Això és convenient quan teniu diversos entorns (dev, stage, prod, etc.). Per defecte, el nom d'usuari és postgres i la contrasenya és un exemple. La resta de la configuració és trivial, així que ens centrarem només en els paràmetres més interessants:

  • synchronize - Indica si l'esquema de la base de dades s'ha de crear automàticament quan s'inicia l'aplicació. Aneu amb compte amb aquesta opció i no l'utilitzeu en producció, en cas contrari perdreu dades. Aquesta opció és convenient quan es desenvolupa i es depura una aplicació. Com a alternativa a aquesta opció, podeu utilitzar l'ordre schema:sync de CLI TypeORM.
  • dropSchema: restableix l'esquema cada vegada que s'estableix una connexió. Igual que l'anterior, aquesta opció només s'ha d'utilitzar durant el desenvolupament i la depuració de l'aplicació.
  • entitats: quins camins cal buscar descripcions de models. Tingueu en compte que s'admet la cerca per màscara.
  • cli.entitiesDir és el directori on s'han d'emmagatzemar per defecte els models creats a partir de la CLI TypeORM.

Per tal que puguem utilitzar totes les funcions de TypeORM a la nostra aplicació Nest, hem d'importar el mòdul TypeOrmModule в AppModule. Aquells. teu AppModule quedarà així:

aplicació.mòdul.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 {}

Com haureu notat, el mètode forRoot es transfereix la mateixa configuració per treballar amb la base de dades que al fitxer ormconfig.ts

El toc final segueix sent: afegiu diverses tasques per treballar amb TypeORM a package.json. El fet és que la CLI està escrita en javascript i s'executa a l'entorn nodejs. Tanmateix, tots els nostres models i migracions estaran escrits a màquina. Per tant, és necessari transpilar les nostres migracions i models abans d'utilitzar la CLI. Per a això necessitem el paquet ts-node:

yarn add -D ts-node

Després d'això, afegiu les ordres necessàries a 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"

La primera ordre, typeorm, afegeix un embolcall ts-node per executar el cli TypeORM. Les ordres restants són dreceres convenients que, com a desenvolupador, utilitzareu gairebé cada dia:
migration:generate — Creació de migracions basades en els canvis dels vostres models.
migration:create — Creant una migració buida.
migration:run - Llançament de migracions.
Bé, ja està, hem afegit els paquets necessaris, hem configurat l'aplicació perquè funcioni amb la base de dades tant des del cli com des de la mateixa aplicació, i també hem llançat el SGBD. És hora d'afegir lògica a la nostra aplicació.

Instal·lació de paquets per crear CRUD

Amb només Nest, podeu crear una API que us permeti crear, llegir, actualitzar i suprimir una entitat. Aquesta solució serà el més flexible possible, però en alguns casos serà redundant. Per exemple, si necessiteu crear ràpidament un prototip, sovint podeu sacrificar la flexibilitat per la velocitat de desenvolupament. Molts marcs ofereixen funcionalitat per generar CRUD descrivint el model de dades d'una determinada entitat. I Nest no és una excepció! Aquesta funcionalitat la proporciona el paquet @nestjsx/crud. Les seves capacitats són molt interessants:

  • fàcil instal·lació i configuració;
  • independència de DBMS;
  • potent llenguatge de consulta amb la capacitat de filtrar, paginar, ordenar, carregar relacions i entitats imbricades, memòria cau, etc.;
  • paquet per generar sol·licituds al front-end;
  • fàcil anul·lació dels mètodes del controlador;
  • petita configuració;
  • Swagger suport de documentació.

La funcionalitat es divideix en diversos paquets:

  • @nestjsx/crud - el paquet bàsic que proporciona el decorador Cruda() per a la generació, configuració i validació de rutes;
  • @nestjsx/crud-request — un paquet que proporciona un creador/analitzador de consultes per utilitzar-lo a la part de la interfície;
  • @nestjsx/crud-typeorm — un paquet per a la integració amb TypeORM, que proporciona el servei bàsic TypeOrmCrudService amb mètodes CRUD per treballar amb entitats de la base de dades.

En aquest tutorial necessitarem paquets niujsx/crud i niujsx/crud-typeorm. Primer, posem-los

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

Paquets transformador de classe и validador de classes en aquesta aplicació es requereix una descripció declarativa de les regles de transformació d'instàncies de model i validació de sol·licituds entrants, respectivament. Aquests paquets són del mateix autor, de manera que les interfícies són similars.

Implementació directa de CRUD

Prenem una llista d'usuaris com a model d'exemple. Els usuaris tindran els camps següents: id, username, displayName, email. id - camp d'increment automàtic, email и username - camps únics. És fàcil! Només queda implementar la nostra idea en forma d'aplicació Nest.
Primer heu de crear un mòdul users, que serà el responsable de treballar amb els usuaris. Utilitzem el cli de NestJS i executem l'ordre al directori arrel del nostre projecte nest g module users.

usuaris del mòdul nest 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)

En aquest mòdul afegirem una carpeta d'entitats, on tindrem els models d'aquest mòdul. En particular, afegim aquí el fitxer user.entity.ts amb una descripció del model d'usuari:

entitat.usuari.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;
}

Perquè aquest model sigui "vist" per la nostra aplicació, és necessari al mòdul UsersModule importació TypeOrmModule el contingut següent:

usuaris.mòdul.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 {}

És a dir, aquí importem TypeOrmModule, on com a paràmetre de mètode forFeature Us indiquem una llista de models relacionats amb aquest mòdul.

Només queda crear l'entitat corresponent a la base de dades. El mecanisme de migració serveix per a aquests propòsits. Per crear una migració basada en canvis en els models, heu d'executar l'ordre npm run migration:generate -- CreateUserTable:

títol de l'spoiler

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

No vam haver d'escriure la migració manualment, tot va passar de manera màgica. No és un miracle! Tanmateix, això no és tot. Fem una ullada al fitxer de migració creat:

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

Com podeu veure, no només es va generar automàticament el mètode per iniciar la migració, sinó també el mètode per revertir-la. Fantàstic!
Només queda desplegar aquesta migració. Això es fa amb la següent comanda:

npm run migration:run.

Això és tot, ara els canvis d'esquema han migrat a la base de dades.
A continuació, crearem un servei que s'encarregarà de treballar amb els usuaris i heretar-lo TypeOrmCrudService. El repositori de l'entitat d'interès s'ha de passar al paràmetre del constructor pare, en el nostre cas User repositori.

usuaris.servei.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);
}
}

Necessitarem aquest servei al controlador users. Per crear un controlador, escriviu a la consola nest g controller users/controllers/users

usuaris/controladors/usuaris del 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)

Obrim aquest controlador i editem-lo per afegir una mica de màgia niujsx/crud. Per classe UsersController Afegim un decorador com aquest:

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

Cruda és un decorador que afegeix al controlador els mètodes necessaris per treballar amb el model. El tipus de model s'indica al camp model.type configuracions del decorador.
El segon pas és implementar la interfície CrudController<User>. El codi del controlador "assemblat" té aquest aspecte:

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

I és tot! Ara el controlador admet tot el conjunt d'operacions amb el model! No em creus? Provem la nostra aplicació en acció!

Creació d'un script de consulta a TestMace

Per provar el nostre servei utilitzarem un IDE per treballar amb l'API TestMace. Per què TestMace? En comparació amb productes similars, té els següents avantatges:

  • treball potent amb variables. Actualment, hi ha diversos tipus de variables, cadascuna de les quals té un paper específic: variables incorporades, variables dinàmiques, variables d'entorn. Cada variable pertany a un node amb suport per al mecanisme d'herència;
  • Creeu scripts fàcilment sense programar. Això es comentarà a continuació;
  • un format llegible per l'home que permet desar el projecte en sistemes de control de versions;
  • autocompleció, ressaltat de sintaxi, ressaltat de valor variable;
  • Compatibilitat amb la descripció de l'API amb la possibilitat d'importar des de Swagger.

Iniciem el nostre servidor amb l'ordre npm start i intenta accedir a la llista d'usuaris. La llista d'usuaris, a jutjar per la configuració del nostre controlador, es pot obtenir a l'URL localhost:3000/users. Fem una sol·licitud a aquesta URL.
Després d'executar TestMace, podeu veure una interfície com aquesta:

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

A la part superior esquerra hi ha un arbre del projecte amb un node arrel projecte. Intentem crear la primera sol·licitud per obtenir una llista d'usuaris. Per a això crearem Sol·licitudPas node Això es fa al menú contextual del node Projecte Afegeix un node -> RequestStep.

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Al camp URL, enganxeu localhost:3000/users i executeu la sol·licitud. Rebrem el codi 200 amb una matriu buida al cos de la resposta. És comprensible, encara no hem afegit ningú.
Creem un script que inclourà els passos següents:

  1. crear un usuari;
  2. sol·licitud de l'identificador de l'usuari acabat de crear;
  3. eliminació per l'identificador d'usuari creat al pas 1.

Així doncs, anem. Per comoditat, creem un node com Carpeta. Essencialment, això és només una carpeta on desarem tot l'script. Per crear un node Carpeta, seleccioneu Projecte al menú contextual del node Afegeix node -> Carpeta. Anem a cridar el node verificar-crear. Dins d'un node verificar-crear Creem la nostra primera sol·licitud per crear un usuari. Anem a cridar el nou node creat crear-usuari. És a dir, de moment, la jerarquia del node tindrà aquest aspecte:

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Anem a la pestanya oberta crear-usuari node. Introduïm els següents paràmetres per a la sol·licitud:

  • Tipus de sol·licitud: POST
  • URL: host local: 3000/usuaris
  • Cos: JSON amb valor {"email": "[email protected]", "displayName": "New user", "username": "user"}

Complim aquesta petició. La nostra aplicació diu que el registre s'ha creat.

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Bé, comprovem aquest fet. Per poder operar amb l'identificador de l'usuari creat en passos posteriors, cal desar aquest paràmetre. El mecanisme és perfecte per a això. variables dinàmiques. Utilitzem el nostre exemple per veure com treballar-hi. A la pestanya analitzada de la resposta, al costat del node d'identificació al menú contextual, seleccioneu l'element Assigna a variable. Al quadre de diàleg heu d'establir els paràmetres següents:

  • Node — en quin dels avantpassats crear una variable dinàmica. Triem verificar-crear
  • Nom de la variable — el nom d'aquesta variable. Truquem userId.

A continuació es mostra el procés de creació d'una variable dinàmica:

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Ara, cada vegada que s'executa aquesta consulta, el valor de la variable dinàmica s'actualitzarà. I perquè les variables dinàmiques donen suport al mecanisme de l'herència jeràrquica, variable userId estarà disponible en descendents verificar-crear node de qualsevol nivell de nidificació.
Aquesta variable ens serà útil en la propera sol·licitud. És a dir, demanarem a l'usuari de nova creació. Com a fill d'un node verificar-crear crearem una sol·licitud comproveu si existeix amb paràmetre url igual localhost:3000/users/${$dynamicVar.userId}. Veure disseny ${variable_name} això és obtenir el valor d'una variable. Perquè Tenim una variable dinàmica, així que per obtenir-la cal accedir a l'objecte $dynamicVar, és a dir, accedir completament a una variable dinàmica userId semblarà així ${$dynamicVar.userId}. Executem la sol·licitud i assegurem-nos que les dades es demanen correctament.
L'últim pas que queda és sol·licitar la supressió. El necessitem no només per comprovar el funcionament de l'eliminació, sinó també, per dir-ho d'alguna manera, per netejar després de nosaltres mateixos a la base de dades, perquè Els camps de correu electrònic i de nom d'usuari són únics. Per tant, al node check-create crearem una sol·licitud d'eliminació d'usuari amb els paràmetres següents

  • Tipus de sol·licitud: SUPRIMIR
  • URL - localhost:3000/users/${$dynamicVar.userId}

Llencem. Nosaltres esperem. Gaudim del resultat)

Bé, ara podem executar tot aquest script en qualsevol moment. Per executar l'script, heu de seleccionar al menú contextual verificar-crear element de node Correr.

Creació ràpida de CRUD amb nest, @nestjsx/crud i TestMace

Els nodes de l'script s'executaran un darrere l'altre
Podeu desar aquest script al vostre projecte executant-lo Fitxer -> Desa el projecte.

Conclusió

Totes les característiques de les eines utilitzades simplement no podrien encaixar en el format d'aquest article. Pel que fa al principal culpable: el paquet niujsx/crud: els temes següents romanen al descobert:

  • validació personalitzada i transformació de models;
  • potent llenguatge de consulta i el seu ús convenient al davant;
  • redefinint i afegint nous mètodes als controladors de crud;
  • suport d'arrossegament;
  • gestió de la memòria cau.

Tanmateix, fins i tot el que es descriu a l'article és suficient per entendre que fins i tot un marc empresarial com NestJS té eines per a la creació ràpida de prototips d'aplicacions. I un IDE tan genial TestMace permet mantenir un ritme determinat.

Codi font d'aquest article, juntament amb el projecte TestMace, disponible al repositori https://github.com/TestMace/nest-rest. Per obrir un projecte TestMace només cal fer-ho a l'aplicació Fitxer -> Obre projecte.

Font: www.habr.com

Afegeix comentari