You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
hapi v8.0.0 is a deep refactoring of the entire framework and a major cleanup of its API. The
main goal for this release was to simplify the developer experience, make it more predictable, and reduce the number of methods and interfaces exposed. This version is much easier to learn without skipping all the powerful extensibility that make the framework great, but has been hard for new adopters to follow.
Upgrade time: moderate to high - a couple of days to a week for most users
Complexity: moderate - a very long list of changes, all well understood with no side effects
Risk: moderate - no side effects but a lot of changes to keep track of
Dependencies: moderate to high - every plugin must be verified to be compatible or upgraded
Breaking Changes
Pack, server, and plugin interfaces all merged into a single Server interface.
Validation failAction function signature changed from function(source, error, next) to function(request, reply, source, error)
Removed all special handling of the HTTP Location header
Changed the request id format
Server method generateKey function must return a string or value is considered an error (previously supported returning null to indicate no caching) and no longer receives the callback argument (all arguments are part of the key including the last)
Plugin interface changes:
servers is now connections
length removed
Logging:
Internal request logging now emitted on a separate channel 'request-internal'
The 'hapi' event tag has been replaced with a new event.internal flag for server logs
'internalError' event is now 'request-error'
Most internal logs tags changes to be more consistent and descriptive
debug option split between request and log for each type
Removed APIs:
Hapi.Pack
Hapi.version
Hapi.createServer()
Hapi.error
Hapi.state.prepareValue()
compose()
the hapi command line interface
Changed the default connection port from 80 and 443 to 0
app configuration no longer copied over to the initial app state
Moved from connection level to server level:
files.etagsCacheMaxSize
debug
load.sampleInterval
timeout.client replaced by payload.timeout
State failAction replaced by ignoreErrors at the cookie level (still available at the route level)
Skip 'onPreResponse' when the request closes prematurely
Removed the maxSockets configuration option (replaced by per-route proxy option via h2o2)
Composer format changes (via glue)
Changed the reply() interface to always signal a response and add reply.continue() to signal continuation of the request lifecycle
Switch UNIX domain sockets and Windows named pipes to use port instead of host option
the host connection option is split into two values host and address
Removed log options in authentication data result
request.responses changed to request.preResponses
register() method only accepts register(), item with a register() property, or an item with a register() and options properties
server.method() using options object syntax changed fn option to method
server.cache() no longer takes name, only options (with segment property)
Removed hapi properties
Removed cache segment name requirements (e.g. '##', '!!')
app and plugins settings are deeply copied (instead of shallow copy)
Default ETags set with etag() to vary based on content encoding
Response object option marshall changed to marshal
Removed the 'h:' prefix from server method cache keys generated internally
Renamed views config basePath to relativeTo
Router logic tweaks handle route priority based on comparison of each path segment at a time
Renamed connection config cacheControlStatus to cache.statuses
Reorganized all routes related configuration under a new connection routes config setting defaults
Change server.config to server.realm.modifiers
Change route object in properties and return values to a new structure.
server.expose() only sets server level plugin properties.
Removed logging on non-cached server methods called using pre or handler string notation.
Server validation option moved to connections.routes.validate.option.
New Features
Promises support in reply() and server methods
Support external listeners and manual listen (SPDY)
Extensible reply() and server interface methods
Routing support for complex path segments (e.g. /a{b}c{d?}e)
Mime database customization
Add response time to request information
Add created and started times to connection information
Response validation of errors and custom validation base on status code
Include the request object as argument to validation failAction method and replaced next with full reply() interface support
Allow using response validation to modify the response (type casting and properties changes)
Improved request logging and reduced noise
Allow authentication schemes to mandate payload validation
Add unique connection id using the format '{hostname}:{pid}:{now base36}'
Improve request id using the format '{now}:{connection.info.id}:{5 digits counter}'
Support server-level connections defaults
Support synchronous server methods (including cached)
Expose default authentication strategy settings
Add cors.override to disable overriding excising CORS headers (from proxy or manual)
Expose request.paramsArray
Add response close processor option
Enable each route to override every route specific configuration and allow setting a server and connection global defaults for routes.
Route lookup by id (server.lookup()) or by path (server.match()).
Expose realms as a public interface.
Support constructing a bare server without extra features (files, directories, proxy, views).
Include auth in input validation context
Override info.uri connection setting
Bug fixes
Log an error when calling reply() twice in a handler
Fixed reply.file() and reply.redirect() in extension methods
Stop cache when server stops
Log an error when failed to generate a unique key for a server method call
Include ETag and Last-Modified headers in 304
Fix ETag varying based on content-encoding and handling of conditional requests
CORS matchOrigin returns the incoming 'Origin' header when configured to '*'
Skip compressing empty payloads
Custom validation function options run protected
Allow turning scope off (when set with default)
Fixed cached method in string notation which bypassed the cache incorrectly
Fixed passing the reply() interface to string notation methods with no arguments
Updated dependencies
joi from v4.7.0 to v5.0.1
boom from v2.5.1 to v2.6.0
catbox-memory from v1.1.0 to v1.1.1
qs from v2.3.2 to v2.3.3
mime-db from v1.1.2 to v1.2.0
catbox from v4.1.0 to v4.2.0
call from v1.0.0 to v2.0.1
statehood from v1.2.0 to v2.0.0
Migration Checklist
The Basics
It is highly recommended to read (again) the complete API reference. It is long, but it is the best way to full appreciate the changes in this release. You can skim through the configuration options but take a look at the examples for each method. While most individual methods remained the same, most of them moved around between parent interfaces.
The most significant change in v8.0.0 is the consolidation of the pack, server, and plugin interfaces. hapi now exposes a single parent API, the Server. Everything else is available as a method of the server, or via separate modules (boom for errors, statehood for cookie manipulation, rejoice for the command line executable, and glue for the application composer).
Checklist:
If you are using Pack.compose(), switch to use glue.
Note that the composer format changed (pack -> server, servers -> connections).
If you are using the bin/hapi command line, switch to use rejoice.
Note that the composer format changed (pack -> server, servers -> connections).
Replace Hapi.error calls with direct called to boom like Boom.badRequest().
If you are using Hapi.state.prepareValue(), switch to use statehood.
If you are using Hapi.version, obtain the version value directly from the package.json file.
If you are using any of the hapi properties (server, request, plugin), there should no longer be any reason to do so given that the only thing exposed from the module is Server. If you are creating another server, require hapi directly.
The configuration options app and plugins are deeply copied (instead of shallow copy). This affects the server, connection, and route configuration objects. If you pass any value that cannot be safely cloned, remove those values and consider setting them directly to the runtime state container (e.g. server.app).
Creating a server
In previous versions, the server represented a single connection and the pack a collection of
connections. Instead, the server now represents your application, to which you add connections using the server.connection() method. You can add multiple connections to your server. For convenience, a server with a single connection is decorated with the inject() method along with the info property. This makes most code work as is by simply adding a call to server.connection().
Note that unlike previous server API which accepted multiple arguments (for host, port, and options), the new server.connection() method accepts a single options argument where host and port are optional properties.
The default port is no longer 80/443 but 0. This means omitting the port when creating a connection will bind the server to the next available port. If you relied to on the port being set to 80/443, you need to manually configure your connections with the desired value.
While the default host value still bind to all available network interfaces ('0.0.0.0'), the default value of server.info.host is now set to the operating system hostname if available otherwise to localhost. A new configuration value address is provided to separate the value used to bind the listener (address) from the value used to set server.info.host and server.info.uri. If address is not available, host is used and if not available, '0.0.0.0'.
Checklist:
Replace Hapi.Pack with Hapi.Server and call server.connection() instead of pack.server().
server.pack is now just server.
Replace calls to Hapi.createServer() with direct new Hapi.Server()
Change server construction to include server.connection() with host and port passed as options. If you relied on the default host or port values, set those manually. Consider using address and host with different values when you need to set the internal IP address for binding (address) and a public host used by plugins (host).
Switch UNIX domain sockets and Windows named pipes to use port instead of host option.
Server configuration
The following configuration options moved from the connection to the server:
files.etagsCacheMaxSize
debug
load.sampleInterval
When constructing a server, you can now set default configuration for every connection added to it using the connections option property.
The connection maxSockets configuration option used by the proxy handler is no longer available. Instead, set the maxSockets option of the proxy handler configuration.
Checklist:
Review the options passed to each server and connection and ensure the values are set at the right level. The framework will throw if you configure it incorrectly indicating which option does not belong.
The app server and connection configuration option is no longer copied over to the initial app state of the object. If you need to access the app value provided in the server or connections settings, use server.settings.app and connections.settings.app. Note that these values are considered static and should not be modified. If you need to store runtime state, use server.app and connection.app.
Connection configuration
Most of the options in previous versions (e.g. server configuration) were actually not about the connection but about the routes in it (e.g. payload handling, cookie parsing, timeouts). All these options have been moved to the route configuration object.
To set global defaults, a new routes configuration option is added under the connections server configuration option. The routes configuration supports all the options available when setting up routes and works in a cascading fashion (in this order):
route config
plugin config (set via server.path() and server.bind())
connection routes defaults
server connections.routes defaults
Checklist:
If you set the timeout.client options, it is now consistent with the route level config under payload.timeout.
Review each connection configuration settings and move it into the new routes container if needed.
Reply interface
The reply() interface is passed to every handler, extension, prerequisite, and authentication methods. It allows those methods to return control back to the framework (acting as a callback), as well as set the response sent back to the client. The reply interface is now used more consistently across method types.
Handlers - unchanged. Calls to reply.continue() are treated the same as an empty reply().
Extensions - instead of calling reply() with no arguments to indicate "continue", call reply.continue(). Any call to reply() with or without arguments will be considered an abort.
Prerequisites - unchanged. Calls to reply.continue() are treated the same as an empty reply().
Authentication - first, all three methods (authenticate(), payload(), and response()) now receive the reply() interface instead of the plain next(). Second, to signal successful authentication, call reply.continue(results) where results is the credentials object. Use reply(error, null, results) to sent back errors.
Checklist:
Change reply() to reply.continue() in every extension where no value is passed back to the reply() interface.
If you are using a custom authentication scheme:
change the success callback from reply(null, result) to reply.continue(result).
change the error callback from reply(error, result) to reply(error, null, result).
the result object no longer supports the log option. Log locally.
Plugin interface
This is the most productive change. There is no longer a plugin interface. The plugin register() function now gets a server reference. The same options to select() are available and will result in another server reference with the selected connections.
Checklist:
It is recommended to rename the plugin argument to server as it makes it more intuitive.
Replace plugin.servers with server.connections.
Replace plugin.length with server.connections.length.
Logging
Checklist:
If you subsribe to the server 'request' event, you need to also subsribe to the new 'request-internal' event which will include all the logs generated internally by the framework.
The 'hapi' event tag has been replaced with a new event.internal flag for all events generated by the framework. Look for 'hapi' in your code and replace it when a check for the new event.internal boolean flag.
The server 'internalError' event was renamed to 'request-error'.
If you rely on the specific tags of any internally generated event, check the new list of events emitted as most have changed a bit to make them more consistent. The list is now fully documented.
If you set a debug settings, the request option is now split between request and log for each event type.
Routing
Routing changed slightly to fix a few bugs in how route specificity was determined. For example:
/a/b/{p*} is more specific than /{p*5}
/a/b/{p*} is more specific than /a/{b}/{c}
/a/{b}/{c*} is more specific than /a/{b*}
All these were matched incorrectly in previous versions due to how wildcards were treated.
Checklist:
If you have multiple routes with parameters and wildcards that may overlap and the order in which they are matched matters, review the new routing logic to ensure your expectations are preserved.
State (cookies)
The state configuration was split between the connection (setting default state options) and the route (how to process cookies).
Checklist:
The parse and failAction settings are now route specific.
The cookie failAction option is now called ignoreErrors and is a boolean.
Misc
The server.views()basePath option was changed to relativeTo.
If you pass a function to the validation failAction option, the function signature changed from function(source, error, next) to function(request, reply, source, error).
The request.info.id identifier format changed. If you rely on the format you will need to change you logic to use the new style.
Removed all special handling of the HTTP Location header. If you set reply().location(), reply().redirect(), reply.redirect(), or reply().created() with a relative URI location, the framework will no longer convert it to an absolute URI. The server location configuration setting is no longer supported.
Server method generateKey function:
must return a string or the outcome is considered an error. If you need to indicate that a certain key should not be cached, use the ttl argument in the generateFunc callback (previously supported returning null to indicate no caching).
the funciton no longer receives the callback argument. If you provide a custom generateKey, all the arguments are part of the key including the last.
The default key generator no longer adds the 'h:' prefix. This will cause any currently cached data to become unreachable. If you need to retain the current key generation logic, create your own custom generateKey method using the code from previous versions.
The 'onPreResponse' extension point is no longer called when the request closes prematurely.
If you are using prerequisites, request.responses changed to request.preResponses. This property is used to store the decorated output of the prerequisite methods.
The server.register() method now requires a different format for the first plugins argument. The full description of what is now expected can be found in the API documentation. Existing code is likely to throw an expection if not adjusted to the new requirements.
If you are calling server.method() with an options object (vs. arguments), change the fn option to method.
server.cache():
now only accepts a single options argument. To pass a cache name, use the segment option.
No longer requires cache segment name to begin with special characters (e.g. '##', '!!').
No longer prefix cache segment names with '|'. If you want to keep your existing cache, manually prefix your segment with it.
If you set ETags using reply().etag(), the etag value will be modified with a suffix based on the content encoding used to send the response. Use the vary option to disable modifying the etag value.
If you are crafting your own response objects, the response object option marshall was renamed to marshal.
The cacheControlStatus connection configuration option is now cache.statuses.
If you use server.config (previously plugin.config) switch to server.realm.modifiers.
The server.handler() method passes a new object to the generation function for route. This also affects request.route.
server.expose() only sets server level plugin properties which should only affect those working directly with pack objects in previous versions and expecting plugin.expose() to set both connection level and pack level options.
request.route properties moved to request.route.settings except for path and method which remained the same.
The text was updated successfully, but these errors were encountered:
Summary
hapi v8.0.0 is a deep refactoring of the entire framework and a major cleanup of its API. The
main goal for this release was to simplify the developer experience, make it more predictable, and reduce the number of methods and interfaces exposed. This version is much easier to learn without skipping all the powerful extensibility that make the framework great, but has been hard for new adopters to follow.
Breaking Changes
failAction
function signature changed fromfunction(source, error, next)
tofunction(request, reply, source, error)
generateKey
function must return a string or value is considered an error (previously supported returningnull
to indicate no caching) and no longer receives the callback argument (all arguments are part of the key including the last)servers
is nowconnections
length
removed'request-internal'
'hapi'
event tag has been replaced with a newevent.internal
flag for server logs'internalError'
event is now'request-error'
debug
option split betweenrequest
andlog
for each typeHapi.Pack
Hapi.version
Hapi.createServer()
Hapi.error
Hapi.state.prepareValue()
compose()
hapi
command line interface80
and443
to0
app
configuration no longer copied over to the initialapp
statefiles.etagsCacheMaxSize
debug
load.sampleInterval
timeout.client
replaced bypayload.timeout
failAction
replaced byignoreErrors
at the cookie level (still available at the route level)'onPreResponse'
when the request closes prematurelymaxSockets
configuration option (replaced by per-route proxy option via h2o2)reply()
interface to always signal a response and addreply.continue()
to signal continuation of the request lifecycleport
instead ofhost
optionhost
connection option is split into two valueshost
andaddress
log
options in authentication data resultrequest.responses
changed torequest.preResponses
register()
method only acceptsregister()
, item with aregister()
property, or an item with aregister()
andoptions
propertiesserver.method()
using options object syntax changedfn
option tomethod
server.cache()
no longer takes name, only options (withsegment
property)hapi
propertiesapp
andplugins
settings are deeply copied (instead of shallow copy)etag()
to vary based on content encodingmarshall
changed tomarshal
'h:'
prefix from server method cache keys generated internallybasePath
torelativeTo
cacheControlStatus
tocache.statuses
routes
config setting defaultsserver.config
toserver.realm.modifiers
route
object in properties and return values to a new structure.server.expose()
only setsserver
level plugin properties.validation
option moved toconnections.routes.validate.option
.New Features
reply()
and server methodsreply()
andserver
interface methods/a{b}c{d?}e
)failAction
method and replacednext
with fullreply()
interface supportcors.override
to disable overriding excising CORS headers (from proxy or manual)request.paramsArray
close
processor optionserver.lookup()
) or by path (server.match()
).info.uri
connection settingBug fixes
reply()
twice in a handlerreply.file()
andreply.redirect()
in extension methodsmatchOrigin
returns the incoming 'Origin' header when configured to'*'
reply()
interface to string notation methods with no argumentsUpdated dependencies
Migration Checklist
The Basics
It is highly recommended to read (again) the complete API reference. It is long, but it is the best way to full appreciate the changes in this release. You can skim through the configuration options but take a look at the examples for each method. While most individual methods remained the same, most of them moved around between parent interfaces.
The most significant change in v8.0.0 is the consolidation of the pack, server, and plugin interfaces. hapi now exposes a single parent API, the
Server
. Everything else is available as a method of the server, or via separate modules (boom for errors, statehood for cookie manipulation, rejoice for the command line executable, and glue for the application composer).Checklist:
Pack.compose()
, switch to use glue.pack
->server
,servers
->connections
).bin/hapi
command line, switch to use rejoice.pack
->server
,servers
->connections
).Hapi.error
calls with direct called to boom likeBoom.badRequest()
.Hapi.state.prepareValue()
, switch to use statehood.Hapi.version
, obtain the version value directly from the package.json file.hapi
properties (server, request, plugin), there should no longer be any reason to do so given that the only thing exposed from the module isServer
. If you are creating another server, require hapi directly.app
andplugins
are deeply copied (instead of shallow copy). This affects the server, connection, and route configuration objects. If you pass any value that cannot be safely cloned, remove those values and consider setting them directly to the runtime state container (e.g.server.app
).Creating a server
In previous versions, the server represented a single connection and the pack a collection of
connections. Instead, the server now represents your application, to which you add connections using the
server.connection()
method. You can add multiple connections to your server. For convenience, a server with a single connection is decorated with theinject()
method along with theinfo
property. This makes most code work as is by simply adding a call toserver.connection()
.Note that unlike previous server API which accepted multiple arguments (for
host
,port
, andoptions
), the newserver.connection()
method accepts a singleoptions
argument wherehost
andport
are optional properties.The default port is no longer
80
/443
but0
. This means omitting theport
when creating a connection will bind the server to the next available port. If you relied to on the port being set to80
/443
, you need to manually configure your connections with the desired value.While the default
host
value still bind to all available network interfaces ('0.0.0.0'
), the default value ofserver.info.host
is now set to the operating system hostname if available otherwise tolocalhost
. A new configuration valueaddress
is provided to separate the value used to bind the listener (address
) from the value used to setserver.info.host
andserver.info.uri
. Ifaddress
is not available,host
is used and if not available,'0.0.0.0'
.Checklist:
Hapi.Pack
withHapi.Server
and callserver.connection()
instead ofpack.server()
.server.pack
is now justserver
.Hapi.createServer()
with directnew Hapi.Server()
server.connection()
withhost
andport
passed as options. If you relied on the defaulthost
orport
values, set those manually. Consider usingaddress
andhost
with different values when you need to set the internal IP address for binding (address
) and a public host used by plugins (host
).port
instead ofhost
option.Server configuration
The following configuration options moved from the connection to the server:
files.etagsCacheMaxSize
debug
load.sampleInterval
When constructing a server, you can now set default configuration for every connection added to it using the
connections
option property.The connection
maxSockets
configuration option used by the proxy handler is no longer available. Instead, set themaxSockets
option of the proxy handler configuration.Checklist:
app
server and connection configuration option is no longer copied over to the initialapp
state of the object. If you need to access theapp
value provided in the server or connections settings, useserver.settings.app
andconnections.settings.app
. Note that these values are considered static and should not be modified. If you need to store runtime state, useserver.app
andconnection.app
.Connection configuration
Most of the options in previous versions (e.g. server configuration) were actually not about the connection but about the routes in it (e.g. payload handling, cookie parsing, timeouts). All these options have been moved to the route configuration object.
To set global defaults, a new
routes
configuration option is added under theconnections
server configuration option. Theroutes
configuration supports all the options available when setting up routes and works in a cascading fashion (in this order):server.path()
andserver.bind()
)routes
defaultsconnections.routes
defaultsChecklist:
timeout.client
options, it is now consistent with the route level config underpayload.timeout
.routes
container if needed.Reply interface
The
reply()
interface is passed to every handler, extension, prerequisite, and authentication methods. It allows those methods to return control back to the framework (acting as a callback), as well as set the response sent back to the client. The reply interface is now used more consistently across method types.reply.continue()
are treated the same as an emptyreply()
.reply()
with no arguments to indicate "continue", callreply.continue()
. Any call toreply()
with or without arguments will be considered an abort.reply.continue()
are treated the same as an emptyreply()
.authenticate()
,payload()
, andresponse()
) now receive thereply()
interface instead of the plainnext()
. Second, to signal successful authentication, callreply.continue(results)
whereresults
is the credentials object. Usereply(error, null, results)
to sent back errors.Checklist:
reply()
toreply.continue()
in every extension where no value is passed back to thereply()
interface.reply(null, result)
toreply.continue(result)
.reply(error, result)
toreply(error, null, result)
.result
object no longer supports thelog
option. Log locally.Plugin interface
This is the most productive change. There is no longer a plugin interface. The plugin
register()
function now gets aserver
reference. The same options toselect()
are available and will result in anotherserver
reference with the selected connections.Checklist:
plugin
argument toserver
as it makes it more intuitive.plugin.servers
withserver.connections
.plugin.length
withserver.connections.length
.Logging
Checklist:
'request'
event, you need to also subsribe to the new'request-internal'
event which will include all the logs generated internally by the framework.'hapi'
event tag has been replaced with a newevent.internal
flag for all events generated by the framework. Look for'hapi'
in your code and replace it when a check for the newevent.internal
boolean flag.'internalError'
event was renamed to'request-error'
.debug
settings, therequest
option is now split betweenrequest
andlog
for each event type.Routing
Routing changed slightly to fix a few bugs in how route specificity was determined. For example:
/a/b/{p*}
is more specific than/{p*5}
/a/b/{p*}
is more specific than/a/{b}/{c}
/a/{b}/{c*}
is more specific than/a/{b*}
All these were matched incorrectly in previous versions due to how wildcards were treated.
Checklist:
State (cookies)
The state configuration was split between the connection (setting default state options) and the route (how to process cookies).
Checklist:
parse
andfailAction
settings are now route specific.failAction
option is now calledignoreErrors
and is a boolean.Misc
server.views()
basePath
option was changed torelativeTo
.failAction
option, the function signature changed fromfunction(source, error, next)
tofunction(request, reply, source, error)
.request.info.id
identifier format changed. If you rely on the format you will need to change you logic to use the new style.reply().location()
,reply().redirect()
,reply.redirect()
, orreply().created()
with a relative URI location, the framework will no longer convert it to an absolute URI. The serverlocation
configuration setting is no longer supported.generateKey
function:ttl
argument in thegenerateFunc
callback (previously supported returningnull
to indicate no caching).callback
argument. If you provide a customgenerateKey
, all the arguments are part of the key including the last.'h:'
prefix. This will cause any currently cached data to become unreachable. If you need to retain the current key generation logic, create your own customgenerateKey
method using the code from previous versions.'onPreResponse'
extension point is no longer called when the request closes prematurely.request.responses
changed torequest.preResponses
. This property is used to store the decorated output of the prerequisite methods.server.register()
method now requires a different format for the firstplugins
argument. The full description of what is now expected can be found in the API documentation. Existing code is likely to throw an expection if not adjusted to the new requirements.server.method()
with an options object (vs. arguments), change thefn
option tomethod
.server.cache()
:options
argument. To pass a cache name, use thesegment
option.'|'
. If you want to keep your existing cache, manually prefix your segment with it.reply().etag()
, the etag value will be modified with a suffix based on the content encoding used to send the response. Use thevary
option to disable modifying the etag value.marshall
was renamed tomarshal
.cacheControlStatus
connection configuration option is nowcache.statuses
.server.config
(previouslyplugin.config
) switch toserver.realm.modifiers
.server.handler()
method passes a new object to the generation function forroute
. This also affectsrequest.route
.server.expose()
only setsserver
level plugin properties which should only affect those working directly withpack
objects in previous versions and expectingplugin.expose()
to set both connection level and pack level options.request.route
properties moved torequest.route.settings
except forpath
andmethod
which remained the same.The text was updated successfully, but these errors were encountered: