Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

For øyeblikket har REST API blitt en standard for webapplikasjonsutvikling, slik at utviklingen kan deles inn i uavhengige deler. Ulike populære rammeverk som Angular, React, Vue og andre brukes for tiden for UI. Backend-utviklere kan velge mellom et bredt utvalg av språk og rammer. I dag vil jeg gjerne snakke om et slikt rammeverk som NestJS. Vi er i TestMace Vi bruker det aktivt til interne prosjekter. Bruker rede og pakke @nestjsx/crud, vil vi lage en enkel CRUD-applikasjon.

Hvorfor NestJS

Nylig har ganske mange backend-rammeverk dukket opp i JavaScript-fellesskapet. Og hvis de når det gjelder funksjonalitet gir lignende funksjoner som Nest, så vinner den definitivt på én ting - dette er arkitekturen. Følgende NestJS-funksjoner lar deg lage industrielle applikasjoner og skalere utvikling til store team:

  • bruker TypeScript som hovedutviklingsspråk. Selv om NestJS støtter JavaScript, kan det hende at noen funksjoner ikke fungerer, spesielt hvis vi snakker om tredjepartspakker;
  • tilstedeværelsen av en DI-beholder, som lar deg lage løst koblede komponenter;
  • Funksjonaliteten til selve rammeverket er delt inn i uavhengige utskiftbare komponenter. For eksempel under panseret som rammeverk kan den brukes som ekspressOg faste, for å jobbe med databasen, gir neste ut av boksen bindinger til typeorm, Mongoose, oppfølger;
  • NestJS er plattformagnostisk og støtter REST, GraphQL, Websockets, gRPC, etc.

Selve rammeverket er inspirert av Angular frontend-rammeverket og har konseptuelt mye til felles med det.

Installere NestJS og distribuere prosjektet

Nest inneholder en pakke reir/cli, som lar deg raskt distribuere et grunnleggende applikasjonsrammeverk. La oss installere denne pakken globalt:

npm install --global @nest/cli

Etter installasjonen vil vi generere det grunnleggende rammeverket for applikasjonen vår med navnet nest-rest. Dette gjøres ved å bruke kommandoen nest new nest-rest.

reir ny 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

Vi vil velge garn som vår pakkeforvalter.
På dette tidspunktet kan du starte serveren med kommandoen npm start og går til adressen http://localhost:3000 du kan se hovedsiden. Det er imidlertid ikke derfor vi har samlet oss her, og vi går videre.

Sette opp arbeid med databasen

Jeg valgte PostrgreSQL som DBMS for denne artikkelen. Det er ingen tvist om smak; etter min mening er dette den mest modne DBMS, som har alle nødvendige evner. Som allerede nevnt gir Nest integrasjon med ulike pakker for å jobbe med databaser. Fordi Siden valget mitt falt på PostgreSQL, ville det være logisk å velge TypeORM som en ORM. La oss installere de nødvendige pakkene for integrasjon med databasen:

yarn add typeorm @nestjs/typeorm pg

I rekkefølge, hva hver pakke trengs til:

  1. typeorm - en pakke direkte fra selve ORM;
  2. @nestjs/typeorm - TypeORM-pakke for NestJS. Legger til moduler for import til prosjektmoduler, samt et sett med hjelpedekoratører;
  3. pg - driver for arbeid med PostgreSQL.

Ok, pakkene er installert, nå må du starte selve databasen. For å distribuere databasen vil jeg bruke docker-compose.yml med følgende innhold:

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

Som du kan se, konfigurerer denne filen lanseringen av 2 containere:

  1. db er en beholder som direkte inneholder databasen. I vårt tilfelle brukes postgresql versjon 11.2;
  2. adminer – databasebehandler. Gir et webgrensesnitt for visning og administrasjon av databasen.

For å jobbe med tcp-tilkoblinger la jeg til følgende konfigurasjon.

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

Det er alt, du kan starte containere med kommandoen docker-compose up -d. Eller i en egen konsoll med kommandoen docker-compose up.

Så, pakkene er installert, databasen har blitt lansert, alt som gjenstår er å gjøre dem venner med hverandre. For å gjøre dette må du legge til følgende fil i prosjektroten: 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"
}
}

Denne konfigurasjonen vil bli brukt for cli typeorm.

La oss se på denne konfigurasjonen mer detaljert. På linje 3 og 4 får vi brukernavn og passord fra miljøvariablene. Dette er praktisk når du har flere miljøer (dev, scene, prod, etc). Som standard er brukernavnet postgres og passordet er eksempel. Resten av konfigurasjonen er triviell, så vi vil kun fokusere på de mest interessante parameterne:

  • synchronize - Angir om databaseskjemaet skal opprettes automatisk når applikasjonen starter. Vær forsiktig med dette alternativet og ikke bruk det i produksjon, ellers vil du miste data. Dette alternativet er praktisk når du utvikler og feilsøker en applikasjon. Som et alternativ til dette alternativet kan du bruke kommandoen schema:sync fra CLI TypeORM.
  • dropSchema - tilbakestill skjemaet hver gang en tilkobling opprettes. Akkurat som den forrige, bør dette alternativet kun brukes under utvikling og feilsøking av applikasjonen.
  • enheter - hvilke veier du skal se etter beskrivelser av modeller. Vær oppmerksom på at søk etter maske støttes.
  • cli.entitiesDir er katalogen der modeller opprettet fra TypeORM CLI skal lagres som standard.

For at vi skal kunne bruke alle funksjonene til TypeORM i Nest-applikasjonen vår, må vi importere modulen TypeOrmModule в AppModule. De. din AppModule vil se slik ut:

app.modul.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 {}

Som du kanskje har lagt merke til, metoden forRoot den samme konfigurasjonen for arbeid med databasen overføres som i ormconfig.ts-filen

Siste touch gjenstår - legg til flere oppgaver for å jobbe med TypeORM i package.json. Faktum er at CLI er skrevet i javascript og kjører i nodejs-miljøet. Imidlertid vil alle våre modeller og migreringer være skrevet med maskinskrift. Derfor er det nødvendig å transpilere våre migrasjoner og modeller før du bruker CLI. For dette trenger vi ts-node-pakken:

yarn add -D ts-node

Etter det, legg til de nødvendige kommandoene til 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"

Den første kommandoen, typeorm, legger til en ts-node wrapper for å kjøre TypeORM cli. De resterende kommandoene er praktiske snarveier som du som utvikler vil bruke nesten hver dag:
migration:generate — lage migreringer basert på endringer i modellene dine.
migration:create — opprette en tom migrering.
migration:run — igangsetting av migrasjoner.
Vel, det er det nå, vi har lagt til de nødvendige pakkene, konfigurert applikasjonen til å fungere med databasen både fra cli og fra selve applikasjonen, og også lansert DBMS. Det er på tide å legge til logikk i applikasjonen vår.

Installere pakker for å lage CRUD

Ved å bare bruke Nest kan du opprette en API som lar deg opprette, lese, oppdatere og slette en enhet. Denne løsningen vil være så fleksibel som mulig, men i noen tilfeller vil den være overflødig. For eksempel, hvis du raskt trenger å lage en prototype, kan du ofte ofre fleksibilitet for hastigheten på utviklingen. Mange rammeverk gir funksjonalitet for å generere CRUD ved å beskrive datamodellen til en bestemt enhet. Og Nest er intet unntak! Denne funksjonaliteten leveres av pakken @nestjsx/crud. Dens evner er veldig interessante:

  • enkel installasjon og konfigurasjon;
  • DBMS uavhengighet;
  • kraftig spørringsspråk med muligheten til å filtrere, paginere, sortere, laste inn relasjoner og nestede enheter, hurtigbufring, etc.;
  • pakke for å generere forespørsler på front-end;
  • enkel overstyring av kontrollermetoder;
  • liten konfigurasjon;
  • swagger dokumentasjonsstøtte.

Funksjonaliteten er delt inn i flere pakker:

  • @nestjsx/crud - grunnpakken som dekoratøren gir Grov() for rutegenerering, konfigurasjon og validering;
  • @nestjsx/crud-request — en pakke som gir en spørringsbygger/parser for bruk på frontend-siden;
  • @nestjsx/crud-typeorm — en pakke for integrasjon med TypeORM, som gir den grunnleggende TypeOrmCrudService-tjenesten med CRUD-metoder for å jobbe med enheter i databasen.

I denne opplæringen trenger vi pakker reirjsx/crud og reirjsx/crud-typeorm. Først, la oss legge dem

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

Pakker klasse-transformator и klassevalidator i denne søknaden kreves det for en deklarativ beskrivelse av reglene for henholdsvis transformering av modellforekomster og validering av innkommende forespørsler. Disse pakkene er fra samme forfatter, så grensesnittene er like.

Direkte implementering av CRUD

Vi tar en liste over brukere som eksempelmodell. Brukere vil ha følgende felt: id, username, displayName, email. id - felt for automatisk økning, email и username - unike felt. Det er enkelt! Alt som gjenstår er å implementere ideen vår i form av en Nest-applikasjon.
Først må du lage en modul users, som vil være ansvarlig for å jobbe med brukere. La oss bruke cli fra NestJS og utføre kommandoen i rotkatalogen til prosjektet vårt nest g module users.

nest g-modulbrukere

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)

I denne modulen vil vi legge til en entitetsmappe, der vi vil ha modellene til denne modulen. La oss spesielt legge til filen user.entity.ts med en beskrivelse av brukermodellen:

bruker.enhet.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;
}

For at denne modellen skal bli "sett" av applikasjonen vår, er den nødvendig i modulen UsersModule import TypeOrmModule følgende innhold:

brukere.modul.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 {}

Det vil si, her importerer vi TypeOrmModule, hvor som en metodeparameter forFeature Vi viser en liste over modeller relatert til denne modulen.

Alt som gjenstår er å opprette den tilsvarende enheten i databasen. Migrasjonsmekanismen tjener til disse formålene. For å opprette en migrering basert på endringer i modeller, må du kjøre kommandoen npm run migration:generate -- CreateUserTable:

Spoiler tittel

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

Vi trengte ikke å skrive migreringen manuelt, alt skjedde på magisk vis. Er ikke dette et mirakel! Det er imidlertid ikke alt. La oss ta en titt på den opprettede migrasjonsfilen:

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

Som du kan se, ble ikke bare metoden for å starte migreringen generert automatisk, men også metoden for å rulle tilbake den. Fantastisk!
Alt som gjenstår er å rulle ut denne migrasjonen. Dette gjøres med følgende kommando:

npm run migration:run.

Det er det, nå har skjemaendringene migrert til databasen.
Deretter vil vi lage en tjeneste som skal være ansvarlig for å jobbe med brukere og arve den fra TypeOrmCrudService. Depotet til enheten av interesse må overføres til parameteren til overordnet konstruktør, i vårt tilfelle User oppbevaringssted.

brukere.tjeneste.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);
}
}

Vi trenger denne tjenesten i kontrolleren users. For å lage en kontroller, skriv inn konsollen nest g controller users/controllers/users

nest g kontroller brukere/kontrollere/brukere

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)

La oss åpne denne kontrolleren og redigere den for å legge til litt magi reirjsx/crud. Per klasse UsersController La oss legge til en dekoratør som dette:

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

Grov er en dekoratør som legger til kontrolleren de nødvendige metodene for å jobbe med modellen. Modelltypen er angitt i feltet model.type dekoratørkonfigurasjoner.
Det andre trinnet er å implementere grensesnittet CrudController<User>. "Sammensatt" kontrollerkode ser slik ut:

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

Og det er alt! Nå støtter kontrolleren hele settet med operasjoner med modellen! Tro meg ikke? La oss prøve applikasjonen vår i aksjon!

Opprette et spørreskript i TestMace

For å teste tjenesten vår vil vi bruke en IDE for å jobbe med API TestMace. Hvorfor TestMace? Sammenlignet med lignende produkter har den følgende fordeler:

  • kraftig arbeid med variabler. For øyeblikket er det flere typer variabler, som hver spiller en spesifikk rolle: innebygde variabler, dynamiske variabler, miljøvariabler. Hver variabel tilhører en node med støtte for arvemekanismen;
  • Lag enkelt skript uten programmering. Dette vil bli diskutert nedenfor;
  • et menneskelest format som lar deg lagre prosjektet i versjonskontrollsystemer;
  • autofullføring, syntaksutheving, utheving av variabel verdi;
  • API-beskrivelsesstøtte med muligheten til å importere fra Swagger.

La oss starte serveren vår med kommandoen npm start og prøv å få tilgang til listen over brukere. Listen over brukere, å dømme etter kontrollkonfigurasjonen vår, kan fås fra url localhost:3000/users. La oss sende en forespørsel til denne nettadressen.
Etter å ha kjørt TestMace kan du se et grensesnitt som dette:

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

Øverst til venstre er et prosjekttre med en rotnode Prosjekt. La oss prøve å lage den første forespørselen for å få en liste over brukere. For dette vil vi skape RequestStep node Dette gjøres i kontekstmenyen til prosjektnoden Legg til node -> RequestStep.

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

I URL-feltet limer du inn localhost:3000/users og kjører forespørselen. Vi vil motta kode 200 med en tom array i svarteksten. Det er forståelig, vi har ikke lagt til noen ennå.
La oss lage et skript som inkluderer følgende trinn:

  1. opprette en bruker;
  2. forespørsel om ID-en til den nyopprettede brukeren;
  3. sletting etter bruker-ID opprettet i trinn 1.

Så la oss gå. For enkelhets skyld, la oss lage en node som Folder. I hovedsak er dette bare en mappe der vi lagrer hele skriptet. For å opprette en mappenode, velg Prosjekt fra kontekstmenyen til noden Legg til node -> Mappe. La oss ringe noden sjekk-opprett. Inne i en node sjekk-opprett La oss lage vår første forespørsel om å opprette en bruker. La oss kalle den nyopprettede noden Opprett bruker. Det vil si at nodehierarkiet for øyeblikket vil se slik ut:

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

La oss gå til den åpne fanen Opprett bruker node. La oss angi følgende parametere for forespørselen:

  • Forespørselstype - POST
  • URL - localhost:3000/brukere
  • Body - JSON med verdi {"email": "[email protected]", "displayName": "New user", "username": "user"}

La oss oppfylle denne forespørselen. Vår søknad sier at posten er opprettet.

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

Vel, la oss sjekke dette faktum. For å kunne operere med IDen til den opprettede brukeren i påfølgende trinn, må denne parameteren lagres. Mekanismen er perfekt for dette. dynamiske variabler. La oss bruke vårt eksempel til å se på hvordan du kan jobbe med dem. I den analyserte fanen til svaret, ved siden av id-noden i kontekstmenyen, velg elementet Tilordne til variabel. I dialogboksen må du angi følgende parametere:

  • Node — i hvilken av forfedrene som skal lage en dynamisk variabel. La oss velge sjekk-opprett
  • Variabel navn — navnet på denne variabelen. La oss ringe userId.

Slik ser prosessen med å lage en dynamisk variabel ut:

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

Nå, hver gang denne spørringen utføres, vil verdien til den dynamiske variabelen bli oppdatert. Og fordi dynamiske variabler støtter mekanismen for hierarkisk arv, variabel userId vil være tilgjengelig i etterkommere sjekk-opprett node på et hvilket som helst hekkenivå.
Denne variabelen vil være nyttig for oss i neste forespørsel. Vi vil nemlig be om den nyopprettede brukeren. Som et barn av en node sjekk-opprett vi oppretter en forespørsel sjekk om finnes med parameter url lik localhost:3000/users/${$dynamicVar.userId}. Se design ${variable_name} dette er å få verdien av en variabel. Fordi Vi har en dynamisk variabel, så for å få den må du få tilgang til objektet $dynamicVar, dvs. fullstendig tilgang til en dynamisk variabel userId vil se slik ut ${$dynamicVar.userId}. La oss utføre forespørselen og sørge for at dataene blir forespurt riktig.
Det siste trinnet som gjenstår er å be om sletting. Vi trenger det ikke bare for å sjekke driften av slettingen, men også, så å si, for å rydde opp etter oss i databasen, fordi E-post- og brukernavnfeltene er unike. Så i sjekk-opprett-noden vil vi opprette en slett-brukerforespørsel med følgende parametere

  • Forespørselstype - SLETT
  • url - localhost:3000/users/${$dynamicVar.userId}

La oss lansere. Vi venter. Vi nyter resultatet)

Vel, nå kan vi kjøre hele dette skriptet når som helst. For å kjøre skriptet må du velge fra hurtigmenyen sjekk-opprett nodeelement Kjør.

Rask CRUD-oppretting med nest, @nestjsx/crud og TestMace

Nodene i skriptet vil bli utført etter hverandre
Du kan lagre dette skriptet til prosjektet ditt ved å kjøre Fil -> Lagre prosjekt.

Konklusjon

Alle funksjonene til verktøyene som ble brukt kunne rett og slett ikke passet inn i formatet til denne artikkelen. Når det gjelder hovedskyldigen - pakken reirjsx/crud - følgende emner forblir avdekket:

  • tilpasset validering og transformasjon av modeller;
  • kraftig spørrespråk og praktisk bruk foran;
  • redefinere og legge til nye metoder til crud-kontrollere;
  • swagger støtte;
  • caching administrasjon.

Men selv det som er beskrevet i artikkelen er nok til å forstå at selv et slikt bedriftsrammeverk som NestJS har verktøy for rask applikasjonsprototyping. Og en så kul IDE som TestMace lar deg opprettholde et gitt tempo.

Kildekode for denne artikkelen, sammen med prosjektet TestMace, tilgjengelig i depotet https://github.com/TestMace/nest-rest. For å åpne et prosjekt TestMace bare gjør det i appen Fil -> Åpne prosjekt.

Kilde: www.habr.com

Legg til en kommentar