Techie-Qabila / fastify-mongoose

Fastify Mongoose plugin
MIT License
11 stars 11 forks source link

some examples #1

Open andrevandal opened 6 years ago

andrevandal commented 6 years ago

Hi, could u add any examples of mongoose schema use?

tomclark commented 6 years ago

Some examples would really be helpful.

I define my server and register fastify-mongoose in server.js, my route in route.js, my controller in another file, and my model in its own file. I don't quite understand how to pass the database connection between them all. I can do it using Mongoose on its own, I just don't understand quite how to do it with fastify and mongoose.

andrevandal commented 6 years ago

@tomclark I think this can help you:

const fastify = require('fastify')()
const { Schema } = require('mongoose')

// Declare a route
fastify.get('/', function (request, reply) {
  reply.send({ hello: 'world' })
})

fastify.register(require('fastify-mongoose'), {
  uri: 'mongodb://test:test@localhost/test'
}, err => {
  if (err) throw err
}).after(() => {
  var schema = new Schema({
    foo1: {
      type: String,
      required: false
    },
    bar1: {
      type: String,
      required: false
    }
  })
  let Model = fastify.mongo.db.model('model', schema)
  let m1 = new Model({
    foo: 'value_for_foo',
    bar: 'value_for_bar'
  })
  m1.save(function (err) {
    if (err) {
      console.log(err.stack)
    }
    console.log('saving done...')
  })
})

// Run the server!
fastify.listen(3000, function (err) {
  if (err) throw err
  console.log(`server listening on ${fastify.server.address().port}`)
})
tomclark commented 6 years ago

Legend, thank you!

I actually managed it in the end in a slightly different way where I connect to the database in my db module, import it to the server, and then wrap my server with the db connection. Like this:

// db.js
mongoose.connect(config.db.uri, opts)
const db = mongoose.connection
db.on('error', err => {
  if (err.message.code === 'ETIMEDOUT') {
    mongoose.connect(config.db.uri, opts)
  }
})

module.exports = db

Then in the server:

// server.js
import db from './db'

db.once('open', () => {
  /**
   * Bind routes
   */
  server.register(BaseRoute)
  server.register(BaseRoute, { prefix: '/api/v1' })

  // Run the server
  const start = async () => {
    try {
      await server.listen(config.port, '0.0.0.0')
    } catch (err) {
      server.log.error(err)
      process.exit(1)
    }
  }

  start()
})

Then later in the routes I can just do something like this:

  async detail (itemId) {
    return ItemModel.findById(itemId).lean()
  }

I can then just use models etc in my routes and controllers by importing mongoose into them.

Not sure it's quite the right way to do it, but it works just fine.

Thanks for coming back to me.

andrevandal commented 6 years ago

Start server only if the connection to the database is open seems to me a problem and it won't help with microservices.

This module import mongoose through fastify decorator, so, mongoose will be avalible for all application through fastify.mongo.db (connection).

Maybe I'll fork this plugin to improve some things like schema and mongoose plugins.

About your code, fastify is architected for microservices. So, reusing code through decorators, hooks and registers will improve your performance and development speed.

tomclark commented 6 years ago

But if the database connection is required for any API call at all, am I not better waiting until it's open before bringing up the server? i.e. there are no API calls in my app where you don't hit the database.

Am obviously a newbie, so I appreciate the tips, thank you. I'll have a look at refactoring as you suggest. This is my first project with Fastify, so I'm grateful for your advice.

andrevandal commented 6 years ago

It's my first too 😃 When you use process.exit(1) your application will close if db is unreachable. Your application may be reachable even if the database is not. You can send a 501 error informing you that the server is unavailable.

Imagine this scenario:

Do you understand me?

tomclark commented 6 years ago

@derevandal So I think I have a slightly more appropriate way of doing this. Would appreciate your feedback.

I have a models module that I register with the Fastify server object like this:

server.register(mongoose, {
  uri: config.db.uri
})
server.register(models)
server.register(route)

models.js contains:

import MyModel from '../Models/MyModel.model'

export default async function models (server) {
  server.register(MyModel)
}

Where MyModel.js looks something like this:

import { Schema } from 'mongoose'

export default async function (server) {
  const myModel = new Schema(
    {
      username: { type: String, trim: true },
      someNumber: { type: Number, unique: false, required: true },
    },
    { collection: 'MyModel' }
  )

  return server.mongo.db.model('MyModel', myModel)
}

Then in my route.js, I do:

server.get('/mymodel/:id', async (req, res) => {
  try {
    const models = await new GetMyModelController(server.mongo.db.models.MyModel).detail(req.params.id)
    SuccessResponse(
      res,
      `${res.length}`,
      models
    )
  } catch (exception) {
    if (!parseInt(req.params.id, 10)) {
      BadRequestResponse(res, 'Invalid ID') // in case someone gets /mymodel/foo
    } else {
      InternalServerErrorResponse(res, exception, 500)
    }
  }
})

And finally, in the controller, I do:

export class GetMyModelController {
  constructor (MyModel) {
    this._myModel = MyModel
  }

  async detail (ModelId) {
    return this._myModel.findById(ModelId).lean()
    .then(model => {
      if (model !== null) {
        return model
      } else {
        throw new NotFoundException('modelId not found')
      }
    })
  }

Some code/edited for brevity/relevance, so there may be errors, but hopefully it gets across the approach. The various response objects just return some json and an HTTP code.

I realise I could simplify it by getting the model directly in the route rather than via a controller, but there's other reasons for having the controller, though they're not that important, so I guess I could drop the controller in favour of a leaner approach.

Does that look a little more like how one should do it in Fastify? It works, but as I say, I'm grateful for your feedback as to whether that's correct, as I don't have anyone else to do code review.

tomclark commented 6 years ago

Sorry, posted that before I'd seen your reply about the database - yep, gotcha, that makes sense.

traaidmark commented 6 years ago

@tomclark I realise that this is super late, and perhaps a bit irrelevant, but then again this is an issue to describe examples (which could hopefully make it to the readme?).

Anyways, I found your example above to be super useful (and simple to understand) man, thanks for that.

Just a simple high-five.

tomclark commented 6 years ago

Glad it helped! :-)

mandaputtra commented 5 years ago

Okay okay this should be on the README.md