Kakapo.js
:bird: Next generation mocking framework in Javascript
Install / Use
/learn @devlucky/Kakapo.jsREADME
Kakapo.js <img src="https://sdl-stickershop.line.naver.jp/products/0/0/1/1087538/LINEStorePC/main.png?__=20150924" width="40" height="40" >
<img src="http://benschwarz.github.io/bower-badges/badge@2x.png" width="130" height="25">
Next generation mocking framework in Javascript - docs
Contents
Kakapo its a full featured http mocking library, he allows you to entirely replicate your backend logic in simple and declaritive way directly in the browser. This way you can easily prototype and develop the whole Application without backend and just deactivate Kakapo when you go production. In order to achieve that Kakapo gives you a set of utilities like Routing, Database, Response, Request and so on...
Installation
$ npm i kakapo -D
$ bower i kakapo -D
Examples
Before going deep into the Kakapo docs, we want to show you some examples of how to use Kakapo in your apps, in order to demonstrate how easy to integrate it is
Basic
Use the kakapo router to declare two custom routes and returning custom data.
import {Router, Server} from 'Kakapo';
const router = new Router();
router.get('/users', _ => {
return [{
id: 1,
name: 'Hector'
}, {
id: 2,
name: 'Zarco'
}]
});
router.get('/users/:user_id', request => {
const userId = request.params.user_id;
return [{
id: userId,
name: 'Hector'
}];
});
const server = new Server();
server.use(router);
fetch('/users', users => {
console.log(users[0].id === 1);
console.log(users[1].id === 2);
});
fetch('/users/3', user => {
console.log(user.id === 3);
console.log(user.name === 'Hector');
});
Using the DB
Combine the usage of the Router and the Database to have a more consistent way of managing the mock data. In the following example we are defining the user factory and later using some access methods provided by the db.
import {Database, Router, Server} from 'Kakapo';
const db = new Database();
db.register('user', () => ({
firstName: 'Hector',
lastName: 'Zarco'
}));
db.create('user', 10);
const router = new Router();
router.get('/users', (request, db) => {
return db.all('user');
});
router.get('/users/:user_id', (request, db) => {
const userId = request.params.user_id;
return db.findOne('user', userId);
});
const server = new Server();
server.use(db);
server.use(router);
fetch('/users', users => {
console.log(users[0].id === 1);
console.log(users[1].id === 2);
console.log(users[2].id === 3);
});
fetch('/users/7', user => {
console.log(user.id === 7);
console.log(user.name === 'Hector');
});
As you can see, the database automatically handles the id generation, so you don't have to worry about assigning incremental id's of any kind.
Unchaining the Router
Next you will see how to use other features of the router and figure out the things you can build with it. In this example we show you how easy is to create a fake pagination handling, which is based in the actual requested page.
import {Router, Server} from 'Kakapo';
const router = new Router();
router.get('/users', (request) => {
const currentPage = request.query.page;
let count = request.query.count;
let users = []
while (--count) {
users.push({name: 'hector', age: 24});
}
return {
data: users,
metadata: {
previous_page: currentPage - 1,
current_page: currentPage,
next_page: currentPage + 1
}
}
});
router.post('/users/:user_id/friends/:friend_id', (request) => {
const token = request.headers.get('X-auth-token');
const userId = request.params.friend_id;
const friendId = request.params.friend_id;
let message;
if (token === 'foo-bar') {
message = `successs friend: ${friendId} created for userId: ${userId}`;
} else {
message = 'Unauthorized';
}
return {
message
}
});
const server = new Server();
server.use(router);
fetch('/users?page=1&count=3', response => {
console.log(response.data.length === 3);
console.log(response.metadata.previous_page === 1);
console.log(response.metadata.next_page === 2);
});
const headers = new Headers();
headers.append('X-auth-token', 'foo-bar');
const request = new Request('/users/10/friends/55');
fetch(request, {method: 'POST', headers}).then(response => {
console.log(response.message);
});
Fetch & XMLHttpRequest support
Kakapo have Fetch and XMLHttpRequest support by default, but you can always change that if you want, see the interceptors docs.
import {Router, Server} from 'Kakapo';
const router = new Router();
router.get('/users/', () => {
return 'meh';
});
const server = new Server();
server.use(router);
fetch('/users', users => {
console.log(users[0].id === 1);
console.log(users[1].id === 2);
});
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
const response = JSON.parse(xhr.responseText);
console.log(response[0].id === 1);
console.log(response[1].id === 2);
};
xhr.open('GET', '/users', true);
xhr.send();
Database candyness
Check how easy to build a consistent CRUD Api with the kakapo db.
import {Database, Router, Server, Response as KakapoResponse} from 'Kakapo';
const sever = new Server();
const router = new Router();
const databse = new Database();
database.register('user', faker => {
return {
firstName: faker.name.firstName,
lastName: faker.name.lastName,
age: 24
};
});
database.create('user', 5);
router.get('/users', (request, db) => {
return db.all('user');
});
router.post('/users', (request, db) => {
const user = JSON.parse(request.body);
db.push('user', user);
return user;
});
router.put('/users/:user_id', (request, db) => {
const newLastName = JSON.parse(request.body).lastName;
const user = db.findOne('user', request.params.user_id);
user.lastName = newLastName;
user.save();
return user;
});
router.delete('/users/:user_id', (request, db) => {
const user = db.findOne('user', request.params.user_id);
const code = user ? 200 : 400;
const body = {code};
user && user.delete();
return new KakapoResponse(code, body);
});
const server = new Server();
server.use(database);
server.use(router);
const getUsers = () => {
return fetch('/users').then(r => r.json());
}
const createUser = (user) => {
return $.post('/users', user);
};
const updateLast = () => {
return $.ajax({
url: '/users/6',
method: 'PUT',
data: {lastName: 'Zarco'}
});
};
const deleteFirst = () => {
return $.ajax({
url: '/users/1',
method: 'DELETE'
});
};
getUsers() // users[0].id === 1, users.length === 5
.then(createUser.bind(null, {firstName: 'Hector'})) // response.id === 6, response.firstName === 'Hector'
.then(deleteFirst) // response.code === 200
.then(getUsers) // users[0].id == 2, users[4].id == 6, users.length == 5
.then(updateLast) // user.id == 6, user.lastName == 'Zarco'
.then(getUsers) // users.length == 5
Passthrough
You don't need to do anything in order to keep your existing request working as they were before. Kakapo will just passthrough all the request that doesn't match any of the defined routes and fire the associated callback.
import {Router, Server} from 'Kakapo';
const server = new Server();
const router = new Router();
router.get('/users', (request) => {
return 'Kakapo';
});
const server = new Server();
server.use(router);
fetch('/users', users => {
users == 'Kakapo';
});
fetch('https://api.github.com/users', users => {
//Real Github users data
});
Demo Apps
TODO App
Every project needs a TODO App, so here is the Kakapo one. Straightforward vanilla js app, uses the fetch api for the networking part.
Check the demo or look at the code

Github explorer
Basic github users search example, based 100% on the (Github Api)[https://developer.github.com/v3]. It shows you how easy is to replicate the logic of a backend with Kakapo and iterate faster when building the UI. This example also uses jQuery just to demostrate the compatibility with Kakapo.
Check the demo or look at the code

