Skip to content

v2.0.0

Compare
Choose a tag to compare
@jacobwgillespie jacobwgillespie released this 02 Mar 13:03
· 51 commits to main since this release
6f045b6

Breaking Changes

Version 2.0 represents a large refactor of the entire API surface, most methods have changed in some way, though not all is new.

ServerRequest

ServerRequest no longer overrides url from http.IncomingMethod- zap 1.0 overrode that string property with a URL. In zap 2.0, a new property parsedURL is provided instead:

- req.url.searchParams
+ req.parsedURL.searchParams

Handlers

The next argument on Handler and RouteHandler has been removed, a handler's signature is now:

(req: ServerRequest, res: ServerResponse) => void | ResponseBodyType | Promise<ResponseBodyType>

If you previously depended on explicitly calling next, you should instead use regular function composition:

const wrapped: Handler = (req, res) => {}
const composed: Handler = (req, res) => {
  await wrapped(req, res)
  ...
}

The route method no longer accepts a body validation function as the fourth optional argument. You should instead perform validation inside the route function itself, and potentially throw an error if validation fails:

async function parseBody(req: ServerRequest) {
  const body = await json(req)
  if (!validate(body)) throw httpError(400, 'invalid body')
  return body
}

route('POST', '/example', (req) => {
  const body = await parseBody(req)
  // body is now typed according to your parseBody return type
})

Errors

serve(handler, {onError} has been replaced by serve(handler, {errorHandler}), which is a full Handler with access to the request and response. This function can be used to report errors elsewhere, format error responses, etc:

serve(handler, {
  errorHandler: (req, res, error) => {
    send(res, 500, {message: 'Internal server error', details: formatError(error)})
  },
})

sendError has been removed, in favor of simply throwing the error instead (the error will be handled by the errorHandler):

- return sendError(res, error)
+ throw error

HttpError is now a class rather than an interface, with a metadata field of type unknown rather than an originalError Error field:

if (error instanceof HttpError) {
  error.statusCode
  error.message
  error.metadata
}

createError has been renamed to httpError:

- export function createError(code: number, message: string, original?: Error): HttpError
+ export function httpError(code: number, message: string, metadata?: unknown): HttpError

The notFound helper no longer needs access to req:

- notFound(req)
+ notFound()

Returning an Error from a handler, or sending an Error via send would previously have tried to interpret the Error as a generic object. Zap will now throw errors returned from handlers so that they are caught in the error handler:

route('GET', '/example', () => {
  // Previously this was interpreted like returning an object, now
  // this will throw the error to be caught by the `errorHandler`
  return new Error('example')
})

New Features

A new RedirectError and redirect(location, statusCode) helper have been added to make it easy to "throw" a redirect:

route('GET', '/example', () => {
  // defaults to a 303 status code
  throw redirect('/somewhere')

  // custom status code
  throw redirect('/somewhere', 301)
})

A new fromRequest helper is provided to construct functions that cache their result for a given request, allowing you to create your own helper functions efficiently:

const currentUser = fromRequest(async (req) => {
  const user = await findUser(req)
  if (!user) throw httpError(403, 'Not authorized')
  return user
})

route('GET', '/example', async (req) => {
  const user = await currentUser(req)
  ...
})

What's Changed

Full Changelog: v1.1.1...v2.0.0