One place for hosting & domains

      How to List Open Files with lsof


      Updated by Linode

      Contributed by

      Mihalis Tsoukalos

      Introduction

      lsof was created by Victor A. Abell and is a utility that lists open files. As everything in Linux can be considered a file, this means that lsof can gather information on the majority of activity on your Linode, including network interfaces and network connections. lsof by default will output a list of all open files and the processes that opened them.

      The two main drawbacks of lsof are that it can only display information about the local machine (localhost), and that it requires administrative privileges to print all available data. Additionally, you usually do not execute lsof without any command line parameters because it outputs a large amount of data that can be difficult to parse. This happens because lsof will natively list all open files belonging to all active processes – for example, the output of wc(1) (a word count utility) when applied to lsof on a test instance shows the size of the output is extremely large:

      sudo lsof | wc
      
        
          7332   68337 1058393
      
      

      Before You Begin

      Note

      Running lsof without root privileges will only return
      the results available to the current user. If you are not familiar with the sudo command,
      see the Users and Groups guide.

      On most major distributions, lsof will come pre-installed and you can begin using it immediately. If for any reason it is not found, you can install lsof using your preferred package manager.

      Command Line Options

      The lsof(8) binary supports a large number of command line options, including the following:

      Option Description
      -h and -? Both options present a help screen. Please note that you will need to properly escape the ? character for -? to work.
      -a This option tells lsof to logically ADD all provided options.
      -b This option tells lsof to avoid kernel functions that might block the returning of results. This is a very specialized option.
      -l If converting a user ID to a login name is working improperly or slowly, you can disable it using the -l parameter.
      –P The -P option prevents the conversion of port numbers to port names for network files.
      -u list The -u option allows you to define a list of login names or user ID numbers whose files will be returned. The -u option supports the ^ character for excluding the matches from the output.
      -c list The -c option selects the listing of files for processes executing the commands that begin with the characters in the list. This supports regular expressions, and also supports the ^ character for excluding the matches from the output.
      -p list The -p option allows you to select the files for the processes whose process IDs are in the list. The -p option supports the ^ character for excluding the matches from the output.
      -g list The -g option allows you to select the files for the processes whose optional process group IDs are in the list. The -g option supports the ^ character for excluding the matches from the output.
      -s The -s option allows you to select the network protocols and states that interest you. The -s option supports the ^ character for excluding the matches from the output. The correct form is PROCOTCOL:STATE. Possible protocols are UDP and TCP. Some possible TCP states are: CLOSED, SYN-SENT, SYN-RECEIVED, ESTABLISHED, CLOSE-WAIT, LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING, and TIME-WAIT. Possible UDP states are Unbound and Idle.
      +d s The +d option option tells lsof to search for all open instances of directory s and the files and directories it contains at its top level.
      +D directory The +D option tells lsof to search for all open instances of directory directory and all the files and directories it contains to its complete depth.
      -d list The -d option specifies the list of file descriptors to include or exclude from the output. -d 1,^2 means include file descriptor 1 and exclude file descriptor 2.
      -i4 This option is used for displaying IPv4 data only.
      -i6 This option is used for displaying IPv6 data only.
      -i The -i option without any values tells lsof to display network connections only.
      -i ADDRESS The -i option with a value will limit the displayed information to match that value. Some example values are TCP:25 for displaying TCP data that listens to port number 25, @google.com for displaying information related to google.com, :25 for displaying information related to port number 25, :POP3 for displaying information related to the port number that is associated to POP3 in the /etc/services file, etc. You can also combine hostnames and IP Addresses with port numbers and protocols.
      -t The -t option tells lsof to display process identifiers without a header line. This is particularly useful for feeding the output of lsof to the kill(1) command or to a script. Notice that -t automatically selects the -w option.
      -w The -w option disables the suppression of warning messages.
      +w The +w option enables the suppression of warning messages.
      -r TIME The -r option causes the lsof command to repeat every TIME seconds until the command is manually terminated with an interrupt.
      +r TIME The +r command, with the + prefix, acts the same as the -r command, but will exit its loop when it fails to find any open files.
      -n The -n option prevents network numbers from being converted to host names.
      -F CHARACTER The -F command instructs lsof to produce output that is suitable as input for other programs. For a complete explanation, consult the lsof manual entry.

      Note

      By default, the output of lsof will include the output of each one of its command line options,
      like a big logical expression with multiple OR logical operators between all the command line
      options. However, this default behavior can change with the use of the -a option.

      Note

      For the full list of command line options supported by lsof and a more detailed
      explanation of the presented command line options, you should consult its manual page:

      man lsof
      

      Anatomy of lsof Output

      The following command uses the -i option to display all open UDP files/connections:

      sudo lsof -i UDP
      
        
      COMMAND   PID USER  FD    TYPE DEVICE SIZE/OFF NODE NAME
      rpcbind   660  root  6u    IPv4  20296  0t0      UDP  *:sunrpc
      rpcbind   660  root  7u    IPv4  20298  0t0      UDP  *:836
      rpcbind   660  root  9u    IPv6  20300  0t0      UDP  *:sunrpc
      rpcbind   660  root  10u   IPv6  20301  0t0      UDP  *:836
      avahi-dae 669 avahi   12u   IPv4  20732  0t0      UDP  *:mdns
      avahi-dae 669 avahi   13u   IPv6  20733  0t0      UDP  *:mdns
      avahi-dae 669 avahi   14u   IPv4  20734  0t0      UDP  *:54087
      avahi-dae 669 avahi   15u   IPv6  20735  0t0      UDP  *:48582
      rsyslogd  675  root  6u    IPv4  20973  0t0      UDP  li10-121.members.linode.com:syslog
      dhclient  797  root  6u    IPv4  21828  0t0      UDP  *:bootpc
      ntpd      848   ntp   16u   IPv6  22807  0t0      UDP  *:ntp
      ntpd      848   ntp   17u   IPv4  22810  0t0      UDP  *:ntp
      ntpd      848   ntp   18u   IPv4  22814  0t0      UDP  localhost:ntp
      ntpd      848   ntp   19u   IPv4  22816  0t0      UDP  li10-121.members.linode.com:ntp
      ntpd      848   ntp   20u   IPv6  22818  0t0      UDP  localhost:ntp
      ntpd      848   ntp   24u   IPv6  24916  0t0      UDP  [2a01:7e00::f03c:91ff:fe69:1381]:ntp
      ntpd      848   ntp   25u   IPv6  24918  0t0      UDP  [fe80::f03c:91ff:fe69:1381]:ntp
      
      

      The output of lsof has various columns.

      • The COMMAND column contains the first nine
        characters of the name of the UNIX command associated with the process.
      • The PID column
        shows the process ID of the command.
      • The USER column displays the name of the
        user that owns the process.
      • The TID column shows the task ID. A blank TID indicates a
        process. Note that this column will not appear in the output of many lsof commands.
      • The FD column stands for file descriptor. Its values can be cwd, txt, mem, and
        mmap.
      • The TYPE column displays the type of the file: regular file, directory, socket, etc.
      • The DEVICE column contains the device numbers separated by commas.
      • The value of the SIZE/OFF
        column is the size of the file or the file offset in bytes. The value of the NODE column
        is the node number of a local file.
      • Lastly, the NAME column shows the name of the mount point
        and file system where the file is located, or the Internet address.

      The Repeat Mode

      Running lsof with the –r option puts lsof in repeat mode, re-running the command in a loop every few seconds. This mode is useful for monitoring for a process or a connection that might only exist for a short time. The -r command will run forever, so when you are finished you must manually terminate the command.

      The +r option will also put lsof in repeat mode – the difference between -r and +r is that +r will
      automatically terminate lsof when a loop has no new output to print.

      When lsof
      is in repeat mode, it prints new output every t seconds (a loop); the default value
      of t is 15 seconds, which you can change by typing an integer value after -r or +r.

      The following command tells lsof to display all UDP connections every 10 seconds:

      sudo lsof -r 10 -i UDP
      

      Choosing Between IPv4 and IPv6

      lsof lists both IPv4 and IPv6 connections by default, but you can choose the kind
      of connections you want to display. The following command displays IPv4 connections
      only:

      sudo lsof -i4
      

      Therefore, the next command will display all TCP connections of the IPv4 protocol:

      sudo lsof -i4 -a -i TCP
      

      An equivalent command to the above is the following command that uses grep:

      sudo lsof -i4 | grep TCP
      

      On the other hand, the following command will display IPv6 connections only:

      sudo lsof -i6
      

      Therefore, the next command will display all UDP connections of the IPv6 protocol:

      sudo lsof -i6 | grep UDP
      
        
      avahi-dae  669  avahi  13u  IPv6  20733  0t0  UDP  *:mdns
      avahi-dae  669  avahi  15u  IPv6  20735  0t0  UDP  *:48582
      ntpd       848  ntp    16u  IPv6  22807  0t0  UDP  *:ntp
      ntpd       848  ntp    20u  IPv6  22818  0t0  UDP  localhost:ntp
      ntpd       848  ntp    24u  IPv6  24916  0t0  UDP  [2a01:7e00::f03c:91ff:fe69:1381]:ntp
      ntpd       848  ntp    25u  IPv6  24918  0t0  UDP  [fe80::f03c:91ff:fe69:1381]:ntp
      
      

      Logically ADD All Options

      In this section of the guide you will learn how to logically ADD the existing options
      using the -a flag. This provides you enhanced filtering capabilities. Take the following command as an example:

      sudo lsof -Pni -u www-data
      

      The above command would print out all network connections (-i), suppressing network number conversion (-n) and the conversion of port numbers to port names (-P), and it would also print out all files pertaining to the www-data user, without combining the two options into one logical statement.

      The following command combines these two options with the -a logical AND option and finds all open sockets belonging to the www-data user:

      lsof -Pni -a -u www-data
      
        
      COMMAND  PID    USER      FD  TYPE  DEVICE   SIZE/OFF  NODE  NAME
      apache2  6385   www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  6385   www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  6386   www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  6386   www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  6387   www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  6387   www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24567  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24567  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24570  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24570  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24585  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24585  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  25431  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  25431  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  27827  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  27827  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  27828  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  27828  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  27829  www-data  4u  IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  27829  www-data  6u  IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      
      

      Note

      You are allowed to place the -a option wherever you like as lsof will still detect the relevant options.

      Using Regular Expressions

      lsof has support for regular expressions. Regular expressions begin and end with a
      forward slash (/) character. The ^ character denotes the beginning of a line whereas $
      denotes the end of the line. Each dot (.) character represents a single character in
      the output.

      The following lsof command will find all commands that have precisely five characters:

      lsof -c /^.....$/
      
        
      COMMAND  PID  USER  FD   TYPE     DEVICE  SIZE/OFF  NODE  NAME
      netns    18   root  cwd  DIR      8,0     4096      2     /
      netns    18   root  rtd  DIR      8,0     4096      2     /
      netns    18   root  txt  unknown                          /proc/18/exe
      jfsIO    210  root  cwd  DIR      8,0     4096      2     /
      jfsIO    210  root  rtd  DIR      8,0     4096      2     /
      jfsIO    210  root  txt  unknown                          /proc/210/exe
      kstrp    461  root  cwd  DIR      8,0     4096      2     /
      kstrp    461  root  rtd  DIR      8,0     4096      2     /
      kstrp    461  root  txt  unknown                          /proc/461/exe
      
      

      Output For Other Programs

      Using the -F option, lsof generates output that is suitable for processing by scripts
      written in programming languages such as awk, perl and python.

      The following command will display each field of the lsof output in a separate line:

      sudo lsof -n -i4 -a -i TCP:ssh -F
      
        
      p812
      g812
      R1
      csshd
      u0
      Lroot
      f3
      au
      l
      tIPv4
      .
      .
      .
      
      

      Providing various arguments to the -F option allows you to generate less output – notice that the process ID
      and the file descriptor are always printed in the output. As an example, the following command
      will only print the process ID, which is preceded by the p character, the file descriptor, which
      is preceded by the f character, and the protocol name of each entry, which is preceded by
      the P character:

      sudo lsof -n -i4 -a -i TCP:ssh -FP
      
        
      p812
      f3
      PTCP
      p22352
      f3
      PTCP
      p22361
      f3
      PTCP
      
      

      Note

      For the full list of options supported by -F, you should visit the manual page of lsof.

      Additional Examples

      Show All Open TCP Files

      Similar to the aforementioned UDP command, the following command will display all open TCP files/connections:

      sudo lsof -i TCP
      
        
      COMMAND   PID     USER     FD   TYPE  DEVICE  SIZE/OFF NODE NAME
      sshd      812     root     3u   IPv4  23674   0t0      TCP  *:ssh (LISTEN)
      sshd      812     root     4u   IPv6  23686   0t0      TCP  *:ssh (LISTEN)
      mysqld    1003    mysql    17u  IPv4  24217   0t0      TCP  localhost:mysql (LISTEN)
      master    1245    root     13u  IPv4  24480   0t0      TCP  *:smtp (LISTEN)
      sshd      22352   root     3u   IPv4  8613370 0t0      TCP  li10-121.members.linode.com:ssh->ppp-2-8-23-19.home.otenet.gr:60032 (ESTABLISHED)
      sshd      22361   mtsouk   3u   IPv4  8613370      0t0      TCP  li10-121.members.linode.com:ssh->ppp-2-8-23-19.home.otenet.gr:60032 (ESTABLISHED)
      apache2   24565   root     4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24565   root     6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      apache2   24567   www-data 4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24567   www-data 6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      apache2   24568   www-data 4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24568   www-data 6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      apache2   24569   www-data 4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24569   www-data 6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      apache2   24570   www-data 4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24570   www-data 6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      apache2   24571   www-data 4u   IPv6  8626153      0t0      TCP  *:http (LISTEN)
      apache2   24571   www-data 6u   IPv6  8626157      0t0      TCP  *:https (LISTEN)
      
      

      Listing All ESTABLISHED Connections

      Internet Connections

      If you process the output of lsof with some traditional UNIX command line tools, like grep and awk,
      you can list all active network connections:

      sudo lsof -i -n -P | grep ESTABLISHED | awk '{print $1, $9}' | sort -u
      
        
      sshd 109.74.193.253:22->2.86.23.29:60032
      
      

      Note

      The lsof -i -n -P command can be also written as lsof -i -nP or alternatively as
      lsof -nPi – writing it as lsof -inP would generate a syntax error because lsof
      thinks that np is a parameter to -i.

      SSH Connections

      The following command finds all established SSH connections to the local machine:

      sudo lsof | grep sshd | grep ESTABLISHED
      
        
      253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      sshd  22361  mtsouk  3u  IPv4  8613370  0t0  TCP li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      
      

      The following command produces the same output as the previous command, but will do so more quickly because the -i TCP
      option limits the amount of information lsof prints, which mean that grep will have less data
      to process:

      sudo lsof -i TCP | grep ssh | grep ESTABLISHED
      

      Alternatively, you can execute the following command to find all established SSH
      connections:

      sudo lsof -nP -iTCP -sTCP:ESTABLISHED | grep SSH
      

      Showing Processes that are Listening to a Particular Port

      The following command shows all network connections that listen to port number 22
      (ssh) using either UDP or TCP:

      sudo lsof -i :22
      
        
      COMMAND  PID    USER    FD  TYPE  DEVICE   SIZE/OFF  NODE  NAME
      sshd     812    root    3u  IPv4  23674    0t0       TCP   *:ssh (LISTEN)
      sshd     812    root    4u  IPv6  23686    0t0       TCP   *:ssh (LISTEN)
      sshd     22352  root    3u  IPv4  8613370  0t0       TCP   li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      sshd     22361  mtsouk  3u  IPv4  8613370  0t0       TCP   li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      
      

      Determine Which Program Listens to a TCP port

      One of the most frequent uses of lsof is determining which program listens to a given TCP port.
      The following command will print TCP processes that are in the LISTEN state by using the -s option to provide a protocol and protocol state:

      sudo lsof -nP -i TCP -s TCP:LISTEN
      
        
      COMMAND  PID    USER      FD   TYPE  DEVICE   SIZE/OFF  NODE  NAME
      sshd     812    root      3u   IPv4  23674    0t0       TCP   *:22 (LISTEN)
      sshd     812    root      4u   IPv6  23686    0t0       TCP   *:22 (LISTEN)
      mysqld   1003   mysql     17u  IPv4  24217    0t0       TCP   127.0.0.1:3306 (LISTEN)
      master   1245   root      13u  IPv4  24480    0t0       TCP   *:25 (LISTEN)
      apache2  24565  root      4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24565  root      6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24567  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24567  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24568  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24568  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24569  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24569  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24570  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24570  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24571  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24571  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      apache2  24585  www-data  4u   IPv6  8626153  0t0       TCP   *:80 (LISTEN)
      apache2  24585  www-data  6u   IPv6  8626157  0t0       TCP   *:443 (LISTEN)
      
      

      Other possible states of a TCP connection are CLOSED, SYN-SENT, SYN-RECEIVED,
      ESTABLISHED, CLOSE-WAIT, LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING, and TIME-WAIT.

      Finding Information on a Given Protocol

      The next lsof command shows open UDP files that use the NTP (Network Time Protocol) port only:

      sudo lsof -i UDP:ntp
      
        
      COMMAND  PID  USER  FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
      ntpd     848  ntp   16u  IPv6  22807   0t0       UDP   *:ntp
      ntpd     848  ntp   17u  IPv4  22810   0t0       UDP   *:ntp
      ntpd     848  ntp   18u  IPv4  22814   0t0       UDP   localhost:ntp
      ntpd     848  ntp   19u  IPv4  22816   0t0       UDP   li140-253.members.linode.com:ntp
      ntpd     848  ntp   20u  IPv6  22818   0t0       UDP   localhost:ntp
      ntpd     848  ntp   24u  IPv6  24916   0t0       UDP   [2a01:7e00::f03c:91ff:fe69:1381]:ntp
      ntpd     848  ntp   25u  IPv6  24918   0t0       UDP   [fe80::f03c:91ff:fe69:1381]:ntp
      
      

      The output displays connections that use either IPv4 or IPv6. If you want to display
      the connections that use IPv4 only, you can run the following command:

      sudo lsof -i4 -a -i UDP:ntp
      
        
      COMMAND  PID  USER  FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
      ntpd     848  ntp   17u  IPv4  22810   0t0       UDP   *:ntp
      ntpd     848  ntp   18u  IPv4  22814   0t0       UDP   localhost:ntp
      ntpd     848  ntp   19u  IPv4  22816   0t0       UDP   li140-253.members.linode.com:ntp
      
      

      Disabling DNS and port Number Resolving

      lsof uses the data found in the /etc/services file to map a port number to a
      service. You can disable this functionality by using the –P option as follows:

      lsof -P -i UDP:ntp -a -i4
      
        
      COMMAND  PID  USER  FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
      ntpd     848  ntp   17u  IPv4  22810   0t0       UDP   *:123
      ntpd     848  ntp   18u  IPv4  22814   0t0       UDP   localhost:123
      ntpd     848  ntp   19u  IPv4  22816   0t0       UDP   li140-253.members.linode.com:123
      
      

      In a similar way, you can disable DNS resolving using the -n option:

      lsof -P -i UDP:ntp -a -i4 -n
      
        
      COMMAND  PID  USER  FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
      ntpd     848  ntp   17u  IPv4  22810   0t0       UDP   *:123
      ntpd     848  ntp   18u  IPv4  22814   0t0       UDP   127.0.0.1:123
      ntpd     848  ntp   19u  IPv4  22816   0t0       UDP   109.74.193.253:123
      
      

      The -n option can be particularly useful when you have a problem with your DNS
      servers or when you are interested in the actual IP address.

      Find Network Connections From or To an External Host

      The following command finds all network connections coming from or going to ppp-2-86-23-29.home.example.com:

      sudo lsof -i @ppp-2-86-23-29.home.example.com
      
        
      sshd  22352  root    3u  IPv4 8613370  0t0  TCP  li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.example.com:60032 (ESTABLISHED)
      sshd  22361  mtsouk  3u  IPv4 8613370  0t0  TCP  li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.example.com:60032 (ESTABLISHED)
      
      

      You can also specify the range of ports that interest you as follows:

      sudo lsof -i @ppp-2-86-23-29.home.example.com:200-250
      

      Determine Which Processes are Accessing a Given File

      With lsof you can find the processes that are accessing a given file. For example, by running the lsof command on it’s own file you can determine the processes that are accessing it:

      sudo lsof `which lsof`
      
        
      lsof  25079  root  txt  REG  8,0  163136 5693 /usr/bin/lsof
      lsof  25080  root  txt  REG  8,0  163136 5693 /usr/bin/lsof
      
      

      There are two lines in the above output because the /usr/bin/lsof file is being accessed twice, by
      both which(1) and lsof.

      If you are only interested in the process ID of the processes that are accessing
      a file, you can use the -t option to suppress header lines:

      sudo lsof -t `which lsof`
      
        
      25157
      25158
      
      

      A process ID can commonly be used for easily killing a process using the kill(1) command,
      however this is something that should only be executed with great care.

      List Open Files Under a Given Directory

      The +D lsof command will display all open files under a given directory,
      which in this case is /etc, as well as the name of the process that keeps a
      file or a directory open:

      sudo lsof +D /etc
      
        
      COMMAND    PID  USER   FD   TYPE  DEVICE  SIZE/OFF  NODE    NAME
      avahi-dae  669  avahi  cwd  DIR   8,0     4096      745751  /etc/avahi
      avahi-dae  669  avahi  rtd  DIR   8,0     4096      745751  /etc/avahi
      
      

      List Files that are Opened by a Specific User

      Another option is to locate the files opened by
      any user, including web and database users.

      The following command lists all open files opened by the www-data user:

      sudo lsof -u www-data
      
        
      COMMAND   PID   USER      FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
      php5-fpm  1066  www-data  cwd  DIR   8,0     4096      2     /
      php5-fpm  1066  www-data  rtd  DIR   8,0     4096      2     /
      
      ...
      
      

      The next variation finds all ESTABLISHED connections owned by the www-data user:

      sudo lsof -u www-data | grep -i ESTABLISHED
      
        
      apache2  24571  www-data  29u  IPv6  8675584  0t0  TCP  li140-253.members.linode.com:https->ppp-2-86-23-29.home.otenet.gr:61383 (ESTABLISHED)
      apache2  24585  www-data  29u  IPv6  8675583  0t0  TCP  li140-253.members.linode.com:https->ppp-2-86-23-29.home.otenet.gr:61381 (ESTABLISHED)
      apache2  27827  www-data  29u  IPv6  8675582  0t0  TCP  li140-253.members.linode.com:https->ppp-2-86-23-29.home.otenet.gr:61382 (ESTABLISHED)
      
      

      Last, the next command will find all processes except the ones owned by www-data by using the ^ character:

      sudo lsof -u ^www-data
      
        
      COMMAND  PID  TID   USER  FD    TYPE  DEVICE  SIZE/OFF  NODE     NAME
      systemd  1          root  cwd   DIR   8,0     4096      2        /
      systemd  1          root  rtd   DIR   8,0     4096      2        /
      systemd  1          root  txt   REG   8,0     1120992   1097764  /lib/systemd/systemd
      
      ...
      
      

      If the user name you are trying to use does not exist, you will get an error message
      similar to the following:

      sudo lsof -u doesNotExist
      
        
      lsof: can't get UID for doesNotExist
      lsof 4.89
       latest revision: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/
       latest FAQ: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/FAQ
       latest man page: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/lsof_man
       usage: [-?abhKlnNoOPRtUvVX] [+|-c c] [+|-d s] [+D D] [+|-E] [+|-e s] [+|-f[gG]]
       [-F [f]] [-g [s]] [-i [i]] [+|-L [l]] [+m [m]] [+|-M] [-o [o]] [-p s]
       [+|-r [t]] [-s [p:s]] [-S [t]] [-T [t]] [-u s] [+|-w] [-x [fl]] [--] [names]
      Use the ``-h'' option to get more help information.
      
      

      Kill All Processes Owned by a User

      The following command will kill all of the processes owned by the www-data user:

      Caution

      Please be careful when combining lsof with the kill(1) command. Do not try to
      test similar commands on a live server unless you are absolutely certain you will not experience issues – for testing purposes you can use a disposable Docker image or something similar.

      sudo kill -9 `lsof -t -u www-data`
      

      Find All Network Activity from a Given User

      The following command lists all network activity by a user named mtsouk:

      lsof -a -u mtsouk -i
      
        
      COMMAND  PID    USER    FD  TYPE  DEVICE   SIZE/OFF  NODE  NAME
      sshd     22361  mtsouk  3u  IPv4  8613370  0t0       TCP   li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      
      

      On the other hand, the following command lists all network activity from processes not owned by
      the root or the www-data user:

      lsof -a -u ^root -i -u ^www-data
      
        
      avahi-dae  669    avahi   12u  IPv4  20732    0t0  UDP   *:mdns
      avahi-dae  669    avahi   13u  IPv6  20733    0t0  UDP   *:mdns
      avahi-dae  669    avahi   14u  IPv4  20734    0t0  UDP   *:54087
      avahi-dae  669    avahi   15u  IPv6  20735    0t0  UDP   *:48582
      ntpd       848    ntp     16u  IPv6  22807    0t0  UDP  *:ntp
      ntpd       848    ntp     17u  IPv4  22810    0t0  UDP  *:ntp
      ntpd       848    ntp     18u  IPv4  22814    0t0  UDP  localhost:ntp
      ntpd       848    ntp     19u  IPv4  22816    0t0  UDP  li140-253.members.linode.com:ntp
      ntpd       848    ntp     20u  IPv6  22818    0t0  UDP  localhost:ntp
      ntpd       848    ntp     24u  IPv6  24916    0t0  UDP  [2a01:7e00::f03c:91ff:fe69:1381]:ntp
      ntpd       848    ntp     25u  IPv6  24918    0t0  UDP  [fe80::f03c:91ff:fe69:1381]:ntp
      mysqld     1003   mysql   17u  IPv4  24217    0t0  TCP  localhost:mysql (LISTEN)
      sshd       22361  mtsouk  3u   IPv4  8613370  0t0  TCP  li140-253.members.linode.com:ssh->ppp-2-86-23-29.home.otenet.gr:60032 (ESTABLISHED)
      
      

      Find the Total Number of TCP and UDP Connections

      If you process the output of lsof with some traditional UNIX command line tools, like grep and awk,
      you can calculate the total number of TCP and UDP connections:

      sudo lsof -i | awk '{print $8}' | sort | uniq -c | grep 'TCP|UDP'
      
        
           28 TCP
           13 UDP
      
      

      The lsof –i command lists all Internet connections whereas awk extracts the 8th
      field, which is the value of the NODE column and sort sorts the output. Then, the
      uniq –c command counts how many times each line exists. Last, the grep –v 'TCP|UDP'
      command displays the lines that contain the TCP or the UDP word in them.

      Summary

      lsof is a powerful diagnostic tool capable of a significant number of ways that you can combine its command line options to troubleshoot various issues administrators can find themselves facing. As this guide has only provided a few examples of how to use this tool, additional options can be combined for various effects that can be specifically suited to your needs.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      How To Build a Customer List Management App with React and TypeScript


      The author selected the Tech Education Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      TypeScript has brought a lot of improvement into how JavaScript developers structure and write code for apps, especially web applications. Defined as a superset of JavaScript, TypeScript behaves identically to JavaScript but with extra features designed to help developers build larger and more complex programs with fewer or no bugs. TypeScript is increasingly gaining popularity; adopted by major companies like Google for the Angular web framework. The Nest.js back-end framework was also built with TypeScript.

      One of the ways to improve productivity as a developer is the ability to implement new features as quickly as possible without any concern over breaking the existing app in production. To achieve this, writing statically typed code is a style adopted by many seasoned developers. Statically typed programming languages like TypeScript enforce an association for every variable with a data type; such as a string, integer, boolean, and so on. One of the major benefits of using a statically typed programming language is that type checking is completed at compile time, therefore developers can see errors in their code at a very early stage.

      React is an open-source JavaScript library, which developers use to create high-end user interfaces for scalable web applications. The great performance and dynamic user interfaces built with React for single-page applications make it a popular choice among developers.

      In this tutorial, you will create a customer list management application with a separate REST API backend and a frontend built with React and TypeScript. You will build the backend using a fake REST API named json-server. You’ll use it to quickly set up a CRUD (Create, Read, Update, and Delete) backend. Consequently you can focus on handling the front-end logic of an application using React and TypeScript.

      Prerequisites

      To complete this tutorial, you will need:

      Step 1 — Installing TypeScript and Creating the React Application

      In this step, you will install the TypeScript package globally on your machine by using the Node Package Manager (npm). After that, you will also install React and its dependencies, and check that your React app is working by running the development server.

      To begin, open a terminal and run the following command to install TypeScript:

      • npm install -g typescript

      Once the installation process is complete, execute the following command to check your installation of TypeScript:

      You will see the current version installed on your machine:

      Output

      Version 3.4.5

      Next, you will install the React application by using the create-react-app tool to set up the application with a single command. You'll use the npx command, which is a package runner tool that comes with npm 5.2+. The create-react-app tool has built-in support for working with TypeScript without any extra configuration required. Run the following command to create and install a new React application named typescript-react-app:

      • npx create-react-app typescript-react-app --typescript

      The preceding command will create a new React application with the name typescript-react-app. The --typescript flag will set the default filetype for React components to .tsx.

      Before you complete this section, the application will require moving from one port to another. To do that, you will need to install a routing library for your React application named React Router and its corresponding TypeScript definitions. You will use yarn to install the library and other packages for this project. This is because yarn is faster, especially for installing dependencies for a React application. Move into the newly created project folder and then install React Router with the following command:

      • cd typescript-react-app
      • yarn add react-router-dom

      You now have the React Router package, which will provide the routing functionality within your project. Next, run the following command to install the TypeScript definitions for React Router:

      • yarn add @types/react-router-dom

      Now you'll install axios, which is a promised-based HTTP client for browsers, to help with the process of performing HTTP requests from the different components that you will create within the application:

      Once the installation process is complete, start the development server with:

      Your application will be running on http://localhost:3000.

      React application homepage

      You have successfully installed TypeScript, created a new React application, and installed React Router in order to help with navigating from one page of the application to another. In the next section, you will set up the back-end server for the application.

      Step 2 — Creating a JSON Server

      In this step, you'll create a mock server that your React application can quickly connect with, as well as use its resources. It is important to note that this back-end service is not suitable for an application in production. You can use Nest.js, Express, or any other back-end technology to build a RESTful API in production. json-server is a useful tool whenever you need to create a prototype and mock a back-end server.

      You can use either npm or yarn to install json-server on your machine. This will make it available from any directory of your project whenever you might need to make use of it. Open a new terminal window and run this command to install json-server while you are still within the project directory:

      • yarn global add json-server

      Next, you will create a JSON file that will contain the data that will be exposed by the REST API. For the objects specified in this file (which you'll create), a CRUD endpoint will be generated automatically. To begin, create a new folder named server and then move into it:

      Now, use nano to create and open a new file named db.json:

      Add the following content to the file:

      /server/db.json

      {
          "customers": [
              {
                  "id": 1,
                  "first_name": "Customer_1",
                  "last_name": "Customer_11",
                  "email": "customer1@mail.com",
                  "phone": "00000000000",
                  "address": "Customer_1 Address",
                  "description": "Customer_1 description"
              },
              {
                  "id": 2,
                  "first_name": "Customer_2",
                  "last_name": "Customer_2",
                  "email": "customer2@mail.com",
                  "phone": "00000000000",
                  "address": "Customer_2 Adress",
                  "description": "Customer_2 Description"
              }
          ]
      }
      

      The JSON structure consists of a customer object, which has two datasets assigned. Each customer consists of seven properties: id, description, first_name, last_name, email, phone, and address.

      Save and exit the file.

      By default, the json-server runs on port 3000—this is the same port on which your React application runs. To avoid conflict, you can change the default port for the json-server. To do that, move to the root directory of the application:

      • cd ~/typescript-react-app

      Open the application with your preferred text editor and create a new file named json-server.json:

      Now insert the following to update the port number:

      /json-server.json

      {
          "port": 5000
      }
      

      This will act as the configuration file for the json-server and it will ensure that the server runs on the port specified in it at all times.

      Save and exit the file.

      To run the server, use the following command:

      • json-server --watch server/db.json

      This will start the json-server on port 5000. If you navigate to http://localhost:5000/customers in your browser, you will see the server showing your customer list.

      Customer list shown by json-server

      To streamline the process of running the json-server, you can update package.json with a new property named server to the scripts object as shown here:

      /package.json

      {
      ...
        "scripts": {
          "start": "react-scripts start",
          "build": "react-scripts build",
          "test": "react-scripts test",
          "eject": "react-scripts eject",
          "server": "json-server --watch server/db.json"
        },
      ...
      }
      

      Save and exit the file.

      Now anytime you wish to start the json-server, all you have to do is run yarn server from the terminal.

      You've created a simple REST API that you will use as the back-end server for this application. You also created a customer JSON object that will be used as the default data for the REST API. Lastly, you configured an alternative port for the back-end server powered by json-server. Next, you will build reusable components for your application.

      Step 3 — Creating Reusable Components

      In this section, you will create the required React components for the application. This will include components to create, display, and edit the details of a particular customer in the database respectively. You'll also build some of the TypeScript interfaces for your application.

      To begin, move back to the terminal where you have the React application running and stop the development server with CTRL + C. Next, navigate to the ./src/ folder:

      Then, create a new folder named components inside of it and move into the new folder:

      • mkdir components
      • cd components

      Within the newly created folder, create a customer folder and then move into it:

      • mkdir customer
      • cd customer

      Now create two new files named Create.tsx and Edit.tsx:

      • touch Create.tsx Edit.tsx

      These files are React reusable components that will render the forms and hold all the business logic for creating and editing the details of a customer respectively.

      Open the Create.tsx file in your text editor and add the following code:

      /src/components/customer/Create.tsx

      import * as React from 'react';
      import axios from 'axios';
      import { RouteComponentProps, withRouter } from 'react-router-dom';
      
      export interface IValues {
          first_name: string,
          last_name: string,
          email: string,
          phone: string,
          address: string,
          description: string,
      }
      export interface IFormState {
          [key: string]: any;
          values: IValues[];
          submitSuccess: boolean;
          loading: boolean;
      }
      
      

      Here you've imported React, axios, and other required components necessary for routing from the React Router package. After that you created two new interfaces named IValues and IFormState. TypeScript interfaces help to define the specific type of values that should be passed to an object and enforce consistency throughout an application. This ensures that bugs are less likely to appear in your program.

      Next, you will build a Create component that extends React.Component. Add the following code to the Create.tsx file immediately after the IFormState interface:

      /src/components/customer/Create.tsx

      ...
      class Create extends React.Component<RouteComponentProps, IFormState> {
          constructor(props: RouteComponentProps) {
              super(props);
              this.state = {
                  first_name: '',
                  last_name: '',
                  email: '',
                  phone: '',
                  address: '',
                  description: '',
                  values: [],
                  loading: false,
                  submitSuccess: false,
              }
          }
      }
      export default withRouter(Create)
      

      Here you've defined a React component in Typescript. In this case, the Create class component accepts props (short for “properties”) of type RouteComponentProps and uses a state of type IFormState. Then, inside the constructor, you initialized the state object and defined all the variables that will represent the rendered values for a customer.

      Next, add these methods within the Create class component, just after the constructor. You'll use these methods to process customer forms and handle all changes in the input fields:

      /src/components/customer/Create.tsx

      ...
                values: [],
                loading: false,
                submitSuccess: false,
            }
        }
      
        private processFormSubmission = (e: React.FormEvent<HTMLFormElement>): void => {
                e.preventDefault();
                this.setState({ loading: true });
                const formData = {
                    first_name: this.state.first_name,
                    last_name: this.state.last_name,
                    email: this.state.email,
                    phone: this.state.phone,
                    address: this.state.address,
                    description: this.state.description,
                }
                this.setState({ submitSuccess: true, values: [...this.state.values, formData], loading: false });
                axios.post(`http://localhost:5000/customers`, formData).then(data => [
                    setTimeout(() => {
                        this.props.history.push('/');
                    }, 1500)
                ]);
            }
      
            private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {
                e.preventDefault();
                this.setState({
                    [e.currentTarget.name]: e.currentTarget.value,
            })
        }
      
      ...
      export default withRouter(Create)
      ...
      

      The processFormSubmission() method receives the details of the customer from the application state and posts it to the database using axios. The handleInputChanges() uses React.FormEvent to obtain the values of all input fields and calls this.setState() to update the state of the application.

      Next, add the render() method within the Create class component immediately after the handleInputchanges() method. This render() method will display the form to create a new customer in the application:

      /src/components/customer/Create.tsx

      ...
        public render() {
            const { submitSuccess, loading } = this.state;
            return (
                <div>
                    <div className={"col-md-12 form-wrapper"}>
                        <h2> Create Post </h2>
                        {!submitSuccess && (
                            <div className="alert alert-info" role="alert">
                                Fill the form below to create a new post
                        </div>
                        )}
                        {submitSuccess && (
                            <div className="alert alert-info" role="alert">
                                The form was successfully submitted!
                                </div>
                        )}
                        <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>
                            <div className="form-group col-md-12">
                                <label htmlFor="first_name"> First Name </label>
                                <input type="text" id="first_name" onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />
                            </div>
                            <div className="form-group col-md-12">
                                <label htmlFor="last_name"> Last Name </label>
                                <input type="text" id="last_name" onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />
                            </div>
                            <div className="form-group col-md-12">
                                <label htmlFor="email"> Email </label>
                                <input type="email" id="email" onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />
                            </div>
                            <div className="form-group col-md-12">
                                <label htmlFor="phone"> Phone </label>
                                <input type="text" id="phone" onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />
                            </div>
                            <div className="form-group col-md-12">
                                <label htmlFor="address"> Address </label>
                                <input type="text" id="address" onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />
                            </div>
                            <div className="form-group col-md-12">
                                <label htmlFor="description"> Description </label>
                                <input type="text" id="description" onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />
                            </div>
                            <div className="form-group col-md-4 pull-right">
                                <button className="btn btn-success" type="submit">
                                    Create Customer
                                </button>
                                {loading &&
                                    <span className="fa fa-circle-o-notch fa-spin" />
                                }
                            </div>
                        </form>
                    </div>
                </div>
            )
        }
      ...
      

      Here, you created a form with the input fields to hold the values of the first_name, last_name, email, phone, address, and description of a customer. Each of the input fields have a method handleInputChanges() that runs on every keystroke, updating the React state with the value it obtains from the input field. Furthermore, depending on the state of the application, a boolean variable named submitSuccess will control the message that the application will display before and after creating a new customer.

      You can see the complete code for this file in this GitHub repository.

      Save and exit Create.tsx.

      Now that you have added the appropriate logic to the Create component file for the application, you'll proceed to add contents for the Edit component file.

      Open your Edit.tsx file within the customer folder, and start by adding the following content to import React, axios, and also define TypeScript interfaces:

      /src/components/customer/Edit.tsx

      import * as React from 'react';
      import { RouteComponentProps, withRouter } from 'react-router-dom';
      import axios from 'axios';
      
      export interface IValues {
          [key: string]: any;
      }
      export interface IFormState {
          id: number,
          customer: any;
          values: IValues[];
          submitSuccess: boolean;
          loading: boolean;
      }
      

      Similarly to the Create component, you import the required modules and create IValues and IFormState interfaces respectively. The IValues interface defines the data type for the input fields' values, while you'll use IFormState to declare the expected type for the state object of the application.

      Next, create the EditCustomer class component directly after the IFormState interface block as shown here:

      /src/components/customer/Edit.tsx

      ...
      class EditCustomer extends React.Component<RouteComponentProps<any>, IFormState> {
          constructor(props: RouteComponentProps) {
              super(props);
              this.state = {
                  id: this.props.match.params.id,
                  customer: {},
                  values: [],
                  loading: false,
                  submitSuccess: false,
              }
          }
      }
      export default withRouter(EditCustomer)
      

      This component takes the RouteComponentProps<any> and an interface of IFormState as a parameter. You use the addition of <any> to the RouteComponentProps because whenever React Router parses path parameters, it doesn’t do any type conversion to ascertain whether the type of the data is number or string. Since you're expecting a parameter for uniqueId of a customer, it is safer to use any.

      Now add the following methods within the component:

      /src/components/customer/Edit.tsx

      ...
          public componentDidMount(): void {
              axios.get(`http://localhost:5000/customers/${this.state.id}`).then(data => {
                  this.setState({ customer: data.data });
              })
          }
      
          private processFormSubmission = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
              e.preventDefault();
              this.setState({ loading: true });
              axios.patch(`http://localhost:5000/customers/${this.state.id}`, this.state.values).then(data => {
                  this.setState({ submitSuccess: true, loading: false })
                  setTimeout(() => {
                      this.props.history.push('/');
                  }, 1500)
              })
          }
      
          private setValues = (values: IValues) => {
              this.setState({ values: { ...this.state.values, ...values } });
          }
          private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {
              e.preventDefault();
              this.setValues({ [e.currentTarget.id]: e.currentTarget.value })
          }
      ...
      }
      
      export default withRouter(EditCustomer)
      

      First, you add a componentDidMount() method, which is a lifecycle method that is being called when the component is created. The method takes the id obtained from the route parameter to identify a particular customer as a parameter, uses it to retrieve their details from the database and then populates the form with it. Furthermore, you add methods to process form submission and handle changes made to the values of the input fields.

      Lastly, add the render() method for the Edit component:

      /src/components/customer/Edit.tsx

      ...
          public render() {
              const { submitSuccess, loading } = this.state;
              return (
                  <div className="App">
                      {this.state.customer &&
                          <div>
                              < h1 > Customer List Management App</h1>
                              <p> Built with React.js and TypeScript </p>
      
                              <div>
                                  <div className={"col-md-12 form-wrapper"}>
                                      <h2> Edit Customer </h2>
                                      {submitSuccess && (
                                          <div className="alert alert-info" role="alert">
                                              Customer's details has been edited successfully </div>
                                      )}
                                      <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="first_name"> First Name </label>
                                              <input type="text" id="first_name" defaultValue={this.state.customer.first_name} onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />
                                          </div>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="last_name"> Last Name </label>
                                              <input type="text" id="last_name" defaultValue={this.state.customer.last_name} onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />
                                          </div>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="email"> Email </label>
                                              <input type="email" id="email" defaultValue={this.state.customer.email} onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />
                                          </div>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="phone"> Phone </label>
                                              <input type="text" id="phone" defaultValue={this.state.customer.phone} onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />
                                          </div>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="address"> Address </label>
                                              <input type="text" id="address" defaultValue={this.state.customer.address} onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />
                                          </div>
                                          <div className="form-group col-md-12">
                                              <label htmlFor="description"> Description </label>
                                              <input type="text" id="description" defaultValue={this.state.customer.description} onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />
                                          </div>
                                          <div className="form-group col-md-4 pull-right">
                                              <button className="btn btn-success" type="submit">
                                                  Edit Customer </button>
                                              {loading &&
                                                  <span className="fa fa-circle-o-notch fa-spin" />
                                              }
                                          </div>
                                      </form>
                                  </div>
                              </div>
                          </div>
                      }
                  </div>
              )
          }
      ...    
      

      Here, you created a form to edit the details of a particular customer, and then populated the input fields within that form with the customer's details that your application's state obtained. Similarly to the Create component, changes made to all the input fields will be handled by the handleInputChanges() method.

      You can see the complete code for this file in this GitHub repository.

      Save and exit Edit.tsx.

      To view the complete list of customers created within the application, you’ll create a new component within the ./src/components folder and name it Home.tsx:

      • cd ./src/components
      • nano Home.tsx

      Add the following content:

      /src/components/Home.tsx

      import * as React from 'react';
      import { Link, RouteComponentProps } from 'react-router-dom';
      import axios from 'axios';
      
      interface IState {
          customers: any[];
      }
      
      export default class Home extends React.Component<RouteComponentProps, IState> {
          constructor(props: RouteComponentProps) {
              super(props);
              this.state = { customers: [] }
          }
          public componentDidMount(): void {
              axios.get(`http://localhost:5000/customers`).then(data => {
                  this.setState({ customers: data.data })
              })
          }
          public deleteCustomer(id: number) {
              axios.delete(`http://localhost:5000/customers/${id}`).then(data => {
                  const index = this.state.customers.findIndex(customer => customer.id === id);
                  this.state.customers.splice(index, 1);
                  this.props.history.push('/');
              })
          }
      }
      

      Here, you've imported React, axios, and other required components from React Router. You created two new methods within the Home component:

      • componentDidMount(): The application invokes this method immediately after a component is mounted. Its responsibility here is to retrieve the list of customers and update the home page with it.
      • deleteCustomer(): This method will accept an id as a parameter and will delete the details of the customer identified with that id from the database.

      Now add the render() method to display the table that holds the list of customers for the Home component:

      /src/components/Home.tsx

      ...
      public render() {
              const customers = this.state.customers;
              return (
                  <div>
                      {customers.length === 0 && (
                          <div className="text-center">
                              <h2>No customer found at the moment</h2>
                          </div>
                      )}
                      <div className="container">
                          <div className="row">
                              <table className="table table-bordered">
                                  <thead className="thead-light">
                                      <tr>
                                          <th scope="col">Firstname</th>
                                          <th scope="col">Lastname</th>
                                          <th scope="col">Email</th>
                                          <th scope="col">Phone</th>
                                          <th scope="col">Address</th>
                                          <th scope="col">Description</th>
                                          <th scope="col">Actions</th>
                                      </tr>
                                  </thead>
                                  <tbody>
                                      {customers && customers.map(customer =>
                                          <tr key={customer.id}>
                                              <td>{customer.first_name}</td>
                                              <td>{customer.last_name}</td>
                                              <td>{customer.email}</td>
                                              <td>{customer.phone}</td>
                                              <td>{customer.address}</td>
                                              <td>{customer.description}</td>
                                              <td>
                                                  <div className="d-flex justify-content-between align-items-center">
                                                      <div className="btn-group" style={{ marginBottom: "20px" }}>
                                                          <Link to={`edit/${customer.id}`} className="btn btn-sm btn-outline-secondary">Edit Customer </Link>
                                                          <button className="btn btn-sm btn-outline-secondary" onClick={() => this.deleteCustomer(customer.id)}>Delete Customer</button>
                                                      </div>
                                                  </div>
                                              </td>
                                          </tr>
                                      )}
                                  </tbody>
                              </table>
                          </div>
                      </div>
                  </div>
              )
          }
      ...
      

      In this code block, you retrieve the lists of customers from the application's state as an array, iterate over it, and display it within an HTML table. You also add the customer.id parameter, which the method uses to identify and delete the details of a particular customer from the list.

      Save and exit Home.tsx.

      You've adopted a statically typed principle for all the components created with this application by defining types for the components and props through the use of interfaces. This is one of the best approaches to using TypeScript for a React application.

      With this, you've finished creating all the required reusable components for the application. You can now update the app component with links to all the components that you have created so far.

      Step 4 — Setting Up Routing and Updating the Entry Point of the Application

      In this step, you will import the necessary components from the React Router package and configure the App component to render different components depending on the route that is loaded. This will allow you to navigate through different pages of the application. Once a user visits a route, for example /create, React Router will use the path specified to render the contents and logic within the appropriate component defined to handle such route.

      Navigate to ./src/App.tsx:

      Then replace its content with the following:

      /src/App.tsx

      import * as React from 'react';
      import './App.css';
      import { Switch, Route, withRouter, RouteComponentProps, Link } from 'react-router-dom';
      import Home from './components/Home';
      import Create from './components/customer/Create';
      import EditCustomer from './components/customer/Edit';
      
      class App extends React.Component<RouteComponentProps<any>> {
        public render() {
          return (
            <div>
              <nav>
                <ul>
                  <li>
                    <Link to={'/'}> Home </Link>
                  </li>
                  <li>
                    <Link to={'/create'}> Create Customer </Link>
                  </li>
                </ul>
              </nav>
              <Switch>
                <Route path={'/'} exact component={Home} />
                <Route path={'/create'} exact component={Create} />
                <Route path={'/edit/:id'} exact component={EditCustomer} />
              </Switch>
            </div>
          );
        }
      }
      export default withRouter(App);
      

      You imported all the necessary components from the React Router package and you also imported the reusable components for creating, editing, and viewing customers' details.

      Save and exit App.tsx.

      The ./src/index.tsx file is the entry point for this application and renders the application. Open this file and import React Router into it, then wrap the App component inside a BrowserRouter:

      /src/index.tsx

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './App';
      import { BrowserRouter } from 'react-router-dom'; 
      import * as serviceWorker from './serviceWorker';
      ReactDOM.render(
          <BrowserRouter>
              <App />
          </BrowserRouter>
          , document.getElementById('root')
      );
      serviceWorker.unregister();
      

      React Router uses the BrowserRouter component to make your application aware of the navigation, such as history and current path.

      Once you've finished editing Index.tsx, save and exit.

      Lastly, you will use Bootstrap to add some style to your application. Bootstrap is a popular HTML, CSS, and JavaScript framework for developing responsive, mobile-first projects on the web. It allows developers to build an appealing user interface without having to write too much CSS. It comes with a responsive grid system that gives a web page a finished look that works on all devices.

      To include Bootstrap and styling for your application, replace the contents of ./src/App.css with the following:

      /src/App.css

      @import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';
      
      .form-wrapper {
        width: 500px;
        margin: 0 auto;
      }
      .App {
        text-align: center;
        margin-top: 30px;
      }
      nav {
        width: 300px;
        margin: 0 auto;
        background: #282c34;
        height: 70px;
        line-height: 70px;
      }
      nav ul li {
        display: inline;
        list-style-type: none;
        text-align: center;
        padding: 30px;
      }
      nav ul li a {
        margin: 50px 0;
        font-weight: bold;
        color: white;
        text-decoration: none;
      }
      nav ul li a:hover {
        color: white;
        text-decoration: none;
      }
      table {
        margin-top: 50px;
      }
      .App-link {
        color: #61dafb;
      }
      @keyframes App-logo-spin {
        from {
          transform: rotate(0deg);
        }
        to {
          transform: rotate(360deg);
        }
      }
      

      You have used Bootstrap here to enhance the look and feel of the application by giving it a default layout, styles, and color. You have also added some custom styles, particularly to the navigation bar.

      Save and exit App.css.

      In this section, you have configured React Router to render the appropriate component depending on the route visited by the user and also added some styling to make the application more attractive to users. Next, you will test all the functionality implemented for the application.

      Step 5 — Running Your Application

      Now that you have set up the frontend of this application with React and TypeScript by creating several reusable components, and also built a REST API with the json-server, you can run your app.

      Navigate back to the project’s root folder:

      • cd ~/typescript-react-app

      Next run the following command to start your app:

      Note: Make sure your server is still running in the other terminal window. Otherwise, start it with: yarn server.

      Navigate to http://localhost:3000 to view the application from your browser. Then proceed to click on the Create button and fill in the details of a customer.

      Create customer page

      After entering the appropriate values in the input fields, click on the Create Customer button to submit the form. The application will redirect you back to your homepage once you're done creating a new customer.

      View customers page

      Click the Edit Customer button for any of the rows and you will be directed to the page that hosts the editing functionality for the corresponding customer on that row.

      Edit customer page

      Edit the details of the customer and then click on Edit Customer to update the customer’s details.

      You've run your application to ensure all the components are working. Using the different pages of your application, you've created and edited a customer entry.

      Conclusion

      In this tutorial you built a customer list management app with React and TypeScript. The process in this tutorial is a deviation from using JavaScript as the conventional way of structuring and building applications with React. You've leveraged the benefits of using TypeScript to complete this front-end focused tutorial.

      To continue to develop this project, you can move your mock back-end server to a production-ready back-end technology like Express or Nest.js. Furthermore, you can extend what you have built in this tutorial by adding more features such as authentication and authorization with different tools like the Passport.js authentication library.

      You can find the complete source code for the project on GitHub.



      Source link