Vicente Rodríguez

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.