In the previous blogs, we've talked about HTTP, requests and responses, and how routing helps a backend decide where an incoming request should go.
But there's another interesting question here.
Let's say our frontend is written in JavaScript and our backend is written in Rust.
The frontend has this object:
const jedi = {
name: "Obi-Wan Kenobi",
rank: "Master",
active: true
};
Great.
But how exactly do we send this JavaScript object to a server written in Rust?
Rust doesn't know what a JavaScript object is.
And JavaScript certainly doesn't know what a Rust struct is.
That's the problem serialization and deserialization solve.
1. The Language Barrier
A typical web application can have completely different technologies running on either side.
For example:
Frontend
JavaScript
↓
???
↓
Backend
Rust
JavaScript is dynamically typed, while Rust is statically typed and compiled.
Both languages have their own ways of representing objects, strings, arrays, numbers, and other data structures in memory. So we can't simply take a JavaScript object from one process and magically place it inside a Rust program running on another machine.
We need some common representation that both sides understand.
Something like:
JavaScript Object
↓
Common Format
↓
Rust Struct
This common format acts as the language both applications agree to speak over the network.
2. Serialization and Deserialization
This brings us to two important terms.
Serialization
Serialization is the process of converting data from our application's native representation into a format suitable for storage or transmission.
For example, our JavaScript object:
const jedi = {
name: "Obi-Wan Kenobi",
rank: "Master"
};
can be serialized into JSON:
{
"name": "Obi-Wan Kenobi",
"rank": "Master"
}
Deserialization
Deserialization is the reverse process.
The backend receives the data and converts it into something its own programming language can work with.
For example:
JSON
↓
Deserialize
↓
Rust Struct
Now the Rust application can access fields like name and rank and perform whatever business logic it needs.
So our communication becomes:
JavaScript Object
↓
Serialization
↓
JSON
↓
Network
↓
JSON
↓
Deserialization
↓
Rust Struct
Neither side needs to understand the other's internal representation.
They only need to agree on the format and structure of the data being exchanged.
That's what makes communication between systems written in completely different technologies possible.
3. Serialization Formats
JSON isn't the only format we can use. Serialization formats broadly come in two flavours.
Text-Based Formats
These represent data as text and are generally easier for humans to inspect.
Some common examples are:
JSON
XML
YAML
Binary Formats
Other serialization formats encode data into a compact binary representation.
Some common examples include:
Protocol Buffers (Protobuf)
Apache Avro
MessagePack
Binary formats can often provide smaller payloads and efficient serialization/deserialization, although the exact trade-offs depend on the format and use case.
For most traditional REST APIs, however, we're going to encounter JSON very frequently.
So let's look at it a little more closely.
4. JSON
JSON stands for JavaScript Object Notation.
Despite having JavaScript in its name, JSON isn't limited to JavaScript. Almost every mainstream programming language has libraries for reading and writing JSON. That's one of the reasons it works so well for communication between different systems. It's also human-readable.
If an API sends:
{
"order": 66,
"status": "executed",
"priority": "high"
}
you can immediately understand the structure without needing a special tool to decode it.
JSON Syntax
JSON has a small set of supported value types:
Strings
Numbers
Booleans
Objects
Arrays
null
For example:
{
"name": "Luke Skywalker",
"age": 23,
"jedi": true,
"master": null,
"skills": ["piloting", "lightsaber"]
}
There are also some strict syntax rules.
Object keys must be strings wrapped in double quotes:
{
"name": "Luke"
}
Strings also use double quotes.
And JSON doesn't support things like JavaScript functions, undefined, or comments.
So while JSON looks very similar to a JavaScript object, they're not exactly the same thing.
That's an important distinction.
5. What Happens Over the Network?
Now let's connect this to what we've learned about HTTP.
Suppose our frontend wants to create a new Jedi.
It might send:
POST /api/jedi HTTP/1.1
Content-Type: application/json
with:
{
"name": "Ahsoka Tano",
"rank": "Citizen"
}
The Content-Type header tells the server:
The data I'm sending you is JSON.
Our application serializes the data into JSON text, which is encoded into bytes and placed in the HTTP request body.
From our perspective as backend engineers, we can mostly work at this application layer.
Underneath HTTP, several other networking layers take care of actually transporting those bytes.
Conceptually:
Application Data
↓
HTTP
↓
Transport
↓
IP
↓
Network / Link
↓
Physical Transmission
Depending on the protocol stack, the data may be split into transport segments and eventually represented as signals across the physical medium.
At the destination, those layers perform the corresponding work to deliver the byte stream or application data back up to the server's HTTP stack.
Thankfully, when writing a normal backend endpoint, we don't need to manually deal with any of that.
Our framework usually gives us something much simpler:
Incoming HTTP Request
↓
Request Body
↓
JSON Parser
↓
Native Data Structure
That's the abstraction we work with.
6. The Complete Flow
Now we can put everything together.
Imagine a user submits a form on our frontend.
Step 1: Client prepares the data
JavaScript creates an object:
const data = {
name: "Ahsoka",
affiliation: "Rebel Alliance"
};
Step 2: Client serializes it
The object is converted into JSON
which gives us something equivalent to:
{
"name": "Ahsoka",
"affiliation": "Rebel Alliance"
}
Step 3: HTTP sends the data
The serialized data is included in the HTTP request body:
Client
↓
HTTP Request
↓
Network
↓
Server
Step 4: Server deserializes it
The backend parses the JSON into its own native representation.
JSON
↓
Rust Struct
Step 5: Server performs business logic
Now the backend can:
Validate Data
↓
Apply Business Logic
↓
Save to Database
Maybe it stores our new Jedi.
Maybe it rejects the request.
Maybe the Council needs to approve it first.
That's outside the scope of serialization.
Step 6: Server serializes the response
The backend prepares a response using its own native data structures and serializes that response back into JSON.
{
"success": true,
"message": "Jedi registered"
}
Step 7: Client deserializes the response
The frontend receives the JSON response and parses it back into something JavaScript understands.
Server Data
↓
Serialize
↓
JSON
↓
Network
↓
JSON
↓
Deserialize
↓
JavaScript Object
And that's the complete cycle.
Wrapping it up
Serialization sounds complicated when you first hear the term, but the idea behind it is actually pretty simple.
Two applications might be written in completely different languages:
JavaScript Rust
↓ ↑
Serialize Deserialize
↓ ↑
Common Format
JSON
Neither side needs to understand how the other represents data internally. They simply agree on a format for exchanging it.
And when we're working with typical REST APIs, that format is very often JSON.
A smaller topic than the previous blogs, but an important piece of understanding how data actually moves between our applications.
Thanks for reading, and may your payloads always deserialize successfully.
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.