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

8.0.0 Release Notes #2186

Closed
hueniverse opened this issue Nov 19, 2014 · 0 comments
Closed

8.0.0 Release Notes #2186

hueniverse opened this issue Nov 19, 2014 · 0 comments
Assignees
Labels
breaking changes Change that can breaking existing code release notes Major release documentation
Milestone

Comments

@hueniverse
Copy link
Contributor

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.

  • 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().

var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({ port: 80 });

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.

var Hapi = require('hapi');
var server = new Hapi.Server({ debug: false, connections: { routes: { security: true  } } });
server.connection({ routes: { cors: true } });

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):

  1. route config
  2. plugin config (set via server.path() and server.bind())
  3. connection routes defaults
  4. 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.
@hueniverse hueniverse added breaking changes Change that can breaking existing code release notes Major release documentation labels Nov 19, 2014
@hueniverse hueniverse self-assigned this Nov 19, 2014
@hueniverse hueniverse added this to the 8.0.0 milestone Nov 19, 2014
@lock lock bot locked as resolved and limited conversation to collaborators Jan 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking changes Change that can breaking existing code release notes Major release documentation
Projects
None yet
Development

No branches or pull requests

1 participant