In a typical client-server architecture, a client application—such as a web browser running Chrome—communicates with a server hosted locally or remotely in the cloud (AWS, GCP, Azure, etc.). This communication occurs over a network using protocols such as HTTP (traditional REST APIs), gRPC, or WebSockets. Throughout this series, we focus on HTTP-based REST API communication.
Consider a client application built with a JavaScript framework like React, Angular, or Vue. When this client sends an HTTP request to the server, the request follows a standard structure: a method (GET, POST, etc.), a URL, headers, and optionally a request body. The server processes this request and returns a response, which the client parses and uses to update the UI or execute business logic.
The central challenge is this: how do clients and servers, potentially built with entirely different programming languages, exchange data in a way that both can understand? For instance, a JavaScript client and a Rust server operate with fundamentally different type systems. JavaScript is dynamically typed and interpreted, while Rust enforces strict static typing and compilation. When a JavaScript object is sent over the network, how does the Rust server interpret it correctly? How does the server’s response translate back into something the JavaScript client can use?
The Need for a Common Standard
To understand data transmission between machines, a high-level familiarity with the OSI (Open Systems Interconnection) model is helpful. The OSI model describes how data moves through different network layers—from the application layer at the top to the physical layer at the bottom. Both client and server implement these layers. While a deep understanding of IP packets, data frames, and lower-level protocols is not necessary for backend development, recognizing that data passes through multiple transformation stages during transmission is important.
The solution to the interoperability problem is straightforward: establish a common standard. Both client and server agree on a shared data format. The client converts its internal data structures into this standard format before transmission. Upon receiving the data, the server converts it from the standard format into its own native types (e.g., Rust structs), processes it, and then converts the response back into the standard format before sending it to the client. The client receives this response, parses it back into JavaScript objects, and continues execution.
Serialization and deserialization is the process of converting data to and from a common format so that machines using different languages and environments can exchange information during transmission or storage.
Choosing Technologies
Backend engineering encompasses hundreds of technologies, making it impossible to master them all. This series focuses on the most widely adopted tools. For network communication, we use HTTP and REST APIs, as they remain the dominant approach for client-server interaction. For databases, we use PostgreSQL, one of the most popular relational databases and a common first choice for both startups and enterprises. Similarly, for serialization, we focus on the most prevalent standard.
Serialization Standards
Serialization standards fall into two main categories.
Text-based formats include JSON, YAML, and XML. These formats are human-readable and easy to debug.
Binary formats include Protocol Buffers (protobuf), Avro, and others. These formats are more compact and efficient but not human-readable.
For HTTP-based REST API communication, JSON (JavaScript Object Notation) is the overwhelmingly dominant choice. While exact statistics vary, JSON is used in the vast majority of modern web APIs.
JSON Structure
JSON is human-readable by design. Despite its name suggesting a connection to JavaScript, JSON is language-agnostic and appears in configuration files, application logs, and data transmission across many different systems.
A JSON object consists of key-value pairs enclosed in curly braces. Keys must always be strings enclosed in double quotes. Values can be strings (in double quotes), numbers, booleans (true or false), arrays (enclosed in square brackets), or nested objects (following the same structure rules). For example:
{
"name": "Alice",
"age": 30,
"active": true,
"address": {
"country": "India",
"phone": 123456
}
}In this structure, "address" is a nested object containing its own key-value pairs. Arrays can hold multiple values of any valid JSON type. This simplicity and flexibility make JSON ideal for data exchange.
Data Transmission in Practice
When a client sends an HTTP POST request to a server, the request body contains JSON-formatted data. Although the OSI model describes how this data transforms through multiple network layers—eventually becoming bits transmitted as electrical or optical signals—backend engineers primarily work at the application layer. The mental model is straightforward: the client serializes its data into JSON, the data travels through the network (undergoing various transformations at lower layers), and the server receives JSON at its application layer. The intermediate transformations are handled automatically by network infrastructure and are not the backend engineer’s concern.
For example, when a client sends:
{
"id": 1,
"title": "Learning Backend",
"author": "John Doe"
}The server receives this exact JSON structure, parses it into its native data types, processes the request (perhaps adding the book to a database), and responds with JSON:
{
"books": [
{
"id": 1,
"title": "Learning Backend",
"author": "John Doe"
}
]
}The client receives this response, parses it back into JavaScript objects, and uses the data to update the user interface.
This entire flow—converting data into JSON for transmission, parsing it on the receiving end, processing it, and converting the response back into JSON—represents serialization and deserialization in action. The technique ensures that applications written in different languages can communicate effectively by adhering to a shared, standardized format that is understandable across domains and programming environments.