The One-File API
The One-File API: an endpoint is a contract wearing HTTP. Declare the capability once and the rest is derived - the 422s (with relation rules explained), the 201, and the machine-readable schema your client generator reads. No serializer classes, no validator classes, no docs pipeline. One declaration, three doors.
Data & Pipelines
Round 10
DHH
exit 0
bundle exec ruby examples/one_file_api.rb
a real captured run
THE ONE-FILE API (quotes v3.0.0)
GET /quotes/schema
-> 200 {"$schema":"http://json-schema.org/draft-07/schema#","title":"quotes inputs","type":"object","requir... (687 bytes)
POST /quotes {"mode":"teleport","weight":9000}
-> 422 {"errors":[{"field":"mode","errors":["must be one of: air, sea, road"]},{"field":"weight","errors":[... (140 bytes)
POST /quotes {"mode":"air","weight":4000,"volume":3000}
-> 422 {"errors":[{"rule":"fits","fields":["weight","volume"],"error":"weight + volume must total at most 6000"}]}
POST /quotes {"mode":"air","weight":100,"express":true}
-> 422 {"errors":[{"rule":"customs","fields":["express","customs_code"],"error":"express requires customs_code"}]}
POST /quotes {"mode":"air","weight":100,"express":true,"customs_code":"HS-42"}
-> 201 {"price_cents":1800}
count what you didn't write: the 422 renderer never mentions a
field name, the schema endpoint is one method call, and the
relation rules flow to BOTH doors - the 422 explains
"express requires customs_code" to humans, while the schema's
dependencies clause ({"express":["customs_code"]}) tells
client generators the same law in draft-07. one declaration,
and the API layer is just... reading it. the best code in your
app is the code that isn't there.
source
# frozen_string_literal: true # The One-File API: an endpoint is a contract wearing HTTP. Declare # the capability once and the rest is derived - the 422s (with # relation rules explained), the 201, and the machine-readable schema # your client generator reads. No serializer classes, no validator # classes, no docs pipeline. One declaration, three doors. # # bundle exec ruby examples/one_file_api.rb # # Runs offline; requests are simulated, responses are real. require class="s">"bundler/setup" require class="s">"agentic" require class="s">"json" QUOTES = Agentic:class="y">:CapabilitySpecification.new( name: class="s">"quotes", description: class="s">"Quote a shipment", version: class="s">"3.0.0", inputs: { mode: {type: class="s">"string", required: true, enum: %w[air sea road]}, weight: {type: class="s">"number", required: true, min: 1, max: 5_000}, volume: {type: class="s">"number", min: 0}, express: {type: class="s">"boolean"}, customs_code: {type: class="s">"string"} }, outputs: {price_cents: {type: class="s">"number", required: true}}, rules: { fits: {relation: class="y">:sum_lte, fields: [class="y">:weight, class="y">:volume], limit: 6_000}, customs: {relation: class="y">:requires, fields: [class="y">:express, class="y">:customs_code]} } ) RATES = {class="s">"air" => 9, class="s">"sea" => 2, class="s">"road" => 4}.freeze # The entire app. Everything else in this file is derived from QUOTES. def create_quote(params) {price_cents: (params[class="y">:weight] * RATES[params[class="y">:mode]] * (params[class="y">:express] ? 2 : 1)).round} end # --- the derived API layer ----------------------------------------------------- def handle(method, path, body = nil) case [method, path] in [class="s">"GET", class="s">"/quotes/schema"] [200, QUOTES.to_json_schema] in [class="s">"POST", class="s">"/quotes"] validator = Agentic:class="y">:CapabilityValidator.new(QUOTES) begin params = body.transform_keys(&class="y">:to_sym) validator.validate_inputs!(params) output = create_quote(params) validator.validate_outputs!(output) # the contract guards BOTH doors [201, output] rescue Agentic:class="y">:Errors:class="y">:ValidationError => e errors = e.violations.except(class="y">:base).map { |field, messages| {field: field, errors: messages} } errors += e.rule_violations.map { |v| {rule: v[class="y">:rule], fields: v[class="y">:fields], error: v[class="y">:message]} } [422, {errors: errors}] end else [404, {error: class="s">"no such route"}] end end REQUESTS = [ [class="s">"GET", class="s">"/quotes/schema", nil], [class="s">"POST", class="s">"/quotes", {class="s">"mode" => class="s">"teleport", class="s">"weight" => 9_000}], [class="s">"POST", class="s">"/quotes", {class="s">"mode" => class="s">"air", class="s">"weight" => 4_000, class="s">"volume" => 3_000}], [class="s">"POST", class="s">"/quotes", {class="s">"mode" => class="s">"air", class="s">"weight" => 100, class="s">"express" => true}], [class="s">"POST", class="s">"/quotes", {class="s">"mode" => class="s">"air", class="s">"weight" => 100, class="s">"express" => true, class="s">"customs_code" => class="s">"HS-42"}] ].freeze puts class="s">"THE ONE-FILE API (#{QUOTES.name} v#{QUOTES.version})" puts REQUESTS.each do |method, path, body| status, response = handle(method, path, body) puts class="s">" #{method} #{path}#{body ? " #{JSON.generate(body)}class="s">" : "class="s">"}" rendered = JSON.generate(response) rendered = class="s">"#{rendered[0, 100]}... (#{rendered.size} bytes)" if rendered.size > 110 puts class="s">" -> #{status} #{rendered}" puts end schema = QUOTES.to_json_schema puts class="s">" count what you didn't write: the 422 renderer never mentions a" puts class="s">" field name, the schema endpoint is one method call, and the" puts class="s">" relation rules flow to BOTH doors - the 422 explains" puts class="s">" \"#{QUOTES.rules[class="y">:customs][class="y">:fields].first} requires #{QUOTES.rules[class="y">:customs][class="y">:fields].last}\class="s">" to humans, while the schema's" puts class="s">" dependencies clause (#{JSON.generate(schema["dependenciesclass="s">"])}) tells" puts class="s">" client generators the same law in draft-07. one declaration," puts class="s">" and the API layer is just... reading it. the best code in your" puts class="s">" app is the code that isn't there."