vercel / micro

Asynchronous HTTP microservices

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Async actions before the server starts

cjhowedev opened this issue · comments

There are a number of async actions you might want to do before the server starts. While this works fine if you create the listener itself, it precludes using things like micro-dev or the micro binary with this package. It'd be nice if we could include some async "setup" steps somehow. Maybe something like this?

const db = new DB()
module.exports = async (req, res) => await db.getSomeAsyncData()
module.exports.setup = async () => { await db.connect() }
module.exports.teardown = async () => { await db.disconnect() }

EDIT: added teardown

Maybe this should also include an action for shutting down. micro-dev would need to shut down and restart the database service in the above example.

Probably you know, you can use micro programmatically something like that:

const micro = require('micro')
const sleep = require('async-sleep')

// here your request handler
const handler = async (req, res) => {
  return 'Hi'
}

const createServer = () => {
  const server = micro(handler)
  server.listen(process.env.PORT || 3000, () => {
    console.log('Running on http://localhost:3000')
  })
}

const setup = async () => {
  // setup db your connections here
  await sleep(3000)
}

const main = async () => {
  await setup();
  console.log('Setup completed')
  createServer()
}

main()

For graceful shutdown you can use process events such exit, SIGINT and other.

For graceful shutdown you can use process events such exit, SIGINT and other.

This doesn’t work with micro-dev because it doesn’t use a separate process.

Looks like not only process signals are not working in this case.
If you will use micro programmatically, micro-dev and micro runners will not work.

Another question is do you really need setup connection before your server is running and keep it in runtime? micro is good for granular microservices, so probably you no need keep db connection all time while your microservice is running. Just setup new one as soon as it's really needed.

Correct me if I'm not right.

The database was just an example. I think shutting down the connection is actually more important for making micro-dev work. If you use something like next.js, then it will start a new webpack instance each time the server is restarted. You could do the initialization in the handler itself, but there is no way to shut down next.js instance with micro-dev once the reference is lost (after it drops the require cache). I also don't think leaving a bunch of connections to the database open during development is a great solution either.

I guess you can do something like this to ensure prework is done before request:

const dbPromise = new Promise((resolve, reject) => {
    resolve(new Db()); // Or something async
});

module.exports = async (req, res) => {
    const db = await dbPromise();
    // Do some work with db
}

Teardown is a little trickier – you could just listen to SIGNUSR2 signal:

process.once('SIGNUSR2', function () {
    dbPromise.close();
});

@floatdrop SIGNUSR2 signal not working. I support @cjhowe7 suggestion.

@ansarizafar check, that you have fresh version of micro-dev, SIGNUSR2 was released not so long time ago – vercel/micro-dev@b0281bf

Also chiming in to voice my support for the original suggestion. I'm currently using the implementation that @floatdrop has suggested, but I feel that @cjhowe7 proposal would be cleaner and more self-descriptive. It also removes the need for checking prior to every request that the initialisation has already been done.