diff --git a/handwritten/storage/MIGRATION.md b/handwritten/storage/MIGRATION.md new file mode 100644 index 000000000000..d0e6d24555f1 --- /dev/null +++ b/handwritten/storage/MIGRATION.md @@ -0,0 +1,84 @@ +# Storage v7 to v8 Migration Guide - Node.js 18 & Gaxios Update + +This guide helps you migrate your application from `@google-cloud/storage` v7 to v8, focusing on the transition to Node.js 18+ as the minimum supported environment and changes introduced by updating the underlying HTTP client, `gaxios`, to version 7. + +## Minimum Requirements: Node.js 18 + +`@google-cloud/storage` v8 now officially requires Node.js 18 or higher. This update aligns the library with modern JavaScript environments. + +Specifically, Node.js 18 introduced native global Web APIs (such as `fetch` and `Headers`). Conforming to this standard, the underlying HTTP client, `gaxios` (updated to v7), leverages native `Headers` rather than custom user-land header representations. As a result, `@google-cloud/storage` v8 has transitioned response and request headers to standard native global `Headers` objects. + + +## Key Breaking Changes for Storage Users + +### 1. Response Headers are now `Headers` objects + +When you receive a full API response from Storage methods (e.g., via callbacks or promise resolutions that include the response object), the `headers` property of the response is now a standard native `Headers` object (aligned with the Fetch API standard in Node.js 18) rather than a plain JavaScript object. + +**Before (Storage v7):** + +```js +const [retrievedFile, apiResponse] = await file.get(); +const contentType = apiResponse.headers['content-type']; +``` + +**After (Storage v8):** + +```js +const [retrievedFile, apiResponse] = await file.get(); +// Accessing headers requires the .get() method +const contentType = apiResponse.headers.get('content-type'); +``` + +### 2. Passing Headers in Options + +If you pass custom headers in options to Storage methods (which extend `GaxiosOptions`), you can still pass plain objects, as the Storage library will convert them to standard `Headers` internally for the request. However, if you read them back from the prepared options or response, they will be `Headers` objects. + +**Before (Storage v7):** + +```js +// Reading request headers back from response metadata returned a plain object +const customHeader = apiResponse.config.headers['x-custom-header']; +``` + +**After (Storage v8):** + +```js +// Reading request headers back from response metadata requires .get() +const customHeader = apiResponse.config.headers.get('x-custom-header'); +``` + +> [!WARNING] +> **Header Value Stringification:** Plain JavaScript objects allow passing non-string values (such as arrays or numbers) which are implicitly processed. However, the native `Headers` constructor strictly converts all values to standard string representations. For example, passing an array of values (e.g., `['val1', 'val2']`) will result in a single comma-separated string (e.g., `'val1, val2'`). Ensure you pre-format or verify your header values before passing them to custom options. + + +### 3. URL Resolution (`baseURL`) + +If you are using custom `baseURL` options or passing relative URLs to methods that accept them, be aware that resolution now strictly follows the standard native `URL` constructor spec (`new URL(url, baseURL)`). This can affect how leading slashes in paths resolve. + +**Before (Storage v7):** + +Using standard path-joining custom resolution: +- `baseURL`: `https://storage.googleapis.com/storage/v1` +- `url`: `/b/my-bucket` +- Resolved URL: `https://storage.googleapis.com/storage/v1/b/my-bucket` + +**After (Storage v8):** + +Strictly resolved via the standard native `URL` constructor rules (where a leading slash resolves relative to the root of the host): +- `baseURL`: `https://storage.googleapis.com/storage/v1` +- `url`: `/b/my-bucket` +- Resolved URL: `https://storage.googleapis.com/b/my-bucket` (resolves relative to host root, stripping `storage/v1`) + + +## Upgrade Instructions + +Update your `@google-cloud/storage` dependency to version 8: + +```sh +npm install @google-cloud/storage@latest +``` + +## Troubleshooting + +- If you encounter `undefined` when accessing headers on the response object, ensure you are using `apiResponse.headers.get('header-name')`.