Let's get started with Ketting!
Ketting has two major primitives that are important to learn:
- A
Resource
, which represents a single endpoint or url. - A
State
, which represents the result of aGET
request, or what you would submit with aPUT
request.
Let's assume that we have a REST API that manages blog articles. Each article is encoded in JSON and might look like this:
{
"title": "First article",
"body": "..."
}
Our API's root (or home) is at https://api.example/
, and for now
our goal is to:
- Grab the article located at
/article/1
- Update the title.
- Submit the result back to the server.
Note that the following examples are written with Typescript in mind, so if
you use plain Javascript instead, import
might have to be replaced by
require
.
import { Client } from 'ketting';
// Set up the client
const client = new Client('https://api.example');
// Get the resource. Note that this does not make any HTTP requests.
const myArticleRes = client.go('/article/1');
// Get the state. This does a GET request
const myArticleState = await client.get();
// Lets update the title
myArticleState.data.title = 'Hello world v2';
// And save the article again
await client.put(myArticleState);
The State
object represents either a response body, or a request body
in cases like PUT
.
Often you will get a State object this way:
const state = await resource.get();
The following properties and functions are available on the State
object:
uri
- This contains the absolute URI the state belongs to.data
- This is the parsed response body. Usually the result ofJSON.parse()
, but if you received HTML this will be a string, and in the case of binaries this will be aBuffer
.contentHeaders()
- Returns a list of HTTP headers that directly relate or describe the state. Notably, all theContent-*
headers such asContent-Type
andContent-Language
.timestamp
- When the state was generated.serializeBody()
- Turns the state into an object that can be used in a HTTP request.clone()
- Creates a deep copy of the State object.action()
- Covered in Hypermedia.links
- All Weblinks that were encoded in the HTTP Link header, or in the response body. Covered further in Hypermedia.
In short, the State object attempts to describe a full state you might get back from the server, and provide a convenient API to work with it.
We covered that resource.get()
gives you a State object, and .put()
can
send the state back to the server.
For this to work with Ketting, the assumption is that what you PUT
to
an endpoint on a server more or less has the same shape as what you would
GET
immediately after.
This assumption allows Ketting to do a lot of caching.
In the following example, only 1 GET request is done:
const state1 = await resource.get();
// Get the state again, but this time it's served from cache.
// Note that this state object is a *copy* and not the same object.
const state2 = await resource.get();
// Make a change
state2.data.title = 'Hello again';
// Submit the state back to the server
await resource.put(state2);
// When doing a PUT request, ketting will immediately place the new
// state object in the cache. The following call to .get() is again
// served from cache, but it will reflect the state from the last
// time we called .put()
const state3 = await resource.get();
// Will output 'hello again'
console.log(state3.data.title);
The following three cache-related operations are also available:
// This cleans the state cache for the resource.
resource.clearCache();
// Updates the internal cache *without* doing a request.
resource.updateCache(state);
// Do a GET request but skip the cache
const state = await resource.refresh();
You don't need a full State object to do a PUT
request. Sometimes
you just want to do a fresh PUT
request with data from your
application.
The PUT
request also accepts an incomplete state object as such:
await resource.put({
data: {
foo: 'This is the new body for PUT'
}
});
A common pattern in REST apis is to create new resources with POST
.
Ketting has a special method for this: postFollow()
.
For this to work correctly, your server should:
- Accept
POST
requests - If successful, return a
201 Created
status code. - The server must also return a
Location
header that points to the newly created resource.
Many REST services work this way, and if yours does too, this is how this might work:
const newArticle = {
title: 'Second post!',
body: '....',
};
const newResource = await collectionResource.postFollow({
data: newArticle
});
The postFollow()
function will return a new Resource
object with
all the functions you expect, like .get()
and .post()
.
If you just want to do a regular POST
request, but to do an RPC-like
operation like "Sending an email" or "Submitting a form", there is also
a regular .post()
method that takes a State
and returns a State
,
giving you access to the response body:
const result = resource.post({
data: {
to: '[email protected]',
subject: 'Sup'
}
});
Similarly, resources have a .patch()
method which behaves the exact
same.
Calling .post()
, .patch()
, .postFollow()
or any other "unsafe" HTTP
method will expire the state cache.
Delete requires neither a request body, nor returns a response body:
await resource.delete();
It's possible to subscribe to state changes. To get notified whenever the
State cache gets updated, subscribe to the update
event:
resource.on('update', state => {
console.log('The resource just updated');
console.log('New data: ', state.data);
})
Other events:
delete
, which gets triggered upon callingDELETE
.stale
gets triggered when the cache is no longer deemed valid.
Resources also have a fetch()
function that behaves similar to the normal
browser fetch, but it does not take an uri
argument. This can be
useful for more complex requests.
When using this fetch()
function, the cache will still be expired for
'unsafe' methods.
You saw client.go()
before, but resources also have a .go()
method. This
method does the same thing, but when passing a relative uri here, the absolute
uri will be calculated based on the resource's uri.
This set of functions is used for Ketting's hypermedia features, and are covered in the Hypermedia docs.
Depending on what your goals are, these are good next reads:
- Ketting's Hypermedia features.
- Authentication.
- React integration
- Optimizing many requests.