Nov. 1, 2016
Como crear una app con node, express y mongoDB
En este tutorial crearemos una app sencilla en node usando express como framework y mongo como base de datos.
El código de la app esta en este link.
Instalando dependencias
Iniciamos nuestra app con un package.json:
npm init -y
y pasamos a instalar las librerias necesarias:
npm install --save pug mongoose express body-parser method-override
Creando el archivo inicial
Crearemos el código base para que express pueda crear un servidor:
index.js
var http = require('http')
var app = require('./app/express_config')
var port = 4000
var server = http.createServer(app)
server.listen(port, function() {
console.log('server listening in port: ' + port)
})
Necesitamos la libreria http que viene con node para poder crear nuestro servidor con http.createServer
y a este pasarle nuestra app de express ya configurada, esta configuración la pedimos en la segunda linea y es un archivo que aun no hemos creado, creamos una carpeta llamada app y adentro un archivo express_config.js
:
var express = require('express')
var app = express()
var bodyParser = require('body-parser')
var methodOverride = require('method-override')
var posts = require('./controllers/posts_controller')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.set("views", "app/views")
app.set('view engine', 'pug')
app.use(express.static('app/public'))
app.use(methodOverride("_method"))
app.get("/", function(req, res) {
res.render("index")
})
app.use('/posts', posts)
module.exports = app
Aquí es donde ocurre toda la magia de express, primero exportamos las librerías necesarias y creamos una app de express con express()
y la asignamos a la variable app
esta variable la usaremos para configurar express, las primeras lineas de configuración piden a express que use body parser
para poder recibir datos cuando se haga una petición post o get.
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
Después pasamos a configurar la ruta donde estarán nuestras vistas, que esta dentro de la carpeta app y también el template de las vistas, en este tutorial usaremos pug, no soy muy fan de este template pero es muy fácil de usar como veremos mas adelante.
app.set("views", "app/views")
app.set('view engine', 'pug')
En la siguiente linea indicamos donde están los archivos estáticos (css, js, imágenes) para que express pueda crear rutas y obtenerlos desde las vistas.
app.use(express.static('app/public'))
Por ultimo usamos una libreria llamada methodOverride
, esta libreria nos permite usar put
y delete
para actualizar y eliminar registros respectivamente.
Lo ultimo que hacemos en la configuración es crear dos rutas, una para el index de la aplicación:
app.get("/", function(req, res) {
res.render("index")
})
y como podemos notar la forma de crear una ruta con express es primero indicando el tipo de método que usaremos (en este caso get) e indicando la ruta a la que apunta ("/" la ruta principal o index) después pasando una función que recibe dos parámetros, un request y un response, request nos manda la información de la petición, si fuera de tipo post también tendría los datos del registro que se quiere crear y response nos permite responder al usuario información plana o un archivo html, en este caso le respondemos con la pagina index.
La ultima ruta que creamos es un poco diferente:
app.use('/posts', posts)
esta recibe dos parámetros, el primero es la ruta principal que compartirán las rutas que le pasemos en el segundo parámetro ya que este es un objeto de tipo route, esto nos permite crear varias rutas sin tener que repetir la dirección, por ejemplo en este caso queremos que las rutas para crear, editar, eliminar y mostrar estén todas de la siguiente manera: "/posts/show", "/posts/:id/edit", "/posts/:id/delete", como podemos notar le pasamos el objeto posts que requerimos en la quinta linea:
var posts = require('./controllers/posts_controller')
este archivo esta dentro de una carpeta controllers y es donde tenemos la configuración de las rutas de los posts.
Creando el modelo
Antes de crear el controlador necesitamos configurar un modelo y la conexión con mongo, esta configuración esta en un archivo llamado post.js dentro de la carpeta app/models:
var mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/blog_v')
var PostSchema = {
title: { type: String, required: true },
description: { type: String }
}
var Post = mongoose.model('Post', PostSchema)
module.exports = Post
Lo primero que hacemos es pedir la libreria mongoose, esta libreria nos permite conectarnos con mongoDB y facilita las peticiones, para conectar con mongo tenemos que usar la función mongoose.connect('mongodb://localhost/blog_v')
donde le pasamos la ruta de la base de datos en este caso usaremos una local pero también podríamos conectarnos a una en otro servidor ajeno a su propia pc, nuestra base de datos se llama blog_v
. Después tenemos que crear un schema para que mongo sepa como serán los datos que le mandemos, nuestro modelo post tiene un titulo y una descripción ambas de tipo string, ya teniendo el schema se lo pasamos a la función mongoose.model('Post', PostSchema)
para que se registre con el nombre de Post
, exportamos el nuevo modelo ya registrado para que lo podamos usar en los controladores.
Creando el controlador
Dentro de la carpeta app/controllers creamos un archivo llamado posts_controller.js:
var express = require('express')
var router = express.Router()
var Post = require('./../models/post')
Empezaremos pidiendo express para poder usar su router y crear las rutas necesarias, también pediremos el modelo Post
creado previamente.
Index
En la ruta index queremos tener una lista de todos los posts disponibles:
router.get('/', function(req, res) {
Post.find(function(err, doc) {
if (err) return console.log(err)
res.render('posts', { posts: doc })
})
})
Usamos el router para crear la ruta principal que en este caso sera "/posts" y gracias a la ayuda de moongose podemos usar el modelo Post
para crear peticiones a la base de datos, aquí necesitaremos buscar todos los posts entonces indicamos Post.find
esto nos regresara una función con dos parámetros, un error si es que algo salió mal y un documento con la lista de posts, procedemos a checar si el error existe para evitar enviar datos vacíos al usuario, si no hay error entonces hacemos render del archivo posts
que estará dentro de la carpeta views (al final crearemos todas las vistas) y le pasamos el documento para que lo pueda mostrar al usuario.
Create
Para poder crear un nuevo post necesitamos dos rutas, una que nos muestre el formulario (petición get) y otra que le lleguen los datos para del nuevo post (petición post):
router.get('/new', function(req, res) {
res.render('new')
})
para mostrar el formulario simplemente usamos una ruta de tipo get y hacemos render de la vista new.
router.post('/', function(req, res) {
var data = {
title: req.body.title,
description: req.body.description
}
var post = new Post(data)
post.save(function(err){
if(err) return console.log(err)
res.redirect('/posts')
})
})
la segunda ruta es de tipo post, esta ruta recibe los datos del formulario y para obtenerlos necesitamos acceder a ellos desde el req (request) y su atributo body
, creamos un objeto con estos datos y se lo mandamos a un constructor de Post, ahora intentaremos guardar ese nuevo objeto con post.save
este método solo nos regresara un error si es que algo salió mal, si todo sale bien mandaremos al usuario a la ruta principal "/posts".
Delete
Como podemos notar mongoose es muy fácil de usar y nos ahorra mucho trabajo a la hora de hacer peticiones a mongo, para eliminar un registro los pasos son similares, solo que en este caso necesitaremos el id del post que queremos eliminar y este id pasarlo a la función Post.remove
:
router.delete("/:id", function(req,res) {
id = req.params.id
Post.remove({ "_id": id }, function(err) {
if (err) return console.log(err)
res.redirect('/posts')
})
})
Esta ruta es de tipo delete ("/posts/:id") y podemos lograr esto gracias a la libreria de methodOverride
Update
Para finalizar necesitamos igualmente dos rutas para poder actualizar los registros:
router.get("/:id/edit", function(req, res) {
var id = req.params.id
Post.findOne({ "_id": id }, function(err, doc) {
if (err) return console.log(err)
res.render('edit', { post: doc })
})
})
necesitaremos una ruta get para mostrar el formulario y obtener el objeto que queremos actualizar, Post.findOne
regresara el primer registro que encuentre dependiendo del párametro que le indiquemos en este caso buscaremos un post por su id, si ese post existe hacemos render de la pagina edit y le pasamos el registro post para poder modificarlo.
router.put("/:id", function(req, res) {
var id = req.params.id
var data = {
title: req.body.title,
description: req.body.description
}
Post.update({ "_id": id }, data, function(){
res.redirect('/posts')
})
})
Esta ruta es un poco diferente, primero necesitamos obtener la data y el id del post que se quiere modificar, esta información se le pasa a la función Post.update
que recibe tres parámetros, el id del post a cambiar, la nueva información y una función.
Para terminar necesitamos exportar el router:
module.exports = router
para tenerlo disponible en la configuración del express y crear las rutas.
Creando las vistas
Para terminar crearemos las vistas de las paginas, estas van dentro de la carpeta app/views y tienen la terminación .pug:
Index
html
head
title Posts
link(rel="stylesheet" href="/style.css")
body
h1 Index
a(href="/posts/new") Crear Post
br
a(href="/posts") Lista de posts
Como podemos notar pug usa una sintaxis limpia sin la necesidad de usar <>
para cerrar las etiquetas, la desventaja es que es muy cruel cuando te equivocas en espacios o tabs.
Posts
html
head
title Posts
link(rel="stylesheet" href="/style.css")
body
h1 Posts
each post in posts
a(href="/posts/" + post.id + "/edit")= post.title
p= post.description
form(method="POST" action="/posts/#{post.id}?_method=DELETE")
button Delete
Pug permite tener loops dentro de html, como recordaremos a posts se le pasa un documento con todos los posts que se hayan creado, para poder acceder a la información e imprimirla en html necesitamos usar = post.title
si la información la necesitamos dentro de atributos como href no es necesario usar =
, en esta pagina también podemos notar a methodOverride en acción, en nuestro formulario hacemos una petición POST pero gracias a la libreria esta la cambia a delete permitiendo eliminar registros de una manera mas fácil
Edit
html
head
title Posts
link(rel="stylesheet" href="/style.css")
body
h1 Update Post
form(method="POST" action="/posts/" + post.id + "?_method=PUT")
label titulo
input(type="text" name="title" value=post.title)
br
label descripcion
textarea(name="description")= post.description
input(type="submit" value="Actualizar")
En esta pagina necesitamos pasar la información del post y tenerla en los formularios para saber que nos gustaría cambiar sobre el post, tal vez solo el titulo o detalles de la descripción, por eso es importante rescatar todos los datos del post.
New
html
head
title Posts
link(rel="stylesheet" href="/style.css")
body
h1 New Post
form(method="POST" action="/posts")
label titulo
input(type="text" name="title")
br
label descripcion
textarea(name="description")
input(type="submit" value="crear")
Esta pagina es muy parecida a edit con la diferencia que aquí necesitamos el formulario limpio para crear un post desde cero.