Full Stack Application Development

Chapter 7
Server-Side Application Development

Certificate in Full stack programming
Full Stack Application Development   •   Certificate

Server-Side Application Development

Introduction

Server-side application development concerns the software that runs on a server and responds to requests from browsers, mobile applications, and other services. Although users interact with a visible client interface, many essential activities occur on the server: authenticating users, validating input, applying business rules, reading and writing data, integrating external services, and returning appropriate responses.

A modern server application must do more than “receive a request and send a response.” It should be modular, secure, testable, observable, and maintainable. These qualities are achieved through deliberate use of routing, middleware, configuration management, validation, logging, error handling, dependency management, and layered design.

This chapter uses examples based on a typical JavaScript runtime and web framework. The principles also apply to other ecosystems such as Python, Java, C#, PHP, and Go.

Learning Objectives

By the end of this chapter, you should be able to:

  • explain the request–response lifecycle of a server application;
  • design routes and organize them into modules;
  • distinguish application, router, and route middleware;
  • manage configuration and secrets safely;
  • validate and normalize incoming data;
  • implement structured logging and reliable error handling;
  • explain dependency management and inversion of control;
  • apply layered architecture to a small server application;
  • identify security, reliability, and governance considerations; and
  • evaluate a server-side design using a practical case.

1. The Server-Side Request Lifecycle

A server application normally follows a pipeline. A client sends an HTTP request containing a method, URL, headers, and possibly a body. The server selects a route, executes middleware, invokes application logic, accesses data or external services, and creates an HTTP response.

The principal HTTP methods are:

MethodTypical purposeCommon property
GETRetrieve a resourceShould not change server state
POSTCreate a resource or perform an operationUsually not idempotent
PUTReplace a resourceIntended to be idempotent
PATCHPartially update a resourceMay be idempotent depending on design
DELETERemove a resourceIntended to be idempotent

A route is the combination of an HTTP method and a path. For example, GET /api/products/42 may retrieve product 42. The path identifies the resource, while the method communicates the intended operation.

A useful conceptual pipeline is:

Request → middleware → route matching → validation → controller → service → repository → response

The pipeline is not necessarily linear in implementation. Middleware may terminate a request early, services may call external systems, and error handlers may process failures generated at any stage. Nevertheless, the model helps developers assign responsibilities clearly.

HTTP Responses

A response includes a status code, headers, and an optional body. Common status codes include:

  • 200 OK: successful retrieval or operation;
  • 201 Created: successful creation;
  • 204 No Content: successful operation with no response body;
  • 400 Bad Request: malformed or invalid client input;
  • 401 Unauthorized: authentication is required or failed;
  • 403 Forbidden: the identity is known but lacks permission;
  • 404 Not Found: the requested resource does not exist;
  • 409 Conflict: the request conflicts with current state; and
  • 500 Internal Server Error: an unexpected server failure.

Status codes should communicate a stable contract. Returning 200 for every outcome makes client development, monitoring, and debugging more difficult.

2. Routing and Modular Organization

Routing maps requests to handlers. A small application can define routes in one file, but a growing application should divide them by feature or resource. For example:

// routes/productRoutes.js
router.get('/', productController.list);
router.get('/:id', productController.getById);
router.post('/', validate(createProductSchema), productController.create);
router.delete('/:id', productController.remove);

The application can mount this router under a common prefix:

app.use('/api/products', productRoutes);

The resulting endpoints include GET /api/products and GET /api/products/:id.

Resource-Oriented Route Design

Route names should represent resources rather than implementation details. Prefer /api/orders/123 to /api/getOrderById?id=123. Query parameters are useful for filtering, sorting, and pagination, such as /api/products?category=books&page=2.

A route should generally perform four tasks:

  1. identify the operation;
  2. apply relevant middleware;
  3. invoke a controller; and
  4. return a consistent response.

Complex business rules should not be placed directly in route declarations. This makes routes difficult to test and encourages duplicated logic.

Controllers and Services

A controller adapts HTTP concerns to application concerns. It reads parameters and validated input, calls a service, and formats the response. A service represents business use cases. For example, OrderService.placeOrder() may check inventory, calculate totals, save an order, and publish an event.

This separation is important because business logic may later be reused by a command-line tool, scheduled job, or message consumer that does not use HTTP.

3. Middleware

Middleware is a function that participates in request processing. It may inspect or modify the request, produce a response, or pass control to the next function.

Typical middleware includes:

  • request parsing, such as JSON body parsing;
  • authentication and authorization;
  • request identification;
  • rate limiting;
  • input validation;
  • logging;
  • response compression; and
  • centralized error handling.

Middleware order matters. Authentication must run before protected controllers. Error middleware must be registered in the framework’s expected position. A body parser must run before code that reads the parsed body.

A simple middleware example is:

function requestId(req, res, next) {
  req.requestId = crypto.randomUUID();
  res.setHeader('X-Request-Id', req.requestId);
  next();
}

Middleware should have one clear responsibility. A function that authenticates users, writes audit records, transforms data, and handles exceptions is difficult to reason about and test.

Authentication and Authorization

Authentication answers “Who is the caller?” Authorization answers “What may that caller do?” These are different decisions. A valid access token may identify a user, but the user may still lack permission to delete a particular record.

Authorization can be role-based, attribute-based, or resource-based. For example, a rule may allow a user to edit an invoice only if the user belongs to the owning organization. Authorization checks should occur on the server even when the client interface hides unauthorized controls.

4. Configuration and Environment Management

Configuration consists of values that vary between environments or deployments: database URLs, port numbers, feature flags, email providers, and token settings. Configuration should not be scattered throughout source code.

A common approach is to read environment variables at startup and convert them into a validated configuration object:

const config = {
  port: Number(process.env.PORT || 3000),
  databaseUrl: process.env.DATABASE_URL,
  environment: process.env.NODE_ENV || 'development'
};

if (!config.databaseUrl) {
  throw new Error('DATABASE_URL is required');
}

Secrets such as passwords, private keys, and API tokens should not be committed to a source repository. Development values may be stored in a local environment file excluded from version control; production secrets should normally be supplied through a secret manager or protected deployment system.

Configuration has three important properties:

  • centralization: values are defined and accessed consistently;
  • validation: invalid values cause an early, understandable failure; and
  • separation: code is separated from environment-specific settings.

A server should fail fast when essential configuration is missing. Starting in a partially configured state can produce obscure failures later and may expose unsafe defaults.

5. Validation, Normalization, and Trust Boundaries

All client input is untrusted, including input from authenticated users. Validation establishes whether data has the expected shape, type, range, and relationship between fields.

For example, a product creation request might require:

{
  "name": "Notebook",
  "price": 4.50,
  "stock": 20
}

Validation rules could require a non-empty name, a non-negative price, and an integer stock quantity. Validation should occur near the boundary of the application, before business logic or database operations.

Validation and sanitization are related but distinct. Validation rejects unacceptable input. Normalization transforms acceptable input into a consistent representation, such as trimming whitespace or converting an email address to lowercase. Sanitization may remove or neutralize dangerous content, but it must not be treated as a substitute for context-specific output encoding.

Different contexts require different protections:

  • parameterized queries protect database operations from injection;
  • output encoding protects HTML contexts from cross-site scripting;
  • schema validation protects application contracts; and
  • allowlists are generally safer than attempting to block every harmful value.

Validation errors should be clear to clients but should not disclose internal implementation details. A useful response may identify fields and explain expected formats without revealing database structure or stack traces.

6. Logging and Observability

Logging records events that help developers and operators understand system behavior. Useful server logs normally include a timestamp, severity, event name, request identifier, and relevant context.

Log levels commonly include:

LevelAppropriate use
ERRORA failure requiring investigation or recovery
WARNAn unusual condition that may require attention
INFOSignificant normal application events
DEBUGDetailed information used during development or diagnosis

Structured logs, often represented as JSON, are easier for monitoring systems to search than unstructured sentences. A log event might contain requestId, userId, route, durationMs, and statusCode.

Logging must be designed with privacy in mind. Passwords, access tokens, payment card data, and unnecessary personal information should not be logged. Logs may be retained, copied, and viewed by personnel beyond the development team; therefore, logging is also a governance concern.

Observability is broader than logging. It includes:

  • logs, which describe events;
  • metrics, which quantify behavior, such as request rate and error rate; and
  • traces, which follow a request across services.

A useful operational measure is the error rate:

Error rate = Number of failed requests ÷ Total number of requests

The formula is meaningful only when “failed” is defined consistently, the same time interval is used for numerator and denominator, and automated health checks are treated appropriately. If 25 of 1,000 requests fail during an interval, the error rate is 25 ÷ 1,000 = 0.025, or 2.5%.

7. Error Handling

Failures are inevitable. A robust application distinguishes expected operational errors from programming defects.

Expected errors include invalid input, missing resources, duplicate records, and unavailable third-party services. These should be converted into deliberate responses. Unexpected errors should be logged with diagnostic context and returned to clients as a generic server error.

Centralized error handling prevents every controller from inventing its own response format. A consistent error body might be:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The request contains invalid fields",
    "requestId": "7b2..."
  }
}

The client receives useful information while internal stack traces remain in protected logs.

Error handling should also consider recovery. A timeout when calling a payment provider may be retried in some circumstances, but retrying a request that may already have succeeded can create duplicate charges. Idempotency keys, transactional design, and carefully selected retry policies are therefore important.

8. Dependency Management and Inversion of Control

A dependency is a component required by another component. Examples include database clients, email services, cryptographic libraries, and configuration providers.

Dependency management has two dimensions. First, a package manager installs and records external libraries, including versions. Lock files make installations reproducible. Second, application design determines how components receive the objects they need.

Consider this tightly coupled service:

class UserService {
  constructor() {
    this.repository = new PostgresUserRepository(process.env.DATABASE_URL);
  }
}

Testing this class requires a real database or complicated interception. Dependency injection improves the design:

class UserService {
  constructor(userRepository, mailer) {
    this.userRepository = userRepository;
    this.mailer = mailer;
  }
}

Production code can provide real implementations, while tests can provide fakes. This is inversion of control: the class does not construct every dependency itself; an external composition root supplies them.

Dependencies should be selected carefully. Teams should review maintenance activity, licensing, security advisories, transitive dependencies, and whether a small internal function would be safer than a large package. Updating dependencies is a continuing maintenance activity, not a task reserved for project completion.

9. Layered Architecture

Layered architecture separates responsibilities into cooperating levels. A practical server application may contain:

LayerResponsibility
PresentationHTTP routes, controllers, serializers
Application/serviceUse cases and coordination
DomainBusiness rules and domain concepts
InfrastructureDatabases, queues, files, external APIs

The presentation layer should know about HTTP. The infrastructure layer should know about database protocols. The service and domain layers should avoid unnecessary dependence on either.

A request to create an order might proceed as follows:

  1. The route receives POST /orders.
  2. Middleware authenticates the caller and validates the body.
  3. The controller invokes orderService.createOrder().
  4. The service checks customer eligibility and inventory.
  5. A repository stores the order.
  6. The controller returns 201 Created.

Layering is not a rule that every project must contain four directories. It is a way to manage change. For a small application, strict separation may add unnecessary complexity. For a larger application, failing to separate responsibilities causes fragile code, duplicated rules, and difficult testing.

10. Testing Server Applications

Testing should cover both individual components and complete request behavior.

  • Unit tests test a function or class in isolation, often using test doubles.
  • Integration tests test cooperation with a database or other real component.
  • End-to-end tests exercise the application through an interface close to that used by a real client.

A useful test set includes successful requests, invalid input, missing resources, unauthorized access, downstream failures, and boundary conditions. Tests should verify observable behavior rather than private implementation details.

For example, a service test might verify that an order cannot be placed when inventory is insufficient. A route integration test might verify that invalid JSON produces a 400 response and a stable error structure.

11. Security, Reliability, and Governance

Server-side development has direct security implications. Fundamental practices include:

  • use secure transport such as HTTPS in production;
  • hash passwords with a modern adaptive password-hashing algorithm;
  • apply least privilege to database and service accounts;
  • validate input and use parameterized database queries;
  • configure secure cookies and appropriate cross-origin policies;
  • limit request size and rate where abuse is possible;
  • patch dependencies and monitor vulnerability advisories; and
  • avoid revealing secrets or stack traces in responses.

Reliability also depends on timeouts, graceful shutdown, health checks, database connection management, and controlled concurrency. A server should stop accepting new work during shutdown, complete safe in-flight work where possible, and release resources.

Governance includes access control, auditability, retention, privacy, and compliance with applicable law and organizational policy. Developers should know what personal data the system processes, why it is collected, how long it is retained, and who can access it.

Artificial intelligence may assist with code generation, test creation, log analysis, and documentation. However, generated code must be reviewed for security defects, incorrect assumptions, licensing concerns, and inappropriate data handling. AI tools should not receive confidential production data unless an approved governance process permits it.

12. Illustrative Case: Community Library API

Background

A community library wants an API for searching books, registering members, and recording loans. An initial prototype places database queries directly in route handlers and returns inconsistent error messages.

Problem

As usage increases, several issues appear: invalid dates reach the database, database errors are exposed to clients, tests require a live database, and staff cannot determine which requests caused failures.

Analysis

The prototype lacks clear boundaries. Routes contain business logic, configuration is read throughout the codebase, and no request identifier connects a client report to server logs. The API also trusts client input and does not distinguish authentication from authorization.

Approach and Solution

The team reorganizes the application into feature routers, controllers, services, repositories, and configuration modules. A validation middleware checks member identifiers and loan dates before the service layer runs. The service enforces the rule that a member cannot exceed the allowed number of active loans.

A repository interface isolates database operations. Unit tests inject an in-memory repository, while integration tests use a temporary database. Centralized error middleware maps validation failures to 400, missing books to 404, and unexpected failures to 500.

Structured logging records request identifiers, route names, status codes, and elapsed time, while excluding member passwords and access tokens. Configuration is loaded once at startup, and database credentials are supplied through the deployment environment.

Outcome

The API becomes easier to test and operate. Developers can change the database implementation without rewriting controllers. Operators can trace a reported request, and clients receive predictable responses. The architecture does not eliminate defects, but it makes defects easier to detect, localize, and correct.

Learning Points

The case demonstrates that modularity is not merely a matter of file organization. It is a method for separating responsibilities, reducing coupling, enforcing trust boundaries, and improving operational visibility.

13. Best Practices Checklist

Before releasing a server application, confirm that:

  • routes use clear resource-oriented names;
  • controllers are thin and business logic is placed in services or domain objects;
  • middleware order is deliberate;
  • all external input is validated;
  • configuration is centralized and required settings are checked at startup;
  • secrets are excluded from source control and logs;
  • errors have consistent client-safe responses;
  • logs contain useful, non-sensitive context;
  • dependencies are locked, reviewed, and updated;
  • database access uses safe queries and appropriate transactions;
  • authentication and authorization are separately designed;
  • tests cover success, failure, and security-relevant cases; and
  • shutdown, health, timeout, and monitoring behavior are defined.

Chapter Summary

Server-side application development creates the reliable processing layer behind client applications. Its request–response lifecycle combines routing, middleware, validation, controllers, services, repositories, and response handling. Modular routes clarify public interfaces, while middleware provides reusable cross-cutting behavior such as authentication, logging, and request parsing.

Configuration should be centralized, validated, and separated from source code; secrets must be protected. Validation establishes a boundary between untrusted input and internal logic. Structured logging, metrics, traces, and centralized error handling improve observability and operational recovery. Dependency injection reduces coupling and supports testing. Layered architecture separates presentation, application, domain, and infrastructure responsibilities.

Secure and professional server applications also require least privilege, safe database access, dependency review, privacy-aware logging, controlled retries, reliable shutdown, and appropriate governance. The central design principle is separation of concerns: each component should have a clear responsibility and a clear contract with the components around it.

Key Terms

Routing: Mapping an HTTP method and path to application behavior.

Middleware: A function that participates in request processing before or after a route handler.

Controller: A component that adapts HTTP requests and responses to application services.

Service: A component that coordinates a business use case.

Repository: An abstraction for storing and retrieving domain data.

Validation: Checking whether input satisfies defined structural and business constraints.

Normalization: Converting valid input into a consistent representation.

Authentication: Establishing the identity of a caller.

Authorization: Determining what an authenticated caller is permitted to do.

Configuration: Environment-dependent settings used by an application.

Secret: Sensitive configuration data such as a password, private key, or access token.

Structured logging: Recording events in machine-readable fields.

Observability: The ability to understand system behavior through logs, metrics, and traces.

Dependency injection: Supplying dependencies from outside a component rather than constructing them internally.

Layered architecture: Separating software responsibilities into cooperating architectural layers.

Idempotency: A property whereby repeating an operation has the same intended effect as performing it once.

Timeout: A limit on how long an operation may wait before failing.

Least privilege: Granting only the access required to perform a task.

Trust boundary: A point where data enters a system and must be treated as untrusted.

Review Questions

Short Answer Questions

  1. What is the purpose of middleware in a server application?
  2. Why should configuration be centralized and validated at startup?
  3. Distinguish authentication from authorization.
  4. What responsibilities normally belong to a controller?
  5. Why should sensitive information be excluded from logs?

Long Answer Questions

  1. Explain the request–response lifecycle from client request to server response, using routing, middleware, validation, services, and repositories in your explanation.
  2. Describe layered architecture and explain how it improves maintainability and testing.
  3. Compare unit, integration, and end-to-end testing for a server application.
  4. Explain how centralized error handling improves both client experience and operational diagnosis.
  5. Discuss the security and governance responsibilities involved in developing a server-side application.

Analytical / Application Questions

  1. A route handler contains authentication, validation, database queries, email delivery, and response formatting. Analyze the design problems and propose a modular decomposition.
  2. An API sometimes creates duplicate payments after a network timeout. Explain why a simple retry may be dangerous and propose design controls.
  3. Design the main layers, routes, middleware, and error responses for a small appointment-booking API. Justify the order in which requests should pass through the system.

Multiple Choice Questions with Answers

Question 1

Which component should normally contain business use-case coordination?

A. The service layer

B. The CSS stylesheet

C. The DNS resolver

D. The browser cache

Correct Answer: A. The service layer

Question 2

What is the main purpose of authentication?

A. To determine a user’s permissions

B. To establish the identity of a caller

C. To format a JSON response

D. To select a database index

Correct Answer: B. To establish the identity of a caller

Question 3

Which status code most appropriately represents successful resource creation?

A. 200

B. 204

C. 201

D. 404

Correct Answer: C. 201

Question 4

Why are environment variables commonly used for production secrets?

A. They make passwords unnecessary

B. They prevent all application errors

C. They separate sensitive deployment values from source code

D. They automatically encrypt every database

Correct Answer: C. They separate sensitive deployment values from source code

Question 5

If 25 of 1,000 requests fail, what is the error rate?

A. 0.25%

B. 2.5%

C. 25%

D. 40%

Correct Answer: B. 2.5%

Question 6

Which practice best protects database queries from injection?

A. Using parameterized queries

B. Logging every password

C. Returning stack traces

D. Disabling validation

Correct Answer: A. Using parameterized queries

Question 7

What does dependency injection primarily improve?

A. Coupling and testability

B. Screen resolution

C. Network bandwidth automatically

D. The number of HTTP methods

Correct Answer: A. Coupling and testability

Question 8

Which log level is generally appropriate for an unexpected failure requiring investigation?

A. DEBUG

B. INFO

C. WARN

D. ERROR

Correct Answer: D. ERROR

Question 9

What is the principal responsibility of authorization?

A. Determining what an identified caller may do

B. Parsing JSON syntax

C. Assigning a server port

D. Compressing images

Correct Answer: A. Determining what an identified caller may do

Question 10

Why should middleware order be designed deliberately?

A. Middleware execution can affect whether later processing is possible

B. HTTP methods otherwise disappear

C. It changes the programming language

D. It removes the need for testing

Correct Answer: A. Middleware execution can affect whether later processing is possible

Full Stack Application Development
Chapter 7 of 14