Criando um CRUD simples com Koa

O Node.js é uma tecnologia que cresce a cada dia, apoiado pelo NPM (Gerenciador de Pacotes do Node) que ajuda programadores a manter as dependências dos aplicativos. Vamos direto ao ponto de como fazer um CRUD (Create, Read, Update e Delete), utilizando o Koajs que é feito e mantido pelo mesmo time do Express tendo as mesmas funcionalidades e suporte as funções geradoras do ECMASCRIPT6.

Os pré requisitos para esse tutorial são:
1 — Ter Node.js e NPM instalados caso não tenha siga o tutorial do Matheus.
2 — Ter um editor de textos (Nano, VIM, Notepad++, gEdit, Scratchpad…)
3 — Ter um pouco de entendimento sobre conceito MVC, Restful, API, HTTP.

Vamos começar?

Eu estou utilizando uma maquina com Xubuntu, terminal Guake, e o Nano e considerando que o Node e o NPM estão instalados. Vamos verificar isso antes de começar. Digite no terminal caso esteja usando um Linux ou Osx.

node -v && npm -v
v5.5.0
3.3.12

Tenho os dois instalados, vamos criar uma pasta para o projeto

mkdir koa-crud

Entrar na pasta

cd koa-crud

Agora vamos criar o nosso arquivo pakcage.json, podemos fazer pelo editor de texto ou pelo npm. Digite no terminal.

npm init
name: (koa-crud) 
version: (1.0.0)
description: Pequeno CRUD com KOAJS
entry point: (index.js)
test command:
git repository:
keywords:
author: Fabiano Monte
license: (ISC)

De enter e não se preocupe que tudo aqui pode ser alterado posteriormente. Depois será exibido o nosso aquivo package.json até esse ponto.

About to write to /home/monte/koa-crud/package.json:

{
“name”: “koa-crud”,
“version”: “1.0.0”,
“description”: “Pequeno CRUD com Koajs”,
“main”: “app.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1"
},
“author”: “Fabiano Monte”,
“license”: “ISC”
}

Vamos alterar o arquivo para ter nele as dependências que precisamos

{
“name”: “koa-crud”,
“version”: “1.0.0”,
“description”: “Pequeno CRUD com Koajs”,
“main”: “app.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1"
},
“dependencies”: {
“co-body”: “0.0.1”,
“co-views”: “~0.1.0”,
“jsonfile”: “^2.2.3”,
“koa”: “~0.2.0”,
“koa-logger”: “~1.1.0”,
“koa-route”: “~1.0.2”,
“swig”: “~1.2.2”
},

“author”: “Fabiano Monte”,
“license”: “ISC”
}

Salve o arquivo e digite no terminal.

npm install

Tudo certo até aqui? Então vamos seguir.

Vamos criar o arquivo app.js até aqui sua estrutura deve estar assim

├── app.js
├── node_modules
└── package.json

Sabemos tudo que temos que fazer? Não. Então vamos criar um TODO comentado no aquivo para poder conferir se demos os passos corretos.

Abra o app.js no editor e converse com o código. Isso. De uma de maluco. Converse com o código, em voz alta, para o cérebro pegar no tranco :D

app.js

// Vamos conversar com o código pois ele é seu amigo :D
// Geralmente o que temos que fazer?
// 1 — Chamar as dependências (modules dependencies)
// 2 — Criar as variáveis ou arrays de armazenamento de dados
// 3 — Criar os middlewares, o garçom que vai trabalhar entre a mesa e a cozinha
// 4 — Criar as rotas, o caminho a ser percorrido separado por portas para cada caso
// 5 — Vamos deixar a tela (VIEW) mais inteligente para conversar como o CONTROLLER e o modules
// 6 — Vamos criar as funções para cada ação passada pela rota para o CRUD sendo elas
// 6.1 — Listar(list)
// 6.2 — Adicionar(add)
// 6.3 — Editar(edit)
// 6.4 — Mostrar(show)
// 6.5 — Apagar(remove)
// 6.6 — Criar(create)
// 6.7 — Atualizar(update)
// 7 — Iniciar o servidor http

Agora já sabemos o que temos que fazer, então vamos começar.

No arquivo app.js coloque as dependências que vamos usar seu arquivo deve ficar assim

// 1 — Chamar as dependências (modules dependencies)
var logger = require(‘koa-logger’);
var route = require(‘koa-route’);
var views = require(‘co-views’);
var parse = require(‘co-body’);
var koa = require(‘koa’);
var app = koa();
// 7 — Iniciar o servidor http
app.listen(3000);
console.log(‘listening on port 3000’);

Perceba que estamos fazendo o passo 7 e iniciando o servidor http, fiz isso para testarmos sempre se o que estamos fazendo está certo.

Para isso vamos rodar no terminal.

node app.js

Se nenhum erro foi retornado estamos indo bem até aqui, vamos para o próximo passo.

Vamos fazer uma agenda de contatos telefônicos, para em um futuro fundir com um artigo sobre Angular.js.

// 2 — Criar as variáveis ou arrays de armazenamento de dados
var contatos = [];

Agora que já temos onde armazenar o nossos dados vamos para os middlewares que nesse caso utiliza o nosso modulo koa-logger

// 3 — Criar os middlewares, o garçom que vai trabalhar entre a mesa e a cozinha
app.use(logger());

Em seguida vamos criar as rotas. Agora utilizando o modulo koa-route

// 4 — Criar as rotas, o caminho a ser percorrido separado por portas para cada caso
app.use(route.get(‘/’, list));
app.use(route.get(‘/categoria/new’, add));
app.use(route.get(‘/categoria/:id’, show));
app.use(route.get(‘/categoria/delete/:id’, remove));
app.use(route.get(‘/categoria/edit/:id’, edit));
app.use(route.post(‘/categoria/create’, create));
app.use(route.post(‘/categoria/update’, update));

Se nesse momento você testar a aplicação no terminal, recebera um erro. Sabe por quê?

As rotas apontam para endereços que não existem. Vamos testar? Digite no terminal.

node app.js

app.use(route.get(‘/’, list));
ReferenceError: list is not defined

O que isso significa? Que temos que definir. Vamos colocar somente a função list para ter certeza de que é somente esse o problema. Adicione ao arquivo app.js.

// 6.1 — Listar(list)
function *list() {
this.body = yield render(‘index’, { categorias: categorias });
}

Agora salve e no terminal rode o comando novamente.

node app.js

app.use(route.get(‘/categoria/new’, add));

ReferenceError: add is not defined

Agora a rota e função add que não responde. Bem vamos passar ao template antes de nos estender com as funções que são o nosso CRUD.

Para interagir melhor com o que estamos fazendo vamos criar as nossas telas. Crie uma pasta com o nome de views com os arquivos.

views/
├── edit.html
├── index.html
├── layout.html
├── new.html
└── show.html

O arquivo layout.html é o nosso template. Para não perder tempo vamos colocar um css simples na página ficando da seguinte forma.

layout.html

<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Categoria App{% endblock %}</title>
<style>
body { padding: 80px; font: 16px Helvetica, Arial;}
h1 { font-size: 2em;}
h2 { font-size: 1.2em;}
#posts { margin: 0; padding: 0;}
#posts li { margin: 40px 0; padding: 0; padding-bottom: 20px; border-bottom: 1px solid #eee; list-style: none;}
#posts li:last-child { border-bottom: none; }
textarea { width: 500px; height: 300px; }
input[type=text], textarea { border: 1px solid #eee; border-top-color: #ddd; border-left-color: #ddd; border-radius: 2px;padding: 15px; font-size: .8em; }
input[type=text] { width: 500px;}
</style>
</head>
<body>
<section id=”content”>
{% block content %}
<p>Missing content!</p>
{% endblock %}
</section>
</body>
</html>

index.html

{% extends ‘layout.html’ %}
{% block title %}Contatos{% endblock %}
{% block content %}
<h1>Contatos</h1>
<p>Existe um total de <strong>{{ contatos.length }}</strong> contatos em sua agenda!</p>
<p><a href=”/contato/new”>Inserir contato</a></p>
<ul id=”contatos”>
{% for contato in contatos %}
<li>
<span>Nome: </span> <b>{{ contato.nome }}</b> <span>Telefone: </span><b>{{ contato.telefone}}</b> <span> Operadora: </span> <b> {{ contato.operadora}} </b> ||
<a href=”/contato/{{ contato.id }}”>Ver</a> ||
<a href=”/contato/edit/{{ contato.id }}”>Editar</a> ||
<a href=”/contato/delete/{{ contato.id }}”>Apagar</a>
</li>
{% endfor %}
</ul>
{% endblock %}

new.html

{% extends ‘layout.html’ %}
{% block title %}Cadastrar Contato{% endblock %}
{% block content %}
<h1>Adicionar Contato</h1>
<p>Criar novo Contato</p>
<form action=”/contato/create” method=”post”>
<p><input type=”text” placeholder=”Nome” name=”nome”></p>
<p><input type=”text” placeholder=”Telefone” name=”telefone”></p>
<p><input type=”text” placeholder=”Operadora” name=”operadora”></p>
<p><input type=”submit” value=”Cadastrar”></p>
</form>
{% endblock %}

edit.html

{% extends ‘layout.html’ %}
{% block title %}Editar Contato{% endblock %}
{% block content %}
<h1>Editar Contato</h1>
<p>Faça as alterações.</p>
<form action=”/contato/update” method=”post”>
<p><input type=”hidden” value=”{{ contato.id }}” name=”id”></p>
<p><input type=”text” value=”{{ contato.nome }}” name=”nome”></p>
<p><input type=”text” value=”{{ contato.telefone }}” name=”telefone”></p>
<p><input type=”text” value=”{{ contato.operadora }}” name=”operadora”></p>
<p><input type=”submit” value=”Atualizar”></p>
</form>
{% endblock %}

show.html

{% extends ‘layout.html’ %}
{% block title %}{{ contato.nome }}{% endblock %}
{% block content %}
<h1>{{ contato.nome }}</h1>
<p>{{ contato.telefone }}</p>
<p><i>Criado em: </i> {{ contato.created_on.toDateString() }}</p>
<p><i>atualizado em: </i> {{ contato.updated_on.toDateString() }}</p>
<p><a href=”/”>Voltar para lista</a></p>
{% endblock %}

Agora que terminamos todas os nossas views. Vamos criar as funções para elas.

Para adicionar um novo chamaremos a new

// 6.2 — Adicionar(add)
function *add() {
this.body = yield render(‘new’);
}

A adicionar chama a função create

// 6.6 — Criar(create)
function *create() {
var contato = yield parse(this);
contato.created_on = new Date;
contato.updated_on = new Date;
var id = contatos.push(contato);
contato.id = id-1;
this.redirect(‘/’);
}

Agora vamos a função edit, que recebe o id do contato.

// 6.3 — Editar(edit)
function *edit(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
this.body = yield render(‘edit’, { contato: contato });
}

Depois da edição é chamada a função de update.

// 6.7 — Atualizar(update)
function *update() {
var contato = yield parse(this);
var index=contato.id;
contatos[index].nome=contato.nome;
contatos[index].telefone=contato.telefone;
contatos[index].operadora=contato.operadora;
contatos[index].updated_on = new Date;
this.redirect(‘/’);
}

Agora vamos remover o contato com o remove

// 6.5 — Apagar(remove)
function *remove(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
contatos.splice(id,1);
//Changing the Id for working with index
for (var i = 0; i < contatos.length; i++)
{
contatos[i].id=i;
}
this.redirect(‘/’);
}

Por fim mostramos detalhes de um contato pelo id com a função show

// 6.4 — Mostrar(show)
function *show(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
this.body = yield render(‘show’, { contato: contato });
}

O código do app.js final fica assim

app.js

// Vamos conversar com o código pois ele é seu amigo :D
// Geralmente o que temos que fazer?
// 1 — Chamar as dependências (modules dependencies)
var logger = require(‘koa-logger’);
var route = require(‘koa-route’);
var views = require(‘co-views’);
var parse = require(‘co-body’);
var koa = require(‘koa’);
var app = koa();
// 2 — Criar as variáveis ou arrays de armazenamento de dados
var contatos = [];
// 3 — Criar os middlewares, o garçom que vai trabalhar entre a mesa e a cozinha
app.use(logger());
// 4 — Criar as rotas, o caminho a ser percorrido separado por portas para cada caso
app.use(route.get(‘/’, list));
app.use(route.get(‘/contato/new’, add));
app.use(route.get(‘/contato/:id’, show));
app.use(route.get(‘/contato/delete/:id’, remove));
app.use(route.get(‘/contato/edit/:id’, edit));
app.use(route.post(‘/contato/create’, create));
app.use(route.post(‘/contato/update’, update));
// 5 — Vamos deixar a tela (VIEW) mais inteligente para conversar como o CONTROLLER e o modules
var render= views(__dirname + ‘/views’, { map: { html: ‘swig’ }});
// 6 — Vamos criar as funções para cada ação passada pela rota para o CRUD sendo elas
// 6.1 — Listar(list)
function *list() {
this.body = yield render(‘index’, { contatos: contatos });
}
// 6.2 — Adicionar(add)
function *add() {
this.body = yield render(‘new’);
}
// 6.3 — Editar(edit)
function *edit(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
this.body = yield render(‘edit’, { contato: contato });
}
// 6.4 — Mostrar(show)
function *show(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
this.body = yield render(‘show’, { contato: contato });
}
// 6.5 — Apagar(remove)
function *remove(id) {
var contato = contatos[id];
if (!contato) this.throw(404, ‘invalid contato id’);
contatos.splice(id,1);
//Changing the Id for working with index
for (var i = 0; i < contatos.length; i++)
{
contatos[i].id=i;
}
this.redirect(‘/’);
}
// 6.6 — Criar(create)
function *create() {
var contato = yield parse(this);
contato.created_on = new Date;
contato.updated_on = new Date;
var id = contatos.push(contato);
contato.id = id-1;//Id with index of the array
this.redirect(‘/’);
}
// 6.7 — Atualizar(update)
function *update() {
var contato = yield parse(this);
var index=contato.id;
contatos[index].nome=contato.nome;
contatos[index].telefone=contato.telefone;
contatos[index].operadora=contato.operadora;
contatos[index].updated_on = new Date;
this.redirect(‘/’);
}
// 7 — Iniciar o servidor http
app.listen(3000);
console.log(‘listening on port 3000’);

Agora vamos testar se nossa Agenda de Contatos funcionalidades

node app.js

Tentei simplificar ao máximo em breve vamos melhorar a brincadeira adicionando um banco de dados e depois usar o Angularjs e o Bootstrap.