-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This documents how to setup and use SSR features in Astro.
- Loading branch information
Showing
3 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
--- | ||
layout: ~/layouts/MainLayout.astro | ||
title: Server-side Rendering (experimental) | ||
--- | ||
|
||
**Server-side Rendering**, aka SSR, is enabled in Astro behind an experimental flag. When you enable SSR you can: | ||
|
||
- Implement sessions for login state in your app. | ||
- Render data from an API called dynamically with `fetch`. | ||
- Deploy your site to a host using an *adapter*. | ||
|
||
> SSR is marked as __experimental__ in Astro and changes will occur before it becomes stable. Use only if you can handle API changes. | ||
## Enabling SSR in Your Project | ||
|
||
To enable SSR you need to use an adapter. The following adapters are available today with more to come in the future: | ||
|
||
- [Netlify](https://github.com/withastro/astro/tree/main/packages/integrations/netlify) | ||
- [Node.js](https://github.com/withastro/astro/tree/main/packages/integrations/node) | ||
|
||
In this example we will use `@astrojs/netlify` to build for Netlify. First install the adapter: | ||
|
||
```bash | ||
npm install --save-dev @astrojs/netlify | ||
``` | ||
|
||
Once your packages have been installed, add two new lines to your `astro.config.mjs` project configuration file. | ||
|
||
```diff | ||
// astro.config.mjs | ||
import { defineConfig } from 'astro/config'; | ||
+ import netlify from '@astrojs/netlify/functions'; | ||
|
||
export default defineConfig({ | ||
+ adapter: netlify(), | ||
}); | ||
``` | ||
|
||
With Netlify you can deploy from git, their web UI, or from the cli. Here we'll use the [Netlify CLI](https://docs.netlify.com/cli/get-started/) to deploy. | ||
|
||
First build your site as normal: | ||
|
||
```bash | ||
npm run build | ||
``` | ||
|
||
This creates `netlify/functions/` which contains your SSR code. Deploying your site will deploy this function which contains all of your Astro pages ready to be rendered. | ||
|
||
```bash | ||
netlify deploy | ||
``` | ||
|
||
After the deploy is complete it should provide you a preview URL to see your site. | ||
|
||
## Features | ||
|
||
Astro will remain a static-site generator by default, but once you enable a server-side rendering adapter a few new features become available to you. | ||
|
||
### Astro.request.headers | ||
|
||
The headers for the request are available on `Astro.request.headers`. It is a [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object, a Map-like object where you can retrieve headers such as the cookie. | ||
|
||
```astro | ||
--- | ||
const cookie = Astro.request.headers.get('cookie'); | ||
// ... | ||
--- | ||
<html> | ||
<!-- Page here... --> | ||
</html> | ||
``` | ||
|
||
### Astro.redirect | ||
|
||
On the `Astro` global, this method allows you to redirect to another page. You might do this after checking if the user is logged in by getting their session from a cookie. | ||
|
||
```astro | ||
--- | ||
import { isLoggedIn } from '../utils'; | ||
const cookie = Astro.request.headers.get('cookie'); | ||
// if the user is not logged in, redirect them to the login page. | ||
if(!isLoggedIn(cookie)) { | ||
return Astro.redirect('/login'); | ||
} | ||
--- | ||
<html> | ||
<!-- Page here... --> | ||
</html> | ||
``` | ||
|
||
### Response | ||
|
||
You can also return a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) from any page. You might do this to return a 404 on a dynamic page after looking up an id in the database. | ||
|
||
__[id].astro__ | ||
|
||
```astro | ||
--- | ||
import { getProduct } from '../api'; | ||
const product = await getProduct(Astro.params.id); | ||
// No product found | ||
if(!product) { | ||
return new Response(null, { | ||
status: 404, | ||
statusText: 'Not found' | ||
}); | ||
} | ||
--- | ||
<html> | ||
<!-- Page here... --> | ||
</html> | ||
``` | ||
|
||
#### API Routes | ||
|
||
A [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can also be returned from an API route. | ||
|
||
```js | ||
import { getProduct } from '../db'; | ||
|
||
export function get({ id }) { | ||
const product = await getProduct(id); | ||
|
||
if(!product) { | ||
return new Response(null, { | ||
status: 404, | ||
statusText: 'Not found' | ||
}); | ||
} | ||
|
||
return new Response(JSON.stringify(product), { | ||
status: 200 | ||
}); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
--- | ||
layout: ~/layouts/MainLayout.astro | ||
title: Astro Adapter API (experimental) | ||
--- | ||
|
||
Astro is designed to make it easy to deploy to any cloud provider for SSR (server-side rendering). This ability is provided by __adapters__, which are [integrations](/en/reference/integrations-reference/). | ||
|
||
> Server-side rendering in Astro is *experimental*. If you are interested in building an adapter for a host now is the perfect time to help shape these APIs. If you are worried about breaking changes this might be a little too soon for you. | ||
## What is an adapter | ||
|
||
An adapter is a special kind of [integration](/en/reference/integrations-reference/) that provides an entrypoint for server-side rendering. An adapter does two things: | ||
|
||
- Implements host-specific APIs for handling requests. | ||
- Configures the build according to host conventions. | ||
|
||
## Building an Adapter | ||
|
||
An adapter is an [integration](/en/reference/integrations-reference/) and can do anything that an integration can do. | ||
|
||
An adapter __must__ call the `setAdapter` API in the `astro:config:done` hook like so: | ||
|
||
```js | ||
export default function createIntegration() { | ||
return { | ||
name: '@matthewp/my-adapter', | ||
hooks: { | ||
'astro:config:done': ({ setAdapter }) => { | ||
setAdapter({ | ||
name: '@matthewp/my-adapter', | ||
serverEntrypoint: '@matthewp/my-adapter/server.js' | ||
}); | ||
}, | ||
}, | ||
}; | ||
} | ||
``` | ||
|
||
The object passed into `setAdapter` is defined as: | ||
|
||
```ts | ||
interface AstroAdapter { | ||
name: string; | ||
serverEntrypoint?: string; | ||
exports?: string[]; | ||
} | ||
``` | ||
|
||
The properties are: | ||
|
||
* __name__: A unique name for your adapter, used for logging. | ||
* __serverEntrypoint__: The entrypoint for server-side rendering. | ||
* __exports__: An array of named exports when used in conjunction with `createExports` (explained below). | ||
|
||
### Server Entrypoint | ||
|
||
Astro's adapter API attempts to work with any type of host, and gives a flexible way to conform to the host APIs. | ||
|
||
#### Exports | ||
|
||
Some serverless hosts expect you to export a function, such as `handler`: | ||
|
||
```js | ||
export function handler(event, context) { | ||
// ... | ||
} | ||
``` | ||
|
||
With the adapter API you achieve this by implementing `createExports` in your `serverEntrypoint`: | ||
|
||
```js | ||
import { App } from 'astro/app'; | ||
|
||
export function createExports(manifest) { | ||
const app = new App(manifest); | ||
|
||
const handler = (event, context) => { | ||
// ... | ||
}; | ||
|
||
return { handler }; | ||
} | ||
``` | ||
|
||
And then in your integration, where you call `setAdapter`, provide this name in `exports`: | ||
|
||
```diff | ||
export default function createIntegration() { | ||
return { | ||
name: '@matthewp/my-adapter', | ||
hooks: { | ||
'astro:config:done': ({ setAdapter }) => { | ||
setAdapter({ | ||
name: '@matthewp/my-adapter', | ||
serverEntrypoint: '@matthewp/my-adapter/server.js', | ||
+ exports: ['handler'], | ||
}); | ||
}, | ||
}, | ||
}; | ||
} | ||
``` | ||
|
||
#### Start | ||
|
||
Some hosts expect you to *start* the server yourselves, for example by listening to a port. For these types of hosts, the adapter API allows you to export a `start` function which will be called when the bundle script is run. | ||
|
||
```js | ||
import { App } from 'astro/app'; | ||
|
||
export function start(manifest) { | ||
const app = new App(manifest); | ||
|
||
addEventListener('fetch', event => { | ||
// ... | ||
}); | ||
} | ||
``` | ||
|
||
#### astro/app | ||
|
||
This module is used for rendering pages that have been prebuilt through `astro build`. Astro uses the standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. Hosts that have a different API for request/response should convert to these types in their adapter. | ||
|
||
```js | ||
import { App } from 'astro/app'; | ||
import http from 'http'; | ||
|
||
export function start(manifest) { | ||
const app = new App(manifest); | ||
|
||
addEventListener('fetch', event => { | ||
event.respondWith( | ||
app.render(event.request) | ||
); | ||
}); | ||
} | ||
``` | ||
|
||
The following methods are provided: | ||
|
||
##### app.render(request) | ||
|
||
This method calls the Astro page that matches the request, renders it, and returns a Promise to a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. This also works for API routes, that do not render pages. | ||
|
||
```js | ||
const response = await app.render(request); | ||
``` | ||
|
||
##### app.match(request) | ||
|
||
This method is used to determine if a request is matched by the Astro app's routing rules. | ||
|
||
```js | ||
if(app.match(request)) { | ||
const response = await app.render(request); | ||
} | ||
``` | ||
|
||
You can usually call `app.render(request)` without using `.match` because Astro handles 404s if you provide a `404.astro` file. Use `app.match(request)` if you want to handle 404s in a different way. |