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