Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Typescript Support #16

Open
mastepanoski opened this issue Jul 15, 2022 · 1 comment
Open

Adding Typescript Support #16

mastepanoski opened this issue Jul 15, 2022 · 1 comment

Comments

@mastepanoski
Copy link

mastepanoski commented Jul 15, 2022

You could add support to typescript, rewriting withRest or adding compatibility through index.d.ts:

  import type { NextApiRequest, NextApiResponse } from 'next'
  import Boom from '@hapi/boom'
  import Joi from 'joi'
  
  const defaultLogError = (err: Boom.Boom) => {
    // Only log internal server errors
    if (!err.isServer) {
      return
    }
  
    // Log original error if passed
    if (err.data && err.data.originalError) {
      err = err.data.originalError
    }
  
    console.error(err.stack)
  }
  
  const defaultSendError = (res: NextApiResponse, err: Boom.Boom) => {
    const { output } = err
    const { headers, statusCode, payload } = output
  
    Object.entries(headers).forEach(([key, value]) =>
      res.setHeader(key, value || '')
    )
  
    res.status(statusCode).json(payload)
  }
  
  /**
   * Wraps a HTTP request handler with validation against Joi schemas.
   *
   * @param {object} schemas - An object with `query`, `body` or `headers` keys and their associated Joi schemas.
   *                           Each of these schemas will be matched against the incoming request.
   *
   * @returns {Function} The HTTP handler that validates the request.
   *
   * @example
   *
   * const getSchema = {
   *   query: Joi.object({
   *      id: Joi.string().required(),
   *   }),
   * };
   *
   * export default withRest({
   *   GET: withValidation(getSchema)(async req, res) => {
   *     // Do something with `req.query.id`
   *
   *     return { foo: 'bar' };
   *   },
   * });
   */
  export const withValidation =
    <T = any, R = any>(schemas: Joi.PartialSchemaMap<T> | undefined) =>
    (fn: (arg0: NextApiRequest, arg1: NextApiResponse<R>) => Promise<any>) =>
    async (req: NextApiRequest, res: NextApiResponse) => {
      const joiSchema = Joi.object(schemas).unknown(true)
  
      let validated: { [x: string]: any }
  
      try {
        validated = await joiSchema.validateAsync(req)
      } catch (err: any) {
        throw Boom.badRequest(err.message, { originalError: err as Error })
      }
  
      // Joi normalizes values, so we must copy things back to req
      ;['headers', 'body', 'query'].forEach((key: string) => {
        ;(req as any)[key] = validated[key]
      })
  
      return fn(req, res)
    }
  
  /**
   * @typedef {Function} SendError
   *
   * @param {object} res - Node.js response object.
   * @param {Error} err - The Boom error object.
   */
  /**
   * @typedef {Function} LogError
   *
   * @param {Error} err - The Boom error object.
   */
  
  /**
   * Matches handlers defined in `methods` against the HTTP method, like `GET` or `POST`.
   *
   * @param {object.<string, Function>} methods - An object mapping HTTP methods to their handlers.
   * @param {object} options - The options.
   * @param {SendError} options.sendError - A function responsible to send Boom errors back to the client.
   * @param {LogError} options.logError - A function that logs errors.
   *
   * @returns {Function} The composed HTTP handler.
   *
   * @example
   *
   * export default withRest({
   *   GET: async (req, res) => {
   *     // Do something...
   *
   *     return { foo: 'bar' };
   *   },
   * });
   */
  const withRest = (
    methods: {
      [x: string]: any
    },
    opts: {
      logError?: typeof defaultLogError
      sendError?: typeof defaultSendError
    } = {
      logError: defaultLogError,
      sendError: defaultSendError,
    }
  ) => {
    const options = {
      logError: defaultLogError,
      sendError: defaultSendError,
      ...opts,
    }
  
    return async (req: NextApiRequest, res: NextApiResponse) => {
      try {
        const method = methods && methods[req.method || 'unknown']
  
        if (!method) {
          throw Boom.methodNotAllowed(
            `Method ${req.method} is not supported for this endpoint`
          )
        }
  
        const json = await method(req, res)
  
        // Do nothing if the request is already sent (e.g.: a redirect was issued)
        if (res.headersSent) {
          if (json !== undefined) {
            options.logError(
              Boom.internal(
                'You have sent the response inside your handler but still returned something. This error was not sent to the client, however you should probably not return a value in the handler.'
              ) // eslint-disable-line max-len
            )
          }
  
          return
        }
  
        // Next.js doesn't support nulls as `RFC7159` dictates, but we do
        if (json == null) {
          res.setHeader('Content-Type', 'application/json')
          res.setHeader('Content-Length', '4')
          res.end('null')
        } else {
          res.json(json)
        }
      } catch (err: Error | Boom.Boom | any) {
        // Not an ApiError? Then wrap it into an ApiError and log it.
        let apiError = err
        if (!err.isBoom) {
          apiError = Boom.internal(undefined, { originalError: err })
        }
  
        options.logError(apiError)
        options.sendError(res, apiError)
      }
    }
  }
  
  export default withRest
  
@DavideCarvalho
Copy link

I think this library is not maintained anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants