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