code: async function deferSession(ctx, next) {
      ctx.sessionStore = store
  
      // TODO:
      // Accessing ctx.session when it's defined is causing problems
      // because it has side effect. So, here we use a flag to determine
      // that session property is already defined.
      if (ctx.__isSessionDefined) {
        return next()
      }
      let isNew = false
      let originalHash = null
      let touchSession = false
      let getter = false
  
      // if path not match
      if (!matchPath(ctx)) {
        return next()
      }
  
      addCommonAPI(ctx)
  
      Object.defineProperty(ctx, 'session', {
        async get() {
          if (touchSession) {
            return this._session
          }
          touchSession = true
          getter = true
  
          const result = await getSession(this)
          // if cookie path not match
          // this route's controller should never use session
          if (!result) return
  
          originalHash = result.originalHash
          isNew = result.isNew
          this._session = result.session
          return this._session
        },
        set(value) {
          touchSession = true
          this._session = value
        }
      })
  
      // internal flag to determine that session is already defined
      ctx.__isSessionDefined = true
  
      ctx.regenerateSession = async function regenerateSession() {
        debug('regenerating session')
        // make sure that the session has been loaded
        await ctx.session
  
        if (!isNew) {
          // destroy the old session
          debug('destroying previous session')
          await store.destroy(ctx.sessionId)
        }
  
        ctx._session = generateSession()
        ctx.sessionId = genSid.call(ctx, 24)
        sessionIdStore.reset.call(ctx)
        debug('created new session: %s', ctx.sessionId)
        isNew = true
        return ctx._session
      }
  
      await next()
  
      if (touchSession) {
        // if only this.session=, need try to decode and get the sessionID
        if (!getter) {
          ctx.sessionId = sessionIdStore.get.call(ctx)
        }
  
        await refreshSession(ctx, ctx._session, originalHash, isNew)
      }
    }
docs: defer session middleware
  only generate and get session when request use session
  
  ```
  let session = yield this.session
  ```