14

Construyendo nuestra primera MEAN App

Hola, en este tutorial crearemos una aplicación muy sencilla con el stack MEAN.
Antes de empezar quiero dejar el repositorio de GitHub: https://github.com/Jesussandrea12/mean-app
Y el Demo: https://mean-app.now.sh/posts

¿Pero que es el stack MEAN?
MEAN, Viene de MongoDB, Express, Angular y Node.js.
La ventaja de este, es que podemos crear una aplicación fullstack en un solo lenguaje.
Sip, adivinastes Javascript!!! 💪

Nota: En este tutorial uso Angular 5.

<h3>Requisitos</h3>
  • Tener instalado Node.js, si no, ve a la pagina oficial nodejs.org y descargalo.
  • Angular CLI (Command-line-interface o como se le conoce en español ‘la terminal’)
    lo instalamos ejecutando:
npm install -g angular-cli
<h3>Creando la aplicación</h3>

Una vez instalado Angular-CLI necesitamos crear la aplicacion para esto hacemos lo siguiente:

ng new mean-appcdmean-app
ng serve

Dentro del proyecto podemos ver una carpeta llamada src, Normalmente aquí es donde pasaremos el 80% de nuestro tiempo desarrollando nuestra app.

Luego de esto, podemos ir a nuestro navegador y abrir http://localhost:4200
Captura de pantalla (164).png

<h3>Añadiendo Express al proyecto</h3>

Ejecutamos

npm install --saveexpress body-parser

Luego creamos un archivo llamado server.js en el directorio root. En el archivo ponemos:

// Dependeciesconst express = require('express')
const path = require('path')
const http = require('http')
const bodyParser = require('body-parser')

// Create appconst app = express()

// API Routesconst api = require('./server/routes/api')
app.use('/api', api)

// Parsers for POST data
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')))

// Catch other routes and return index file
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'))
})

// Config Port 3000 or env.Portconst port = process.env.PORT || '3000'
app.set('port', port)

// Create HTTP serverconst server = http.createServer(app)

// Listen provided port
server.listen(port, () => console.log(`API running on localhost:${port}`))

Protip: A lo largo del tutorial hago comentarios en ingles, esto es debido a que el ingles es el idioma universal. Así cualquier persona puede entender nuestro código fácilmente.

Pudieron observar que coloque esta ruta * . Esto significa que cualquier otra ruta a la que el usuario vaya lo redireccionamos siempre a la principal.

server/routes/api.js
En el snippet anterior coloque una ruta /server/routes/api.js que no hemos creado. Vayamos a ello.
creamos una nueva carpeta en root llamada server y dentro de ella routes/api.js

// api.js
const express = require('express');
const router = express.Router();// api test
router.get('/', (req, res) => {
  res.send('api works');
});

module.exports = router;

Ahora probemos como va todo, pero antes corremos el comando

ngbuild

Esto va a crear una carpeta dist en root
y ahora asi levantamos nuestro servidor abriendo otra terminal con:

nodeserver.js

Nota: para no estar “prendiendo y apagando el servidor” podemos instalar con npm nodemon de la siguiente manera:

npm i -g nodemon

Y Ejecutamos

nodemon server.js

Esto nos permite escuchar cambios pero una vez que vayamos a hacer el deploy necesitaremos quitarlo ya que no es estable en entornos de produccion.

Ahora si vamos a probar nuestra app http://localhost:3000, y nuestra API http://localhost:3000/api

Deberia mostrar api works

<h3>Generando data</h3>

Una vez creada la API tenemos que mostrar algo, para esto usaremos (jsonplaceholder)[https://jsonplaceholder.typicode.com/]

<h2>OJO: En una aplicacion real debemos configurarla con una base de datos real, esto es solo un ejercicio practico.</h2>

Los invito a investigar un poco mas sobre el tema.

Siguiendo con la app, necesitamos hacer la peticion a jsonplaceholder usando axios

npm install--save axios

Y ahora sí, Volvemos al archivo api.js y lo actualizamos

const express = require('express')
const router = express.Router()

// axiosconst axios = require('axios')
const API = 'https://jsonplaceholder.typicode.com'

router.get('/', (req, res) => {
  res.send('api works')
})

// get post
router.get('/posts', (req, res) => {
  // This should ideally be replaced with a service that connects to MongoDB
  axios.get(`${API}/posts`)
    .then(posts => {
      res.status(200).json(posts.data)
    })
    .catch(error => {
      res.status(500).send(error)
    })
})

module.exports = router

Quedando asi.

Si vamos a http://localhost:3000/api/posts podemos ver el archivo json.

Captura de pantalla (166).png

Como esto parece una sopa de letras, yo uso (json-viewer chrome plugin)[https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh]. para verlo mas claro

<h3>Angular Component</h3>

Aqui vamos a usar los componentes de angular para crear la parte de los posts. creando el componente desde la terminal

cd src/app
ng generate component posts

Esto nos añade una carperta llamada posts asi como no añade la configuracion en nuestro app.module.ts Ahora simplemente añadimos la ruta. y vamos pensando en crear nuestro servicio colocando PostsService en providers.

import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { RouterModule } from '@angular/router';import { HttpModule } from '@angular/http';import { PostsService } from './posts.service';import { AppComponent } from './app.component';import { PostsComponent } from './posts/posts.component';// Define the routesconst ROUTES = [
  {
    path: '',
    redirectTo: 'posts',
    pathMatch: 'full'
  },
  {
    path: 'posts',
    component: PostsComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    PostsComponent
  ],
  imports: [
    BrowserModule,
    HttpModule,
    RouterModule.forRoot(ROUTES) // Add routes to the app
  ],
  providers: [PostsService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Lo que hice aqui fue que cuando el usuario vaya a / Angular lo detecte y lo redireccione a /posts
Ahora para que funcione en src/app/app.component.html añadimos router-outlet esto para que angular renderize la ruta

<divstyle="text-align:center"><h1class="title">{{ title }}</h1><imgwidth="300"alt="Angular Logo"src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="></div><router-outlet></router-outlet><footerclass="footer"><divclass="container"><divclass="content has-text-centered"><p>💻 with ❤️ by @YOUR_NAME</p></div></div></footer>

Para empezar a automatizar procesos vamos a nuestro package.json y en build ponemos

"scripts": {
    // Other scripts"build": "ng build && node server.js"
  },
<h3>Conectando cabos sueltos</h3>

Anteriormente colocamos por ahi un servicio que nunca lo creamos, ahora que es un servicio?
Segun el manual de las buenas practicas es recomendado manejar las llamadas http con un provider o service asi que generemos uno con Angular-cli

cd src/app
ng generate service posts

esto crea un archivo posts.service.ts, como ya lo declaramos en nuesto app.module.ts simplemente lo empezamos a editar.

import { Injectable } from'@angular/core';
import { Http } from'@angular/http';
import'rxjs/add/operator/map';

@Injectable()
exportclassPostsService{

  constructor(private http: Http) { }

    // Get posts from the api
    getAllPosts() {
      returnthis.http.get('/api/posts')
        .map(res => res.json());
    }
}

Luego simplemente lo importamos desde src/app/posts/posts.component.ts

import { Component, OnInit } from '@angular/core';import { PostsService } from '../posts.service';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {
  // declare posts as a empty arrayposts: any = [];

  constructor(private postsService: PostsService) { }

  ngOnInit() {
    // Retrieve posts from the APIthis.postsService.getAllPosts().subscribe(posts => {
      this.posts = posts;
    });
  }
}

Ya finalizando, ahora sí mostramos lo que nos devuelve nuestra api
src/app/posts/posts.component.html

<div class="container">
  <div class="columns" *ngFor="let post of posts">
    <div class="card">
      <header class="card-header">
        <p class="card-header-title">
          {{ post.title }}
        </p>
      </header>
      <div class="card-content">
        <div class="content">
          {{ post.body }}
        </div>
      </div>
      <footer class="card-footer">
        <a href="#" class="card-footer-item">Link</a>
        <a href="#" class="card-footer-item">Another link</a>
      </footer>
    </div>
  </div>
</div>

Antes que se me pase debemos instalar Bulma, Un framework de css excelente, se parece a bootstrap pero me parece mas lindo

npm i --save bulma

y lo importamos desde nuestro src/styles.css y le damos margen a las cartas

@import'../node_modules/bulma/css/bulma.css';

.card {
  margin: 1em!important;
}
<h3>Deploy a now.sh</h3>

para esto simplemente instalamos now-CLI con npm i -g now

configuramos nuesto package.json para produccion

{
  "name": "mean-app",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve dist/ --single",
    "build": "ng build --prod && node server.js",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^5.0.0",
    "@angular/common": "^5.0.0",
    "@angular/compiler": "^5.0.0",
    "@angular/core": "^5.0.0",
    "@angular/forms": "^5.0.0",
    "@angular/http": "^5.0.0",
    "@angular/platform-browser": "^5.0.0",
    "@angular/platform-browser-dynamic": "^5.0.0",
    "@angular/router": "^5.0.0",
    "axios": "^0.17.1",
    "body-parser": "^1.18.2",
    "bulma": "^0.6.2",
    "core-js": "^2.4.1",
    "express": "^4.16.2",
    "rxjs": "^5.5.2",
    "zone.js": "^0.8.14"
  },
  "devDependencies": {
    "@angular/cli": "1.6.0",
    "@angular/compiler-cli": "^5.0.0",
    "@angular/language-service": "^5.0.0",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.2.0",
    "tslint": "~5.7.0",
    "typescript": "~2.4.2"
  }
}

Y ejecutamos el deploy

now --public

tan simple como eso y tenemos nuestra aplicacion en linea.

Captura de pantalla (167).png

Waoo, Si llegastes hasta acá, felicidades! Lo has logrado. Dejame en los comentarios que piensas de este tutorial si te gustó dale like y dejame el link del proyecto. Si quieren extenderse pueden configurar el proyecto con MongoDB.

Tambien valoro las sugerencias e ideas

¡Muchas gracias y hasta la proxima!

Escribe tu comentario
+ 2