Generación de un proyecto que utilice express

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

Tenemos dos opciones:

  • Usar express-generator:La siguiente herramienta genera una estructura de carpetas y ficheros adecuada para realizar desarrollos con el módulo express. Para instalarla:
npm i express-generator --g

Para usuarla:

express proyecto --hbs //hbs indica el sistema de plantillas a utilizar
  • Creando el proyecto con express manualmente (recomendado):
package.json
{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "local": "cross-env NODE_ENV=development webpack --config webpack.config.js --watch",
    "start-local": "nodemon --watch build --exec \"node build/bundle.js\"",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
    "start": "node ./build/bundle.js",
    "heroku": "npm run build && git add . && git commit -m 'subida' && git push heroku master"
  },
  "author": "Pablo Monteserín",
  "license": "ISC",
  "dependencies": {
    "@babel/runtime": "^7.5.5",
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "easy-ftp": "^0.4.1",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mysql2": "^2.1.0",
    "sequelize": "^5.21.3",
    "socket.io": "^2.3.0"
  },
  "devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/plugin-proposal-class-properties": "^7.5.5",
    "@babel/plugin-proposal-decorators": "^7.7.4",
    "@babel/plugin-proposal-optional-chaining": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.5.5",
    "@babel/plugin-transform-spread": "^7.2.2",
    "@babel/preset-env": "^7.5.5",
    "@babel/runtime": "^7.5.5",
    "cross-env": "^5.2.0",
    "babel-cli": "^6.26.0",
    "babel-core": "^7.0.0-bridge.0",
    "babel-eslint": "^10.0.2",
    "babel-jest": "^24.8.0",
    "babel-loader": "^8.0.6",
    "nodemon": "^1.19.1",
    "terser-webpack-plugin": "^1.3.0",
    "webpack": "^4.37.0",
    "webpack-cli": "^3.3.6",
    "webpack-dev-server": "^3.7.2",
    "webpack-node-externals": "^1.7.2"
  }
}
webpack.config.js
const path = require('path');
const webpackNodeExternals = require('webpack-node-externals');
const TerserPlugin = require('terser-webpack-plugin');

const development = process.env.NODE_ENV === 'development';

// Constant with our paths
const paths = {
  OUTPUT: path.resolve(__dirname, 'build'),
};

module.exports = {
  entry: {
    bundle: './src/index.js',
  },
  target: 'node',
  node: {
    __filename: true,
    __dirname: true,
  },
  output: {
    path: paths.OUTPUT,
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader',
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  optimization: {
    minimizer: [
      new TerserPlugin({
        sourceMap: development,
        cache: true,
        parallel: true,
        terserOptions: {
          compress: true,
          ecma: 6,
          mangle: true,
        },
      }),
    ],
  },
  externals: [webpackNodeExternals()],
  mode: process.env.NODE_ENV,
  devtool: development && 'source-map',
};
./src/controllers/index.js
import Api from './api';

const Controllers = (app) => {
    Api(app);
};

export default Controllers;
.babelrc
{
    "presets": ["@babel/preset-env"],
    "plugins": [
      "@babel/plugin-transform-spread",
      "@babel/plugin-proposal-class-properties",
      "@babel/plugin-proposal-optional-chaining",
      "@babel/plugin-transform-runtime"
    ]
  }
./src/controllers/api/index.js
import * as Model from '../../models';
import { asyncHandler } from '../../middlewares/error-handler';
import jwt from 'jsonwebtoken';

const SECRET = 'uuuu';

const StartApiServer = (app) => {
    app.get('/', function (req, res, next) {
        res.send("va bien la cosa")
    });

    app.post('/alumnoIntroduceCodigo', asyncHandler(async (req, res, next) => {
        const cod = req.body.cod;
        if (!cod) return res.sendStatus(400);
        const students = await Model.searchStudentsFromClass(cod.trim());
        res.send(students);
    }));
}
./src/controllers/sockets/index.js
import socketIO from 'socket.io';
import * as Model from '../../models';
import { socketHandler } from '../../middlewares/error-handler';

const StartSocketServer = (server) => {
  const io = socketIO(server);
  io.on('connection', function (socket) {
    socket.on('alguienTermino', socketHandler(async (msg) => {
      await Model.heterminado(msg.alumnoId);
      io.emit('alguienTermino', msg);
  }));    
}
./src/models/database.js
const { Sequelize } = require('sequelize');

const dbHeroku = {
    host: 'remote.com',
    user: 'remoteUser',
    password: 'remotePassword',
    database: 'remoteDB',
    connectionLimit : 10,
};

const dbLocal = {
    host: 'localhost',
    user: 'root',
    password: '',
    database: 'databaseLocal'
}
const { database, user, password, host } = dbHeroku;
export default new Sequelize(database, user, password, {
    host,
    dialect: 'mysql',
});
./src/models/index.js
import db from './database';
const { QueryTypes } = require('sequelize');

export const searchStudentsFromClass = cod => db.query(
    'SELECT * FROM alumnos WHERE cod = :cod',
    { 
        replacements: { cod },
        type: QueryTypes.SELECT
    }
)
./src/middlewares/error-handler.js
/*Tenemos una función que recibe como parámetro la función de cada controlador,
la ejecuta (resolve)
y si  hay algún error, lo mostrará por consola y lanzará un 500
*/
export const asyncHandler = controller => (req, res, next) => Promise
  .resolve(controller(req, res, next))
  .catch((err) => {
    console.error('Request error:' + err.toString());
    if (err) return res.sendStatus(500).send();
});
 
export const socketHandler = controller => msg => Promise
  .resolve(controller(msg))
  .catch((err) => {
    console.error('Socket error:' + err);
});
./src/middlewares/index.js
import cors from 'cors';
import bodyParser from 'body-parser';

const Middlewares = (app) => {
    app.use(bodyParser.urlencoded());
    app.use(bodyParser.json());
    app.use(cors()) // Use this after the variable declaration
    app.use(function (req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        res.header('Access-Control-Allow-Credentials', true);
        res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    
        if (req.method === 'OPTIONS') {
            res.end();
        } else {
            next();
        }
    });
};

export default Middlewares;
./src/index.js
import express from 'express';
import http from 'http';
import Middlewares from './middlewares';
import Controllers from './controllers';

const app = express();
/*
Heroku utiliza la variable de entorno process.env.port para asignar el puerto donde se va ejecutar el servidor,
por eso la hemos definido en esta plantilla
*/
const port = process.env.PORT || 3005;

Middlewares(app);
Controllers(app);

const server = http.createServer(app);

server.listen(port, () => console.log(`Server listening to http://localhost:${port}`));

Para arrancar nuestra aplicación en el puerto 3000:

npm start

Si queremos parar node:

ps auxxx | grep node #Este comando nos permite ver los servicios de node que se están ejecutando
kill -9 1480 8573 #Paramos los servicios de node activos

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.