O'Reilly logo

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Building Enterprise JavaScript Applications

Book Description

Become a senior developer by building enterprise applications that use modern techniques such as TDD, containerization, continuous integration, and deployment

Key Features

  • Create production-grade JavaScript applications from scratch
  • Build microservices and deploy them to a Docker container for scaling applications
  • Test and deploy your code with confidence using Travis CI

Book Description

With the over-abundance of tools in the JavaScript ecosystem, it's easy to feel lost. Build tools, package managers, loaders, bundlers, linters, compilers, transpilers, typecheckers - how do you make sense of it all?

In this book, we will build a simple API and React application from scratch. We begin by setting up our development environment using Git, yarn, Babel, and ESLint. Then, we will use Express, Elasticsearch and JSON Web Tokens (JWTs) to build a stateless API service. For the front-end, we will use React, Redux, and Webpack.

A central theme in the book is maintaining code quality. As such, we will enforce a Test-Driven Development (TDD) process using Selenium, Cucumber, Mocha, Sinon, and Istanbul. As we progress through the book, the focus will shift towards automation and infrastructure. You will learn to work with Continuous Integration (CI) servers like Jenkins, deploying services inside Docker containers, and run them on Kubernetes.

By following this book, you would gain the skills needed to build robust, production-ready applications.

What you will learn

  • Practice Test-Driven Development (TDD) throughout the entire book
  • Use Cucumber, Mocha and Selenium to write E2E, integration, unit and UI tests
  • Build stateless APIs using Express and Elasticsearch
  • Document your API using OpenAPI and Swagger
  • Build and bundle front-end applications using React, Redux and Webpack
  • Containerize services using Docker
  • Deploying scalable microservices using Kubernetes

Who this book is for

If you're a JavaScript developer looking to expand your skillset and become a senior JavaScript developer by building production-ready web applications, then this book is for you.

Downloading the example code for this book You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

Table of Contents

  1. Title Page
  2. Copyright and Credits
    1. Building Enterprise JavaScript Applications
  3. Dedication
  4. Packt Upsell
    1. Why subscribe?
    2. PacktPub.com
  5. Contributors
    1. About the author
    2. About the reviewer
    3. Packt is searching for authors like you
  6. Preface
    1. Who this book is for
    2. What this book covers
      1. Section 1 – Theory and practice
      2. Section 2 – Developing our backend API
      3. Section 3 – Developing our frontend UI
      4. Section 4 – Infrastructure and automation
      5. Section 5 – Important JavaScript concepts and syntax
      6. What is not covered
    3. To get the most out of this book
      1. Download the example code files
      2. Conventions used
    4. Get in touch
      1. Reviews
  7. The Importance of Good Code
    1. Technical debt
      1. What is technical debt?
      2. Causes of technical debt
        1. The debt spiral
      3. Consequences of technical debt
        1. Technical debt leads to low morale
          1. Consequences of low morale
      4. Repaying technical debt through refactoring
      5. Preventing technical debt
        1. Informing the decision makers
          1. The triple constraint
          2. The fallacy of the triple constraint
        2. Refuse to develop
        3. Don't be a hero
        4. Defining processes
    2. Test-Driven Development
      1. Understanding the TDD process
        1. Fixing bugs
      2. Benefits of TDD
        1. Avoiding manual tests
        2. Tests as specification
        3. Tests as documentation
        4. Short development cycles
      3. Difficulties with TDD adoption
      4. When not to use TDD
    3. Summary
  8. The State of JavaScript
    1. Evolution of the web application
      1. Just-in-time (JIT) compilers
      2. Single page applications (SPAs)
      3. Isomorphic JavaScript applications
    2. Benefits of Node.js
      1. Context switching
        1. Switching between projects
        2. Switching between languages
        3. The business perspective
      2. Shared code
    3. Summary
  9. Managing Version History with Git
    1. Setting up Git
      1. Creating a new repository
      2. Configuring Git
        1. Configuring a user
    2. Learning the basics
      1. Committing to history
        1. Understanding file states in Git
          1. The three tracked states
        2. Staging our changes
        3. Quick recap
    3. Branching and merging
      1. Git branches
      2. Branching models
        1. The Driessen model
      3. Creating a development branch
      4. Creating feature branches
        1. Naming sub-branches
      5. Merging branches
      6. Examining more realistic examples
        1. Keeping the dev Branch Bug-Free
        2. Keeping our history clean
      7. Keeping our history clean with git rebase
      8. Using merge and rebase together
    4. Releasing code
      1. Semantic versioning
      2. Creating a release branch
      3. Tagging releases
    5. Hotfixes
    6. Working with others
      1. Creating a remote repository
      2. Pulling and pushing
        1. Cloning a repository
      3. Conducting peer review through pull requests
    7. Summary
  10. Setting Up Development Tools
    1. What is Node.js?
      1. Terminology
    2. Modules
      1. The dawn of modules
      2. The birth of Node.js modules
        1. Adoption of the CommonJS standard
          1. Fulfilling the encapsulation requirement
      3. Standardizing module formats
    3. Installing Node
      1. Using nvm to install Node
      2. Documenting Node versions
    4. Starting projects with npm
    5. Using yarn instead of npm
      1. Package version locking
      2. Offline cache
      3. Speed
      4. Installing yarn
      5. Getting familiar with the yarn CLI
      6. npm and yarn, together
    6. Creating an HTTP server
      1. Our HTTP server in detail
    7. Transpiling ES6 with Babel
      1. Babel is a transpiler...and more!
      2. Different faces of Babel
        1. @babel/cli
        2. @babel/register
          1. Using @babel/register for tests
        3. @babel/node
        4. @babel/core
        5. @babel/polyfill
      3. Adding Babel CLI and polyfill
      4. Using Babel CLI to transpile our code
        1. Plugins and presets
          1. The env preset
        2. Separating source and distribution code
      5. Importing the Babel polyfill
    8. Consolidating commands with npm scripts
      1. Ensuring cross-platform compatibility
    9. Automating development using nodemon
    10. Linting with ESLint
      1. Installing ESLint
      2. Linting our code
      3. Adding lint script to package.json
      4. Installing the ESLint extension
      5. Adding pre-commit hooks
    11. Committing our code into Git
      1. Using .gitignore to ignore files
    12. Summary
  11. Writing End-to-End Tests
    1. Understanding different types of test
      1. Structuring our test suite with the testing pyramid
      2. When implementing a new feature, write your E2E tests first
    2. Following a TDD workflow
      1. Gathering business requirements
      2. Formalizing requirements through documentation
      3. Refining requirements into specification
        1. Writing tests as specification
      4. Test-driven development
      5. Writing manual tests
        1. Exploratory testing
      6. Maintenance
    3. Gathering requirements
    4. Setting Up E2E tests with Cucumber
      1. Features, scenarios, and steps
        1. Gherkin keywords
        2. Specifying our feature
        3. Writing our first scenario
      2. Laying out our step definitions
      3. Running our scenarios
    5. Implementing step definitions
      1. Calling our endpoint
      2. Asserting results
      3. Using a debugger for Node.js debugging
        1. Using Chrome DevTools
        2. Using ndb
        3. Using the Visual Studio Code debugger
          1. Retaining line numbers
        4. Examining the req object
        5. Making work-in-progress (WIP) commits
      4. Asserting the correct response status code
        1. You ain't gonna need it (YAGNI)
      5. Asserting the correct response payload
      6. Asserting the correct response payload content
      7. Refactoring
        1. Isolating contexts for each scenario
        2. Making failure more informative
        3. Removing hardcoded values
    6. Validating data type
      1. Refactoring our tests
        1. Using scenario outlines
        2. Combining duplicate step definitions
      2. Refactoring our application
        1. Choosing a framework
    7. Migrating our API to Express
      1. (Re)defining routes
      2. Using body-parser middleware
        1. Run E2E test
    8. Moving common logic into middleware
    9. Validating our payload
      1. Checking for required fields
      2. Checking property type
      3. Checking the payload property's format
      4. Refactoring our step definitions
    10. Testing the success scenario
    11. Summary
  12. Storing Data in Elasticsearch
    1. Introduction to Elasticsearch
      1. Elasticsearch versus other distributed document store
    2. Installing Java and Elasticsearch
      1. Installing Java
      2. Installing and starting Elasticsearch
    3. Understanding key concepts in Elasticsearch
      1. Elasticsearch is a JSON document store
        1. Document vs. relationship data storage
      2. Understanding indices, types, documents, and versions
    4. Querying Elasticsearch from E2E tests
    5. Indexing documents to Elasticsearch
    6. Cleaning up after our tests
      1. Deleting our test user
      2. Improving our testing experience
      3. Running tests in a test database
      4. Separating development and testing servers
      5. Making a standalone E2E test script
        1. The shebang interpreter directive
        2. Ensuring Elasticsearch is running
        3. Running the test API server in the background
        4.  Checking our API server is ready
          1. Checking API status using netstat/ss
        5. Cleaning up the background process
        6. Running our tests
    7. Summary
  13. Modularizing Our Code
    1. Modularizing our code
      1. Modularizing our middleware
      2. Modularizing our request handlers
      3. The single responsibility principle
      4. Decoupling our validation logic
        1. Creating the ValidationError interface
        2. Modularizing our validation logic
      5. Creating engines
    2. Adding a user profile
      1. Writing a specification as a test
      2. Schema-based validation
      3. Types of schema
      4. Picking an object schema and validation library
        1. Interoperability
        2. Expressiveness
      5. Creating our profile schema
        1. Rejecting additional properties
          1. Dynamic mapping in Elasticsearch
        2. Adding specificity to a sub-schema
        3. Adding a title and description
        4. Specifying a meta-schema
        5. Specifying a unique ID
      6. Creating a schema for the Create User request payload
      7. Picking a JSON Schema validation library
      8. Validating against JSON Schema with Ajv
      9. Generating validation error messages
      10. Generalizing functions
      11. Updating the npm build script
      12. Testing the success scenario
      13. Resetting our test index
    3. Summary
  14. Writing Unit/Integration Tests
    1. Picking a testing framework
      1. Installing Mocha
    2. Structuring our test files
    3. Writing our first unit test
      1. Describing the expected behavior
      2. Overriding ESLint for test files
        1. Understanding arrow functions in Mocha
        2. Specifying ESLint environments
      3. Running our unit tests
      4. Running unit tests as an npm script
    4. Completing our first unit test suite
    5. Unit testing ValidationError
    6. Unit testing middleware
      1. Asserting deep equality
      2. Asserting function calls with spies
      3. Simulating behavior with stubs
      4. Testing all middleware functions
    7. Unit testing the request handler
      1. Stubbing create
      2. Dependency injection
      3. Monkey patching
      4. Dependency injection versus monkey patching
        1. Modularity
        2. Readability
        3. Reliance on third-party tools
      5. Following the dependency injection pattern
      6. Promises and Mocha
        1. Dealing with rejected promises
        2. Completing the unit tests
    8. Unit testing our engine
    9. Integration testing our engine
    10. Adding test coverage
      1. Reading a test coverage report
      2. Improving test coverage
      3. Code coverage versus test quality
      4. You don't have to test everything, all the time
      5. Unifying test coverage
      6. Ignoring files
    11. Finishing up
    12. Summary
  15. Designing Our API
    1. What it means to be RESTful
      1. What is REST?
      2. What REST is not
      3. Should my API be RESTful?
    2. Designing our API
      1. Consistent
        1. Common consistency
          1. Sending the correct HTTP status code
          2. Using HTTP methods
          3. Using ISO formats
        2. Local consistency
          1. Naming convention
          2. Consistent data exchange format
          3. Error response payload
        3. Transversal consistency
        4. Domain consistency
        5. Perennial consistency
          1. Breaking changes in APIs
          2. Future-proofing your URL
          3. Future-proofing your data structure
          4. Versioning
      2. Intuitive
        1. URLs for humans
        2. Favor verbosity and explicitness
      3. Keep It Simple Stupid (KISS)
    3. Completing our API
    4. Summary
  16. Deploying Our Application on a VPS
    1. Obtaining an IP address
      1. Managed DNS
    2. Setting up a Virtual Private Server (VPS)
      1. Creating a VPS instance
        1. Choosing an image
        2. Choosing a size
        3. Picking a data center region
        4. Selecting additional options
        5. Naming your server
      2. Connecting to the VPS
      3. Setting up user accounts
        1. Creating a new user
        2. Adding a user to the sudo group
      4. Setting up public key authentication
        1. Checking for existing SSH key(s)
        2. Creating an SSH key
        3. Adding the SSH key to the remote server
          1. Using ssh-copy-id
        4. Providing extra security
          1. Disable password-based authentication
          2. Disable root login
          3. Firewall
        5. Configuring the time zone
    3. Running our API
    4. Keeping our API alive with PM2
      1. Killing a process
      2. Keeping PM2 alive
    5. Running our API on port 80
      1. Privileged ports
      2. Possible solutions
        1. Running as root
          1. De-escalating privileges
        2. Setting capabilities
        3. Using authbind
        4. Using iptables
        5. Using reverse proxy
          1. What's a proxy? What's a reverse proxy?
    6. Setting up NGINX
      1. Configuring NGINX
        1. Understanding NGINX's configuration file
        2. Configuring the HTTP module
        3. Splitting nginx.conf into multiple files
    7. From IP to domain
      1. Buying a domain
      2. Understanding DNS
      3. Updating the domain nameserver
      4. Building our zone file
        1. NS records
        2. A and AAAA
        3. Start of Authority (SOA)
      5. Updating NGINX
    8. Summary
  17. Continuous Integration
    1. Continuous Integration (CI)
      1. Picking a CI server
    2. Integrating with Travis CI
      1. Configuring Travis CI
        1. Specifying the language
        2. Setting up databases
        3. Setting environment variables
        4. Activating our project
      2. Examining Travis CI results
    3. Continuous Integration with Jenkins
      1. Introduction to Jenkins
        1. Freestyle projects
        2. Pipeline
      2. Setting up a new Jenkins server
        1. Creating the jenkins user
        2. Configuring time
        3. Installing Java
        4. Installing Jenkins
        5. Installing NGINX as a reverse proxy
        6. Configuring the firewall
        7. Updating our DNS records
        8. Configuring Jenkins
      3. Composing a Jenkinsfile
        1. The Pipeline DSL syntax
        2. Declarative versus scripted pipelines
        3. The declarative pipeline
        4. The scripted pipeline
        5. Setting up the environment
        6. Installing Docker
      4. Integration with GitHub
        1. Providing access to the repository
          1. The Personal Access (OAuth) Token
        2. Using the GitHub plugin
        3. Setting up GitHub service hooks manually
        4. Creating a new folder
        5. Creating a new pipeline
        6. Running the first build
    4. Summary
  18. Security – Authentication and Authorization
    1. What is Authentication?
    2. Introduction to password-based authentication
      1. Hashing passwords
        1. Cryptographic hash functions
        2. Picking a cryptographic hashing algorithm
          1. Hash stretching
          2. Hash stretching algorithms
      2. Preventing brute-force attacks against a single user
        1. Protecting against brute-force attacks
      3. Reverse lookup table attacks
        1. Protecting against reverse lookup table attacks
    3. Implementing password-base authentication
      1. Updating existing E2E tests
        1. Generating a random digest
          1. Picking a bcrypt library
          2. Using the bcryptjs library
        2. Validating a digest
      2. Updating an existing implementation
      3. Retrieving the salt
        1. Implementing the Retrieve Salt endpoint
          1. Implementing a Retrieve Salt engine
      4. Generating a salt for non-existent users
        1. Writing E2E tests
        2. Implementation
      5. Login
        1. Writing tests
        2. Implementing Login
    4. Keeping users authenticated
      1. JSON web tokens (JWTs)
        1. Anatomy of a JWT
        2. Header
        3. Payload and claims
          1. Registered claim names
          2. Public claim names
          3. Private claim names
          4. Example claim
        4. Signature
          1. Asymmetric signature generation
          2. Symmetric signature generation
          3. Picking an algorithm
        5. A note on encryption
        6. Terminology and summary
      2. Responding with a token
        1. Adding E2E Tests
        2. Implementation
          1. Multiline environment variables
          2. Generating the token
      3. Attaching the token
        1. HTTP cookies
          1. Cross-Site Scripting (XSS)
          2. Cross-Site Request Forgery (XSRF)
        2. HTTP headers
          1. The Authorization header
        3. Writing tests
          1. Features and scenarios
          2. Implementation step definitions
        4. Verifying the digest in the request
    5. Next steps
      1. Preventing man-in-the-middle (MITM) attacks
      2. Encrypting digests
        1. Block cipher
      3. Exploring the Secure Remote Password (SRP) protocol
    6. Summary
  19. Documenting Our API
    1. Overview of OpenAPI and Swagger
      1. Picking an API specification language
      2. Swagger vs OpenAPI
      3. Swagger Toolchain
        1. Swagger Editor
        2. Swagger UI
        3. Swagger Inspector
        4. Swagger codegen
    2. Defining an API specification with OpenAPI
      1. Learning YAML
      2. An overview of the root fields
      3. Specifying the GET /salt endpoint
        1. Specifying parameters
        2. Specifying responses
      4. Specifying the Create User endpoint
        1. Specifying the request body
      5. Defining common components
      6. Specifying the Retrieve User endpoint
      7. Specifying the Replace Profile endpoint
      8. Specifying the rest of the endpoints
    3. Generating documentation with Swagger UI
      1. Adding the Swagger UI to our repository
      2. Using our specification in the Swagger UI
        1. Exposing swagger.yaml from our API
        2. Enabling CORS
          1. Same-origin policy
          2. Cross-Origin Resource Sharing (CORS)
        3. Final touches
          1. Replacing the specification URL
          2. Removing the header
    4. Deployment
    5. Summary
  20. Creating UI with React
    1. Picking a front-end framework/library
      1. Vanilla JavaScript vs. frameworks
      2. Choosing a framework/library
        1. Popularity/community
        2. Features
          1. Virtual DOM
          2. JSX
          3. Post-React
        3. Flexibility
        4. Performance
        5. Cross-platform
          1. Hybrid applications with Ionic
          2. Native UI with React Native and Weex
        6. Learning curve
        7. Conclusion
    2. Getting started with React
      1. What is React?
        1. Components
        2. Virtual DOM
          1. How Virtual DOM improves performance
        3. React is declarative
        4. React summary
      2. Starting a new repository
      3. Adding some boilerplate
      4. Creating our first component
      5. JSX
        1. Transpiling JSX
      6. Defining React components
        1. Functional and class components
        2. Pure components
      7. Maintaining the state and listening for events
        1. Handling events
        2. setState and immutability
        3. Rendering the state
      8. Submitting forms
        1. Uncontrolled form elements
          1. Resolving CORS issues
          2. Disabling the Button component
        2. Controlled form elements
    3. Modularizing React
      1. Client-side modules
        1. Module bundling
          1. Browserify
          2. Webpack
          3. Rollup
          4. Parcel
        2. Asynchronous module loading
          1. AMD and Require.js
          2. Universal Module Definition
          3. SystemJS and the Loader specification
          4. jspm
        3. Module bundler versus module loader
          1. HTTP/2
      2. Webpack
        1. Modularizing our components
        2. Entry/output
        3. Loaders
        4. Plugins
          1. Copying files
      3. Final steps
    4. Summary
  21. E2E Testing in React
    1. Testing strategies
      1. Automated UI testing
      2. Unit testing
        1. Logical units
        2. Component units
      3. Browser testing
    2. Writing E2E tests with Gherkin, Cucumber, and Selenium
      1. Adding test script
      2. Specifying a feature
      3. Adding IDs to elements
      4. Selenium
      5. WebDriver API
      6. Using Selenium WebDriver
      7. Headless browsers
      8. Browser drivers
      9. Setup and teardown
      10. Implementing step definitions
        1. Navigating to a page
        2. Typing into input
        3. Asserting a result
      11. Running the tests
      12. Adding multiple testing browsers
      13. Running our backend API
        1. Dynamic string substitution with Webpack
        2. Serving the API from a submodule
        3. Defining the happy scenario
        4. Generating random data
        5. Making step definitions more generic
        6. Clicking
        7. Waiting
        8. Render components based on state
    3. Routing with React Router
      1. Basics
        1. Router
        2. Route matching
        3. Supporting the History API
        4. Navigation
    4. TDD
      1. Login
        1. Writing tests
        2. Implementing Login
    5. Over to you
    6. Summary
  22. Managing States with Redux
    1. State management tools
      1. Redux
      2. MobX
      3. Redux versus MobX
    2. Converting to Redux
      1. Creating the store
      2. Lifting the state up
      3. Dispatching actions
      4. Updating the state with the Reducer
      5. Connecting with React Redux
      6. Wrapping with the Provider component
      7. Connecting to the Redux store
        1. mapStateToProps
        2. mapDispatchToProps
      8. Decoupling Redux from components
    3. Summary
  23. Migrating to Docker
    1. Problems with manual deployment
    2. Introduction to Docker
      1. What are containers?
      2. Workflow
      3. How does Docker solve our issues?
    3. Mechanics of Docker
      1. What is a Docker container?
        1. Control groups
        2. Namespaces
        3. LXC and Docker
        4. Virtual Machines
        5. Containers versus Virtual Machines
      2. What is a Docker image?
        1. Images are layered
        2. Running a container
        3. Setting up the Docker Toolchain
        4. Adding the Docker package repository
      3. Installing Docker
        1. Docker Engine, Daemon, and Client
        2. Running Elasticsearch on Docker 
        3. Running a container
      4. Understanding the docker run option
        1. Identifying a container by name 
        2. Setting environment variables
        3. Running as daemon
        4. Network port mapping
          1. 0.0.0.0
        5. Updating our test script
        6. Dockerizing our backend API
        7. Overview of a Dockerfile
      5. Writing our Dockerfile
        1. Picking a base image
        2. Copying project files
        3. Building our application
        4. Specifying the executable
      6. Building our image
        1. Running our image
        2. Persisting data
    4. Following best practices
      1. Shell versus exec forms
        1. Allowing Unix signaling
        2. Running as a non-root user
        3. Taking advantage of the cache
        4. Caveats
      2. Using a lighter image
      3. Removing obsolete files
      4. Multi-stage builds
      5. Security
    5. Summary
  24. Robust Infrastructure with Kubernetes
    1. High availability
      1. Measuring availability
      2. Following the industry standard
      3. Eliminating single points of failure (SPOF)
      4. Load balancing versus failover
      5. Load balancing
        1. DNS load balancing
        2. Layer 4/7 load balancers
          1. Layer 4 load balancers
          2. Layer 7 load balancing
    2. High reliability
      1. Testing for reliability
    3. High throughput
    4. High scalability
    5. Clusters and microservices
      1. Microservices
      2. Clusters
    6. Cluster management
      1. Cluster-level tools
        1. Discovery service
        2. Scheduler
        3. Global configuration store
        4. Provisioning tools
    7. Picking a cluster management tool
    8. Control Planes and components
      1. Master components
        1. kube-apiserver
        2. kube-control-manager
      2. Node components
        1. Container runtime
        2. kubelet
        3. kube-proxy
    9. Kubernetes objects
      1. The four basic objects
      2. High-level objects
      3. Controllers
    10. Setting up the local development environment
      1. Checking hardware requirements
      2. Cleaning our environment
      3. Disabling swap memory
      4. Installing kubectl
      5. Installing Minikube
      6. Installing a Hypervisor or Docker Machine
    11. Creating our cluster
      1. Setting environment variables for the local cluster
      2. Running minikube start
      3. Updating the context
      4. Resetting the cluster
    12. Creating our first Pod
      1. Running Pods with kubelet
      2. Running Pods with kubectl run
    13. Understanding high-level Kubernetes objects
    14. Declarative over imperative
      1. Deleting deployment
      2. Creating a deployment manifest
        1. A note on labels
      3. Running pods declaratively with kubectl apply
      4. Kubernetes Object management hierarchy
    15. Configuring Elasticsearch cluster
      1. Networking for distributed databases
      2. Configuring Elasticsearch's Zen discovery
        1. Attaching hostnames to Pods
        2. Working with StatefulSets
          1. Ordinal index
        3. Working with services
          1. Linking StatefulSet to a service
          2. Updating Zen Discovery configuration
      3. Validating Zen Discovery
    16. Deploying on cloud provider
      1. Creating a new remote cluster
        1. Switching contexts
      2. Configuring nodes for Elasticsearch
        1. Running commands on multiple servers
          1. Using pssh
          2. Using init containers
      3. Running the Elasticsearch service
        1. Validating Zen Discovery on the remote cluster
    17. Persisting data
      1. Introducing Kubernetes Volumes
        1. Defining Volumes
        2. Problems with manually-managed Volumes
    18. Introducing PersistentVolume (PV)
      1. Consuming PVs with PersistentVolumeClaim (PVC)
      2. Deleting a PersistentVolumeClaim
      3. Deleting a PersistentVolume
      4. Problems with manually provisioning PersistentVolume
    19. Dynamic volume provisioning with StorageClass
      1. Defining a StorageClass
        1. Using the csi-digitalocean provisioner
      2. Provisioning PersistentVolume to StatefulSet
        1. Configuring permissions on a bind-mounted directory
    20. Visualizing Kubernetes Objects using the Web UI Dashboard
      1. Launching the Web UI Dashboard locally
      2. Launching the Web UI Dashboard on a remote cluster
    21. Deploying the backend API
      1. Publishing our image to Docker Hub
      2. Creating a Deployment
        1. Discovering Services using kube-dns/CoreDNS
        2. Running Our backend Deployment
    22. Creating a backend Service
    23. Exposing services through Ingress
      1. Deploying the NGINX Ingress Controller
      2. Deploying the Ingress resource
      3. Updating DNS records
    24. Summary
  25. Other Books You May Enjoy
    1. Leave a review - let other readers know what you think