05. Adding Addons on Treats Server
Treats was designed so it could be easily customisable with addons. We will try to add addons into our todo apps. Let's start with addons-base. Add this point, the one that's useful enough for our todo-apps is circuit breaker (CB)
, assuming that our countries API may be slower than usual or just hang our request without response.
- Install
@treats/addons-base
in our todo projects.
~$ npm install @treats/addons-base
- Create
_server
hooks to customise our server behaviour. Inside it, createindex.js
with the following implementation.
/* src/_server/index.js */
import initServer from "@treats/server";
import CircuitBreaker from "@treats/addons-base/helper/circuitbreaker";
const app = initServer({
customHelpers: CircuitBreaker
});
export default app;
By doing this, we could get our CircuitBreaker
instance by const cb = app.get("circuitbreaker")
.
- By adding
CircuitBreaker
into our server app, we need to change ourgetInitialState
method, since we will get our app instance fromreq
object (read more from Express).
/* src/redux/profile-page/action.js */
...
const getInitialState = req => dispatch => {
return Promise.all([dispatch(getCountries(req))])
}
...
/* src/page/profile-page/profile.js */
class Profile extends Component {
static async getInitialState({ router, serverContext }) {
const { req, reduxStore } = serverContext;
return reduxStore.dispatch(profileActions.getInitialState(req));
}
...
}
- Now that
getCountries
also getreq
as parameters, we need to change the implementation, too. We also need to consider thatgetCountries()
could be run on client side (when clicking "change name" link) -- it meansgetCountries()
would not getreq
object. Therefore, we need to add two cases forserver
andclient
environment.
/* src/page/profile-page/profile.js */
...
const getCountries = req => dispatch => {
dispatch(getCountriesLoading());
if (process.env.BUILD_TARGET === "server") {
const { app } = req,
cb = app.get("circuitbreaker");
console.log("API CALL with CB")
return cb.call(
axios.get,
"https://restcountries.eu/rest/v2/all",
["https://restcountries.eu/rest/v2/all", { method: "GET" }])
.then(response => {
const parsedResponse = response.data.map(country => ({
code: country.alpha2Code,
name: country.name
}))
dispatch(getCountriesSuccess(parsedResponse));
}).catch(err => {
dispatch(getCountriesError());
console.error(err);
})
}
return axios.get("https://restcountries.eu/rest/v2/all")
.then(response => {
const parsedResponse = response.data.map(country => ({
code: country.alpha2Code,
name: country.name
}))
dispatch(getCountriesSuccess(parsedResponse));
}).catch(err => {
dispatch(getCountriesError());
console.error(err);
});
};
...
- Last but not least, we need to create config for our circuit breaker. To create our circuit breaker config, we will create it in
<PROJECTS_DIRECTORY>/treats.runtime-config.json
.
{
"helper": {
"circuitbreaker": {
"https://restcountries.eu/rest/v2/all": {
"timeout": 3000,
"errorThresholdPercentage": 50,
"resetTimeout": 30000
}
}
}
}
- All set! Let's see if our CB working by proceed to
/profile
. Refresh the page if you are proceed to the profile page via "Change Name" button. To check if our CB work on error, set the config timeout into 1. Most likely, the api call will timeout and logging some error in your server console.
This is some example of using addons in Treats. Explore addons concepts: middleware, helper, and generator and create your own addons!