0Pricing
Swift Academy · Lesson

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 on Codable).
  • Return content to encode JSON; use req.content.decode to read bodies.
  • Customize keys/dates via CodingKeys and ContentConfiguration.
  • Validate input with Validatable and 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

  1. Routing and Request Handling
  2. Content and JSON Encoding
  3. Fluent ORM and Models
  4. Middleware and Authentication
← Back to Swift Academy