
Building Enterprise JavaScript Applications
Description
Alles über E-Books | Antworten auf Fragen rund um E-Books, Kopierschutz und Dateiformate finden Sie in unserem Info- & Hilfebereich.
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.
More details
Other editions
Additional editions

Content
- Cover
- Title Page
- Copyright and Credits
- Dedication
- Packt Upsell
- Contributors
- Table of Contents
- Preface
- Chapter 1: 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
- Chapter 2: 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
- Chapter 3: 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
- Chapter 4: 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
- Chapter 5: 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
- Chapter 6: 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
- Chapter 7: 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
- Chapter 8: 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
- Chapter 9: 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
- Chapter 10: 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
- Chapter 11: 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
- Chapter 12: 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
- Chapter 13: 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
- Chapter 14: 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
- Chapter 15: 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
- Chapter 16: 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
- Chapter 17: 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
- Chapter 18: 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
- Index
System requirements
File format: ePUB
Copy protection: Adobe-DRM (Digital Rights Management)
System requirements:
- Computer (Windows; MacOS X; Linux): Install the free reader Adobe Digital Editions prior to download (see eBook Help).
- Tablet/smartphone (Android; iOS): Install the free app Adobe Digital Editions or the app PocketBook before downloading (see eBook Help).
- E-reader: Bookeen, Kobo, Pocketbook, Sony, Tolino and many more (not Kindle).
The file format ePub works well for novels and non-fiction books – i.e., „flowing” text without complex layout. On an e-reader or smartphone, line and page breaks automatically adjust to fit the small displays.
This eBook uses Adobe-DRM, a „hard” copy protection. If the necessary requirements are not met, unfortunately you will not be able to open the eBook. You will therefore need to prepare your reading hardware before downloading.
Please note: We strongly recommend that you authorise using your personal Adobe ID after installation of any reading software.
For more information, see our ebook Help page.