

EXPRESS.ROUTER()
&
COMMONJS


Vamos a cobrar más

EXPRESS.ROUTER()
Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
Repaso Live Coding
Primera API con 2 recursos
Queremos desarrollar una api restful con node para un proyecto copia de twitter. Aunque de momento, será solo una primera versión con usuarios y tweets como si fuese un blog personal.
USUARIOS
username* : string<uniq>
name? : string
email* : string
tweetsIDs : string[]
TWEETS
id* : string<uniq>
text : string
owner : string<ID>
createdAt : timestamp
Esto sería un id de los tweets que este usuario había creado
"2 tablas"
Primera API
- Deberán estar implementadas las funcionalidades de:
- Crear un nuevo usuario
- Borrar un usuario
- Editar el email de un usuario o el nombre (PATCH)
- Subir un tweet nuevo por parte de un usuario
- Ir a buscar un tweet en concreto por su id
- Borrar un tweet por su id
- *Opcional: Obtener todos los tweets ordenados tanto asc como desc por la fecha de subida
- *Opcional: Guardar los tweets y los usuarios en un fichero db.json y actualizarlo con cada cambio.
- OBLIGATORIO: Usar router para cada recurso
OJO ! CONTROLA QUE LA API NO SE PUEDA QUEDAR PILLADA EN NINGÚN MOMENTO, controla campos vacíos, request inválidas, etc etc y devuelve el error en la respuesta y el código correcto para dicho caso
¡UN PASO MÁS ALLÁ !

MONGOOSE TE PERMITE PONER VALIDACIONES
const mongoose = require('mongoose');
var TODOschema = mongoose.Schema({
text: String,
id: String,
createdAt: Number,
isCompleted: Boolean
});
var TODO = mongoose.model('todo', TODOschema);
module.exports = TODO;

Pues podemos meterle validaciones !
var breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Too few eggs'],
max: 12
},
bacon: {
type: Number,
required: [true, 'Why no bacon?']
},
drink: {
type: String,
enum: ['Coffee', 'Tea'],
required: function() {
return this.bacon > 3;
}
}
});
¿Cómo uso esto?
BreakfastModel.create(req.body)
.then( creado => res.send(creado))
.catch(error => {
// Diferentes posibles errores aquí
res.status(400).send(err);
})
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
username : {
type : String,
unique : true,
required: true,
minLength : 2
},
email : {
type : String,
required: true,
validate : (email) => emailValid(email)
},
name : String,
tweetsIDs : [String]
})
function emailValid(email){
return /^\S+@\S+\.\S+$/.test(email)
}
const userModel = mongoose.model('user', userSchema)
module.exports = userModel;
Otra ejemplo más
var userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v) {
return /\d{3}-\d{3}-\d{4}/.test(v);
},
message: '{VALUE} is not a valid phone number!'
},
required: [true, 'User phone number required']
}
});

var cors = require('cors');
var app = require('express')();
app.use(cors());
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
var app = require('express')();
var cors = require('cors')
var corsOptions = {
origin: ['http://example.com' , 'http://example2.com' ]
}
app.use(cors(corsOptions));
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
Embebed Documents

const userSchema = mongoose.Schema({
...
tweetDelDia : TweetSchema,
tweets : [TweetSchema]
})
Esto sería embebido
const userSchema = mongoose.Schema({
...
tweets : [TweetSchema]
})
Ten cuidado amiguito de youtube
¿Necesitas también tener una colección de tweets?
Sí
No
Tendrás que mantener actualizados ambos tweets siempre entonces ... :S peligro
No pasa nada, es un array de objetos, de momento, solo de momento, pinta bien

Related documents

tweets : [
{
type : mongoose.Schema.Types.ObjectId,
ref : 'tweet'
}
]

Populate
tweets : [
{
type : mongoose.Schema.Types.ObjectId,
ref : 'tweet'
}
]
function getOneByUsername(req, res) {
const { username } = req.params;
return userModel
.findOne({ username })
.populate('tweets')
.then(u => res.json(u) )
.catch(e => res.status(500).json(e) )
}

Sin el populate
Con el populate

Triggers
userSchema.pre('remove', function(next) {
TweetsModel.remove({owner: this._id}).exec();
next();
});
Borrar los datos relacionados
Mongoose middlewares (triggers)
Projections
Dame todos los tweets (sin proyección)

tweetsModel
.find( {} )
Dame todos los tweets (con proyección)

tweetsModel
.find()
.select({text : 1})
tweetsModel
.find(
{},
{ text : 1 }
)
tweetsModel
.find()
.select("text")
tweetsModel
.find(
{},
"text"
)

Sin proyección
Con proyección

return userModel
.findOne({ username })
.select({ tweets : { $slice : 3}})
.then(u => res.json(u) )
.catch(e => res.status(500).json(e) )
Con proyección y con populate
return userModel
.findOne({ username })
.select({ tweets : { $slice : 3}})
.populate('tweets')
.then(u => res.json(u) )
.catch(e => res.status(500).json(e) )

EXTRA: Con proyección y con populate
return userModel
.findOne({ username })
.select({ tweets : { $slice : 3}})
.populate('tweets', "text")
.then(u => res.json(u) )
.catch(e => res.status(500).json(e) )

EXTRA: Con proyección y con populate
return userModel
.findOne({ username })
.select({ tweets : { $slice : 3}})
.populate('tweets', "text owner")
.then(u => res.json(u) )
.catch(e => res.status(500).json(e) )

Mongoose paginating results
MyModel.find(query, projection, { skip: 10, limit: 5 })
.then(results => res.json(results))
.then(err => res.status(500).json(err))
MyModel.find(query, projection, { skip: 10, limit: 5 })
.then(results => res.json(results))
.then(err => res.status(500).json(err))
- skip. Sirve para indicar cuantos elementos de los resultantes tiene que "ignorar" o saltar.
- limit. Indica cuantos elementos quieres como resultado.
Query Params





Pidiendo info filtrada
Pidiendo info filtrada
¿Diferencia?
Pidiendo info filtrada

Pedir información paginada

Pedir información ordenada

Responder con información ordenada
function getAll(req, res) {
const { _skip, _limit } = req.query;
return userModel.find()
.limit(+_limit)
.skip(+_skip)
.then(async u => {
const totalCount = await userModel.find().count();
const totalPages = totalCount / +_limit;
res.json({results : u, totalItems : totalCount, totalPages })
})
.catch(e => res.status(500).json(e) )
}
SPEGC Agosto 2022 Full Stack. Mongoose Validaciones y Linked Data Día 4
By Yunior González Santana
SPEGC Agosto 2022 Full Stack. Mongoose Validaciones y Linked Data Día 4
Aprendemos a montar buenos Schemas de mongoose con validaciones
- 207