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

feat(netlify): Netlify Adapter v4 #84

Merged
merged 64 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
e0ee3e9
first attempt at new plugin
Skn0tt Nov 23, 2023
2ff65a0
some more change
Skn0tt Nov 23, 2023
f5cef64
remove unneeded handholding from edge functions
Skn0tt Nov 23, 2023
9ae2673
some more work
Skn0tt Nov 23, 2023
0cdbe88
remove netlifylocals
Skn0tt Nov 23, 2023
d7ddeab
embed astro version
Skn0tt Nov 23, 2023
ff42dc8
update README a bunch
Skn0tt Nov 23, 2023
4d75877
remove per-page-functions
Skn0tt Nov 24, 2023
488b64e
refactor
Skn0tt Nov 24, 2023
d6f057b
write changeset
Skn0tt Nov 24, 2023
ac2c217
redirects
Skn0tt Nov 24, 2023
8eb40c8
update caching
Skn0tt Nov 24, 2023
e3713b2
update cache control
Skn0tt Nov 24, 2023
bc571b5
Update .changeset/ten-cougars-pretend.md
Skn0tt Nov 27, 2023
3bda429
update docs
Skn0tt Nov 27, 2023
95fad12
remove --build
Skn0tt Nov 27, 2023
aa54d88
remove static warning
Skn0tt Nov 27, 2023
854b825
nit
Skn0tt Nov 27, 2023
a95ba66
fix: update netlify detection
Skn0tt Nov 27, 2023
628248c
write about image cdn
Skn0tt Nov 27, 2023
18145ca
import right types
Skn0tt Nov 27, 2023
9137e68
Update packages/netlify/README.md
Skn0tt Nov 27, 2023
78cfefc
compile edge middleware with neutral platform
Skn0tt Nov 27, 2023
1fd6172
remove todos
Skn0tt Nov 27, 2023
7b3f4bb
remove `[astro] update /.netlify/state.json` log lines from local dev
Skn0tt Nov 27, 2023
1e3193b
remove directories even when they have content
Skn0tt Nov 27, 2023
27e3345
set "alt" attributes etc on images
Skn0tt Nov 28, 2023
d43a2f3
Apply suggestions from code review
Skn0tt Nov 29, 2023
7d53619
remove "you're good" lines
Skn0tt Nov 29, 2023
155d1d8
mention image cdn in changelog
Skn0tt Nov 29, 2023
96b9078
nest all features under "Usage"
Skn0tt Nov 29, 2023
f52963b
clarify changes to middleware
Skn0tt Nov 29, 2023
416a1b8
Update .changeset/ten-cougars-pretend.md
Skn0tt Nov 29, 2023
95051cf
Apply suggestions from code review
Skn0tt Dec 1, 2023
15ca81d
Merge remote-tracking branch 'upstream/main' into v4
Skn0tt Dec 4, 2023
245ad91
fix: update function tests
Skn0tt Dec 4, 2023
6e1f1e7
fix: remove unused test-edge script
Skn0tt Dec 4, 2023
364b096
fix: get all tests to run
Skn0tt Dec 4, 2023
6d741c6
fix: workspace name
Skn0tt Dec 4, 2023
c2c70cc
fix: redirects on static pages
Skn0tt Dec 4, 2023
fdd13ad
feat: add cacheOnDemandPages
Skn0tt Dec 4, 2023
cca29a6
fix: config is optional
Skn0tt Dec 4, 2023
fa5740e
update tests to use fixture utils
lilnasy Dec 4, 2023
5832e5d
update fixture for astro 4
lilnasy Dec 4, 2023
ddbf185
fix failure on windows
lilnasy Dec 4, 2023
36435e6
refactor: use pathname instead of replacing path sep
Skn0tt Dec 5, 2023
24c0d35
Revert "refactor: use pathname instead of replacing path sep"
Skn0tt Dec 5, 2023
c6daeb0
chore: indentation
Skn0tt Dec 5, 2023
e5552de
Merge remote-tracking branch 'upstream/main' into v4
Skn0tt Dec 5, 2023
a99fd9d
Merge branch 'main' into v4
Skn0tt Dec 5, 2023
b82e724
chore: comment on caching directive
Skn0tt Dec 6, 2023
0e96d36
Merge remote-tracking branch 'upstream/main' into v4
Skn0tt Dec 7, 2023
fe8d134
mark image cdn as beta
Skn0tt Dec 8, 2023
bd7dbba
add edgeMiddleware option back in
Skn0tt Dec 8, 2023
3a03eb8
feat: add edge ssr support
Skn0tt Dec 11, 2023
580820c
fix: re-add /static and /functions imports
Skn0tt Dec 11, 2023
28294e5
Revert "feat: add edge ssr support"
Skn0tt Dec 11, 2023
4d4c245
feat: provide netlify context in local dev
Skn0tt Dec 12, 2023
0ac9466
fix: disable edgeMiddleware by default
Skn0tt Dec 12, 2023
81679b1
Apply suggestions from code review
Skn0tt Dec 13, 2023
b63cfcb
update odb changelog
Skn0tt Dec 13, 2023
13a7659
say we want you to use the replacement!
Skn0tt Dec 13, 2023
0a6ec00
Update .changeset/ten-cougars-pretend.md
Skn0tt Dec 15, 2023
5da1f7c
Update packages/netlify/README.md
Skn0tt Dec 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .changeset/ten-cougars-pretend.md
alexanderniebuhr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
'@astrojs/netlify': major
---

# Netlify Adapter v4 simplifies static + SSR deployments

This update is a complete overhaul of the Netlify adapter.
It simplifies the user-facing config, and resolves a number of bugs along the way.

Here's what changes:

## Netlify Context is automatically available via Locals

In v3, you could use `netlify-edge-middleware.ts` to inject data from the Netlify context into your Astro locals.
In v4, this file is no longer needed because the Netlify context is automatically made available via `Astro.locals.netlify.context`.
You can use this context to access information about the user (like geolocation or IP address), your Netlify site (like deploy ID) or the request (like its request ID or the CDN region it's served from).

**Action Required:**
Remove the `netlify-edge-middleware.ts` or `netlify-edge-middleware.js` file.
In your codebase, change all usage of locals injected through that file to use `Astro.locals.netlify.context` instead.

### Image CDN

v4 of this adapter integrates your Astro site with Netlify [Image CDN](https://docs.netlify.com/image-cdn/overview/).
This allows transforming images on-the-fly without impacting build times.
It's implemented using an [Astro Image Service](https://docs.astro.build/en/reference/image-service-reference/), and enabled by default.

## On-Demand Builders are no longer supported
lilnasy marked this conversation as resolved.
Show resolved Hide resolved

On-Demand Builders (ODB) allows SSR-Rendered pages to be cached using a Time to Live (TTL) strategy.
ODB continues to be supported by Netlify, but has since been replaced by the much more powerful
alexanderniebuhr marked this conversation as resolved.
Show resolved Hide resolved
[Fine-Grained Cache Control](https://www.netlify.com/blog/swr-and-fine-grained-cache-control).

In v3, you could deploy your SSR-Rendered Astro pages to ODBs by enabling the `builders` config option,
and then specifying the TTL on a per-page basis.
In v4, there's the `cacheOnDemandPages` option that works very similarly - take a look at the README to learn more.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

**Action Required:**
Replace the `builders` config option with `cacheOnDemandPages`.

```diff lang="ts"
// astro.config.mjs
export default defineConfig({
// ...
adapter: netlify({
- builders: true
+ cacheOnDemandPages: true
}),
});
```

## `functionPerRoute` was removed

In v3, the `functionPerRoute` option allowed the SSR routes to be split up into multiple Netlify Functions.
In theory, this reduced bundle sizes of each individual function, and the speed-up in code parsing should speed up cold starts.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved
In practice, this benefit is often nullified by the increase in number of cold starts - more handlers means fewer requests per handler, means more cold starts.

In v4, support for this deployment mode was removed.

**Action Required:**
Remove the `functionPerRoute` field from your config.

## `binaryMediaTypes` was removed

`binaryMediaTypes` was a workaround required for some Astro endpoints, because v3 deployed those as "old" Netlify Functions (now referred to as ["Lambda Compatibility Mode"](https://docs.netlify.com/functions/lambda-compatibility)).
v4 uses the new [Netlify Functions 2.0](https://www.netlify.com/blog/introducing-netlify-functions-2-0/), which simply doesn't need this workaround anymore - so we're removing it 🎉

**Action Required:**
Remove the `binaryMediaTypes` field from your config.
212 changes: 72 additions & 140 deletions packages/netlify/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# @astrojs/netlify

This adapter allows Astro to deploy your SSR site to [Netlify](https://www.netlify.com/).
This adapter allows Astro to deploy your On-Demand-Rendered site to [Netlify](https://www.netlify.com/).
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

Learn how to deploy your Astro site in our [Netlify deployment guide](https://docs.astro.build/en/guides/deploy/netlify/).

- <strong>[Why Astro Netlify](#why-astro-netlify)</strong>
- <strong>[Installation](#installation)</strong>
- <strong>[Usage](#usage)</strong>
- <strong>[Configuration](#configuration)</strong>
- <strong>[Examples](#examples)</strong>
- <strong>[Troubleshooting](#troubleshooting)</strong>
- <strong>[Contributing](#contributing)</strong>
Expand All @@ -17,13 +16,13 @@ Learn how to deploy your Astro site in our [Netlify deployment guide](https://do

If you're using Astro as a static site builder—its behavior out of the box—you don't need an adapter.

If you wish to [use server-side rendering (SSR)](https://docs.astro.build/en/guides/server-side-rendering/), Astro requires an adapter that matches your deployment runtime.
If you wish to [use On-Demand Rendering, also known as Server-Side Rendering (SSR)](https://docs.astro.build/en/guides/server-side-rendering/), Astro requires an adapter that matches your deployment runtime.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

[Netlify](https://www.netlify.com/) is a deployment platform that allows you to host your site by connecting directly to your GitHub repository. This adapter enhances the Astro build process to prepare your project for deployment through Netlify.

## Installation

Add the Netlify adapter to enable SSR in your Astro project with the following `astro add` command. This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step.
Add the Netlify adapter to enable On-Demand Rendering in your Astro project with the following `astro add` command. This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```sh
# Using NPM
Expand All @@ -49,81 +48,91 @@ If you prefer to install the adapter manually instead, complete the following tw
```diff lang="js"
// astro.config.mjs
import { defineConfig } from 'astro/config';
+ import netlify from '@astrojs/netlify/functions';
+ import netlify from '@astrojs/netlify';

export default defineConfig({
+ output: 'server',
+ adapter: netlify(),
});
```

### Run middleware in Edge Functions
## Usage

When deploying to Netlify Functions, you can choose to use an Edge Function to run your Astro middleware.
[Read the full deployment guide here.](https://docs.astro.build/en/guides/deploy/netlify/)

To enable this, set the `edgeMiddleware` config option to `true`:
After [performing a build](https://docs.astro.build/en/guides/deploy/#building-your-site-locally) the `.netlify/` folder will contain [Netlify Functions](https://docs.netlify.com/functions/overview/) in the `.netlify/functions-internal/` and [Netlify Edge Functions](https://docs.netlify.com/edge-functions/overview/) in the `.netlify/edge-functions/` folder.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```diff lang="js"
// astro.config.mjs
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
output: 'server',
adapter: netlify({
+ edgeMiddleware: true,
}),
});
Now you can deploy. Install the [Netlify CLI](https://docs.netlify.com/cli/get-started/) and run:
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```sh
netlify deploy
```

#### Pass edge context to your site
The [Netlify Blog post on Astro](https://www.netlify.com/blog/how-to-deploy-astro/) and the [Netlify Docs](https://docs.netlify.com/integrations/frameworks/astro/) provide more information on how to use this integration to deploy to Netlify.

Netlify Edge Functions provide a [context object](https://docs.netlify.com/edge-functions/api/#netlify-specific-context-object) including metadata about the request, such as a user’s IP, geolocation data, and cookies.

To expose values from this context to your site, create a `netlify-edge-middleware.ts` (or `.js`) file in your project’s [source directory](https://docs.astro.build/en/reference/configuration-reference/#srcdir). This file must export a function that returns the data to add to [Astro’s `locals` object](https://docs.astro.build/en/reference/api-reference/#astrolocals), which is available in middleware and Astro routes.
### Accessing edge context from your site

In this example, `visitorCountry` and `hasEdgeMiddleware` would both be added to Astro’s `locals` object:
Netlify Edge Functions provide a [context object](https://docs.netlify.com/edge-functions/api/#netlify-specific-context-object) that includes metadata about the request such as a user’s IP, geolocation data, and cookies.

```ts
// src/netlify-edge-middleware.ts
import type { Context } from 'https://edge.netlify.com';

export default function ({ request, context }: { request: Request; context: Context }) {
// Return serializable data to add to Astro.locals
return {
visitorCountry: context.geo.country.name,
hasEdgeMiddleware: true,
};
}
This can be accessed through the `Astro.locals.netlify.context` object:

```astro
---
const { geo: { city } } = Astro.locals.netlify.context
---
<h1>Hello there, friendly visitor from {city}!</h1>
```

> **Note**
> Netlify Edge Functions run in [a Deno environment](https://docs.netlify.com/edge-functions/api/#runtime-environment), so import statements in this file must use Deno’s URL syntax.
If you're using TypeScript, you can get proper typings by updating your `src/env.d.ts` to use `NetlifyLocals`:
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

`netlify-edge-middleware.ts` must provide a function as its default export. This function:
```ts
// src/env.d.ts
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

- must return a JSON-serializable object, which cannot include types like `Map`, `function`, `Set`, etc.
- will always run first, before any other middleware and routes.
- cannot return a response or redirect.
type NetlifyLocals = import('@astrojs/netlify').NetlifyLocals

### Per-page functions
declare namespace App {
interface Locals extends NetlifyLocals {
...
}
}
```

The Netlify adapter builds to a single function by default. Astro 2.7 added support for splitting your build into separate entry points per page. If you use this configuration, the Netlify adapter will generate a separate function for each page. This can help reduce the size of each function so they are only bundling code used on that page.
It's only available during on-demand rendering.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```js
### Run Middleware in Edge Functions

If you use Astro Middleware, the default behaviour is that it's applied to prerendered pages at build-time, and to on-demand-rendered pages at runtime.
For prerendered pages, it can't be used to implement redirects, access control or custom response headers.
If you need any of this, you should probably deploy run your Middleware on Netlify Edge Functions by enabling the `edgeMiddleware` option:
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```diff lang="js"
// astro.config.mjs
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
output: 'server',
adapter: netlify({
functionPerRoute: true,
+ edgeMiddleware: true,
}),
});
```

### Static sites
This will deploy your Middleware as an Edge Function, and run it on all routes - including prerendered pages.
This also means that locals specified in the Middleware won't be applied to any prerendered pages, because they've already been fully rendered at build-time.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

### Image CDN

This adapter integrates your site with [Netlify Image CDN](https://docs.netlify.com/image-cdn/), transforming images on-the-fly without impacting build times.
It's implemented using an [Astro Image Service](https://docs.astro.build/en/reference/image-service-reference/) under the hood.

> **Note**
> This adapter does not support `image.domains` and `image.remotePatterns` config properties in your Astro config - use the `remote_images` field in `netlify.toml` instead: [Netlify Image CDN - Remote Path](https://docs.netlify.com/image-cdn/overview/#remote-path).
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

### Static sites & Redirects

For static sites you usually don't need an adapter. However, if you use `redirects` configuration in your Astro config, the Netlify adapter can be used to translate this to the proper `_redirects` format.

Expand All @@ -146,117 +155,40 @@ Once you run `astro build` there will be a `dist/_redirects` file. Netlify will
> **Note**
> You can still include a `public/_redirects` file for manual redirects. Any redirects you specify in the redirects config are appended to the end of your own.

### On-demand Builders

[Netlify On-demand Builders](https://docs.netlify.com/configure-builds/on-demand-builders/) are serverless functions used to generate web content as needed that’s automatically cached on Netlify’s Edge CDN. You can enable these functions using the [`builders` configuration](#builders).

By default, all pages will be rendered on first visit and the rendered result will be reused for every subsequent visit until you redeploy. To set a revalidation time, call the [`runtime.setBuildersTtl(ttl)` local](https://docs.astro.build/en/reference/api-reference/#astrolocals) with the duration (in seconds).

The following example sets a revalidation time of 45, causing Netlify to store the rendered HTML for 45 seconds.

```astro
---
// src/pages/index.astro
import Layout from '../components/Layout.astro';

if (import.meta.env.PROD) {
Astro.locals.runtime.setBuildersTtl(45);
}
---

<Layout title="Astro on Netlify">
{new Date(Date.now())}
</Layout>
```

It is important to note that On-demand Builders ignore query params when checking for cached pages. For example, if `example.com/?x=y` is cached, it will be served for `example.com/?a=b` (different query params) and `example.com/` (no query params) as well.

## Usage

[Read the full deployment guide here.](https://docs.astro.build/en/guides/deploy/netlify/)

After [performing a build](https://docs.astro.build/en/guides/deploy/#building-your-site-locally) the `netlify/` folder will contain [Netlify Functions](https://docs.netlify.com/functions/overview/) in the `netlify/functions/` folder.

Now you can deploy. Install the [Netlify CLI](https://docs.netlify.com/cli/get-started/) and run:

```sh
netlify deploy --build
```

The [Netlify Blog post on Astro](https://www.netlify.com/blog/how-to-deploy-astro/) and the [Netlify Documentation](https://docs.netlify.com/integrations/frameworks/astro/) provide more information on how to use this integration to deploy to Netlify.

## Configuration

To configure this adapter, pass an object to the `netlify()` function call in `astro.config.mjs` - there's only one possible configuration option:
### Caching On-Demand-Rendered Pages
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

### dist
On-demand-rendered pages that don't contain dynamic content can be cached to improve performance and lower resource usage.
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved
The easiest way to do this is to enable the `cacheOnDemandPages` option in the adapter, which will cache all on-demand-rendered pages for up to one year:
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

We build to the `dist` directory at the base of your project. To change this, use the `dist` option:

```js
```ts
// astro.config.mjs
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
output: 'server',
adapter: netlify({
dist: new URL('./dist/', import.meta.url),
cacheOnDemandPages: true
}),
});
```

And then point to the dist in your `netlify.toml`:
For advanced usage, you can change this on a per-page basis by adding caching headers to your response from within your pages:
Skn0tt marked this conversation as resolved.
Show resolved Hide resolved

```toml
# netlify.toml
[functions]
directory = "dist/functions"
```

### builders

You can enable On-demand Builders using the `builders` option:
```astro
---
// src/pages/index.astro
import Layout from '../components/Layout.astro';

```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';
Astro.response.headers.set('CDN-Cache-Control', "public, max-age=45, must-revalidate")
---

export default defineConfig({
output: 'server',
adapter: netlify({
builders: true,
}),
});
<Layout title="Astro on Netlify">
{new Date()}
</Layout>
```

On-demand Builders are only available with the `@astrojs/netlify/functions` adapter and are not compatible with Edge Functions.

### binaryMediaTypes

> This option is only needed for the Functions adapter and is not needed for Edge Functions.

Netlify Functions requires binary data in the `body` to be base64 encoded. The `@astrojs/netlify/functions` adapter handles this automatically based on the `Content-Type` header.

We check for common mime types for audio, image, and video files. To include specific mime types that should be treated as binary data, include the `binaryMediaTypes` option with a list of binary mime types.

```js
// src/pages/image.jpg.ts
import fs from 'node:fs';

export function GET() {
const buffer = fs.readFileSync('../image.jpg');

// Return the buffer directly, @astrojs/netlify will base64 encode the body
return new Response(buffer, {
status: 200,
headers: {
'content-type': 'image/jpeg',
},
});
}
```
With [fine-grained cache control](https://www.netlify.com/blog/swr-and-fine-grained-cache-control/), Netlify supports
standard caching headers like `CDN-Cache-Control` or `Vary`.
Refer to the docs to learn about implementing e.g. time to live (TTL) or stale while revalidate (SWR) caching: https://docs.netlify.com/platform/caching

## Examples

Expand Down
9 changes: 0 additions & 9 deletions packages/netlify/builders-types.d.ts

This file was deleted.

Loading