Content and JSON Encoding
Decode and encode request and response bodies.
Content and JSON Encoding is a free Swift Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Content Protocol
Vapor models that travel over HTTP conform to Content. Content builds on Swift's Codable, adding the ability to be decoded from request bodies and encoded into responses automatically.
import Vapor
struct Todo: Content {
var id: Int?
var title: String
var done: Bool
}Encoding a Response as JSON
Return any Content value from a handler and Vapor encodes it to JSON, setting the correct Content-Type header for you.
app.get("todo") { req -> Todo in
Todo(id: 1, title: "Learn Vapor", done: false)
}Decoding a Request Body
Read a typed payload from an incoming request with req.content.decode(_:). Vapor inspects the Content-Type and parses JSON (or form data) accordingly.
app.post("todo") { req -> Todo in
let incoming = try req.content.decode(Todo.self)
return incoming
}Arrays as Content
Collections of Content types encode to JSON arrays automatically. Return [Todo] and the client receives a JSON list.
app.get("todos") { req -> [Todo] in
[Todo(id: 1, title: "A", done: false),
Todo(id: 2, title: "B", done: true)]
}Custom Coding Keys
Because Content is Codable, you can map Swift property names to different JSON keys using CodingKeys — useful for snake_case APIs.
struct User: Content {
var firstName: String
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
}
}Configuring the JSON Encoder
You can set a global encoding strategy, for example converting all keys to snake_case or formatting dates as ISO-8601, via ContentConfiguration.
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.dateEncodingStrategy = .iso8601
ContentConfiguration.global.use(encoder: encoder, for: .json)Validating Decoded Content
Vapor's Validatable protocol lets you declare validation rules. Call try Todo.validate(content: req) before decoding to reject bad input with a clear 400 error.
extension Todo: Validatable {
static func validations(_ v: inout Validations) {
v.add("title", as: String.self, is: !.empty)
}
}Using Validation in a Handler
Validate first, then decode. If validation fails, Vapor throws automatically and the client receives a descriptive error response.
app.post("todo") { req -> Todo in
try Todo.validate(content: req)
return try req.content.decode(Todo.self)
}Separate Request and Response DTOs
A good practice is to keep distinct types for input and output. For example a CreateTodo with no id for requests, and a full Todo for responses. This decouples your API from internal models.
struct CreateTodo: Content {
var title: String
}
struct TodoResponse: Content {
var id: Int
var title: String
}Encoding Query Strings
The same Content mechanism decodes query strings into a struct via req.query.decode(_:), ideal for filter/pagination parameters.
struct Page: Content {
var page: Int?
var size: Int?
}
app.get("items") { req -> String in
let p = try req.query.decode(Page.self)
return "page=" + String(p.page ?? 1)
}Returning Custom Status with Content
To control both body and status, build a Response and encode content into it, or return a tuple-like construct. Below sets 201 Created with a JSON body.
app.post("todo") { req -> Response in
let todo = try req.content.decode(Todo.self)
let res = Response(status: .created)
try res.content.encode(todo)
return res
}Quick Check: Content and JSON
Test your encoding knowledge.
Recap: Content and JSON Encoding
You learned how data crosses the wire in Vapor:
- Conform models to
Content(built onCodable). - Return content to encode JSON; use
req.content.decodeto read bodies. - Customize keys/dates via
CodingKeysandContentConfiguration. - Validate input with
Validatableand consider separate request/response DTOs.
Frequently asked questions
Is the “Content and JSON Encoding” lesson free?
Yes — the full text of “Content and JSON Encoding” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Content and JSON Encoding”?
Decode and encode request and response bodies. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Content and JSON Encoding” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Routing and Request Handling
- Content and JSON Encoding
- Fluent ORM and Models
- Middleware and Authentication