Reactive, functional and declarative framework for webapps
API allows you to watch changes in the data model and send Ajax requests to your server. When the server responses, API helps you manage it and save it in the data model.
Reactive data model ➤ API (pure functions) ➤ Server ➤ API (pure functions) ➤ Reactive data model.
The flow that Wu performs with each API item is:
onChange
and when
properties.onResponse
handlers:
onResponse.init
handler.onResponse.status200
, onResponse.status404
, onResponse.status500
…onResponse.success
or onResponse.error
(depending on the answer).onResponse.complete
handler.flags
.cache
(if applicable).| Properties | Required |
|:—————————————————-:|:——–:|
| onChange
| Required |
| when
| Optional |
| request
| Required |
| onResponse
| Required |
| options
| Optional |
wu.create('api', 'getUserProfile', {
onChange: 'user.id',
request: {
method: 'get',
path: 'https://server.com/api/user/profile',
query: {
userId: 'user.id'
}
},
onResponse: {
status200: {
run: (response) => response.body,
update: 'user.profile'
}
},
options: {
cacheable: false
}
})
The definition of the request is formed by:
method (string) [optional, 'GET' by default]:
request method.path (string) [required]:
path request.headers (object) [optional]:
headers request.query (object) [optional]:
query request.body (object) [optional]:
body request.Each of these 5 properties can be:
args
or / and run
:
in this case Wu gets the args
from data model and executes the run
function with them.
The result is the value of the property.
The function receives the context
as the last argument.wu.model.populate()
method.wu.create('api', 'getUserProfile', {
onChange: 'user.id',
request: {
// 'path' is not modified because is a string
path: 'https://server.com/api/user/profile',
// Wu get 'args' from data model and run this function with this args to get 'query'
query: {
args: 'user.id',
run: (userId) => {
return {
userId: userId
}
}
},
// Wu parses 'headers' with data model through 'wu.model.populate()' method
headers: {
authentication: 'user.authentication.token'
}
},
onResponse: {}
})
onResponse
property is formed by the handlers: init
, success
,
error
, complete
and custom handler (status200
, status404
, status500
…).args
,
run
and update
properties.run
methods receive response
, request
and options
as the last arguments.See options.handler
to customize the handlers.
wu.create('api', 'sendUserLogin', {
onChange: 'user.id',
request: {
method: 'post',
path: 'https://server.com/api/user/login',
body: {
email: 'user.email',
password: 'user.password'
}
},
onResponse: {
// array handler, it always runs
init: [
{
run: (response, request, options) => response.status,
update: 'login.call.status'
},
{
run: (response, request, options) => response.errorMessage,
update: 'login.call.errorMessage'
}
],
// custom handler, it runs if the server returns a 200 status
status200: {
run: (response, request, options) => response.body,
update: 'user.login.data'
},
// custom handler, it runs if the server returns a 404 status
status404: {
run: (response, request, options) => '',
update: 'user.password'
},
// handler with data model args, it runs if the server responds correctly
success: {
args: 'user.name',
run: (userName, response, request, options) => 'Welcome ' + userName,
update: 'user.login.greeting'
},
// it runs if the server fails
error: {
run: (response, request, options) => 'Fatal error',
update: 'app.message'
},
// it always runs
complete: {
run: (response, request, options) => true,
update: 'login.call.isCompleted'
}
}
})
You can add the following options to the definition of your API:
options.cacheable:
Wu only caches the calls with GET method, 200 status code and without errors.
If you do not want Wu to cache a call add the property options.cacheable = false
to your API item.
wu.create('api', 'sendUserLogin', {
onChange: 'user.id',
request: {
path: 'https://server.com/api/user/profile',
},
onResponse: {
success: {
run: (response) => response.body,
update: 'user.data'
}
},
options: {
cacheable: false
}
})
options.flags:
A simple way to know if a call is sending is to add flags to an API element. The same to know if the call went well, there were errors, what state code was returned…
To do this, indicate the path of the data model where you want each of these flags to be updated:
complete:
false
when beginning the request and true
when it responds.error:
false
when beginning the request and true
if a fatal error occurs.errorMessage:
empty string ''
when beginning the request and error message if it occurssending:
true
when beginning the request and false
when it responds.success:
false
when beginning the request and true
if everything goes fine.status:
empty string ''
when beginning the request and status code when it responds.wu.create('api', 'sendUserLogin', {
onChange: 'user.id',
request: {
path: 'https://server.com/api/user/profile',
},
onResponse: {
success: {
run: (response) => response.body,
update: 'user.data'
}
},
options: {
flags: {
sending: 'user.login.sending',
success: 'user.login.success',
error: 'user.login.error',
complete: 'user.login.complete',
errorMessage: 'user.login.errorMessage',
status: 'user.login.status'
}
}
})
options.context:
The same API item can be used to send calls to the server with different data or to send several calls in parallel.
To execute each of these calls with different values use options.context
which is an object that can have the properties
when
and run
.
The run
function must return an array.
A call will be sent to the server with each element of the array.
The request
properties (method
, path
, body
, query
and headers
)
receive each of these elements as the last argument.
The onResponse
handlers (init
, success
, error
, complete
, status200
…)
receive options.context
as the last argument.
wu.create('api', 'getUsers', {
onChange: 'users',
request: {
// This call is executed once for each of the elements that are in 'users'
path: {
// options = { context: { user: {...} } }
run: (options) => 'https://server.com/api/user/' + options.context.id
}
},
onResponse: {
success: {
args: ['users'],
// options = { context: { user: {...} } }
run: (users = [], response, request, options) => {
return users.map((user) => {
if (user.id === options.context.id) {
user.data = response.body
}
return user
})
},
update: 'users'
}
},
options: {
// return an array
context: {
args: ['users'],
run: (users) => users
}
}
})
options.handler:
If you want to change the custom handler you can do it by adding the object options.handler
. This object can have the properties args
and run
. And also receives as last arguments response
, request
and options
.
options.handler
must return a string.
wu.create('api', 'sendUserLogin', {
onChange: 'user.id',
request: {
path: 'https://server.com/api/user/profile',
},
onResponse: {
ok: {
run: (response) => response.body,
update: 'user.data'
},
ko: {
run: (response) => ({}),
update: 'user.data'
}
},
options: {
handler: {
run: (response, request, options) => {
return response.status === 200 ? 'ok' : 'ko'
}
}
}
})
navigator.online:
Wu listens to the property window.navigator.onLine
and updates api.online
property of data model each time your browser loses or recovers the Internet connection.
___
Calls to the server go through a queue before they are sent.
Wu is a preventive system. If you make several calls in parallel through the
same API item and with the same request
values,
only the first one will be sent to the server. The rest will be ignored.