sgo.to

Microforms

microforms is a file type (application/microforms) designed to expose REST endpoints.

The syntax, inspired by kotlin, is designed to intermingle data (text) with control (hypertext) more ergonomically (compared to HTML or JSON).

Like HTML, it defines a set of affordances (e.g. links, forms, imports, etc, designed to be isomorphic to HTML) and is typically used with a client side library that follows instructions sent by the server.

{
// Ah, interesting, kindaof looks like JSON ...
foo: "foo",
hello: "world",

// This is where things get interesting: introducing, form {}.
action: form {
method: "POST",
input { name: "content", required: true }
}
}

Example

Microforms are designed to describe REST APIs. Here is a longer example:

{
type: "Blog",
blogPost: [{
type: "BlogPosting",
id: 1,
title: "hello world",
get: a { href: "/posts/1"}
}, {
type: "BlogPosting",
id: 2,
title: "foo bar",
get: a { href: "/posts/2"}
}],
create: form {
method: "POST",
action: "/create",
input { name: "title", type: "text", required: true }
input { name: "content", type: "text", required: true }
input { name: "author", type: "text", required: true }
}
}

And here is how a client (in this specific example, a javascript client) would interact:

// No out-of-band information needed to construct a request to create a post.
// Everything is embedded into the message.
// The client knowns how to follow links (a) and submit forms (form).
let post = await fetch("/blog.micro").then(posts => posts.create({
title: "hello",
content: "world",
author: "goto@google.com"
}));

Which returns:

{
type: "BlogPosting",
id: 4,
title: "hello",
content: "world",
author: "goto@google.com",
delete: form {
action: "/posts/4"
method: "DELETE",
},
update: form {
method: "POST",
input { name: "content", type: "text", required: true }
}
}

With the magic of HATEOAS, submitting a microform gets you another microform, with new exciting data and affordances!

// For example, say you disliked it, you can update it ...
post.update({
content: "Actually, nevermind."
});

// ... or delete it ...
post.delete();
// ...

Shared Semantics

Like HTML, microforms isn't opinionated about JSON conventions nor schemas / ontologies. As a layered data format, it is designed to enable innovation to happen in that space without recompilation of the parsers / binaries. Here is an example of what it could look like if you are into JSON-LD and schema.org.

{
@context: "http://schema.org",
@type: "Blog",
@id: "/",
title: "Sam's blog",
blogPost: [{
@type: "BlogPosting",
title: "Welcome",
content: "This is my first post!"
}],
potentialAction: form {
@type: "CreateAction",
input { name: "title", required: true }
input { name: "content", required: true }
}
}

Affordances

It comes with some common control structures, borrowed from HTML for ease of use. For example:

  • a
  • link
  • form
  • input
  • select
  • option
  • meta
  • fieldset

As well as some additional ones to enable contemporary APIs to be written:

  • group
  • data loading
  • validation

A lot of control structures are also available outside of the context of the file format, which gives clients of microforms more instructions on how to proceed. These are:

  • Accepts-Vocab: for vocabulary negotiation
  • Bearer tokens: for key management
  • Throttling: for quota management

Extension mechanism

We are still exploring some options, but it is clear that we need to support some sort of extension mechanism. Something along the lines of:

{
namespace { id: "oauth", url: "http://oauth.org" }
type: "Error",
messsage: "Oops, you have to be signed-in to access this resource",
retry: [oauth:signin] {
[oauth:authorization]: "http://example.com",
[oauth:scopes]: {
"write.calendar": "modify your calendar events",
"write.calendar": "access your calendar events",
}
}
}

Grammar

TODO(goto): write this down. Looks something like JSON + {} nodes + comments.

Related Work

  • hydra
  • atom
  • swagger
  • alps
  • siren
  • swagger
  • json-home
  • rsdl
  • wadl
  • blueprint
  • restdesc
  • wsdl
  • hal

Acknowledgements

  • TODO(goto): fill this up