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

Server-side rendering docs #255

Merged
merged 1 commit into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const SIDEBAR = {
{ text: 'Pagination', link: 'en/guides/pagination' },
{ text: 'RSS', link: 'en/guides/rss' },
{ text: 'UI Frameworks', link: 'en/core-concepts/framework-components' },
{ text: 'Server-side Rendering (experimental)', link: 'en/guides/server-side-rendering' },

{ text: 'Reference', header: true, type: 'api' },
{
Expand All @@ -46,6 +47,7 @@ export const SIDEBAR = {
{ text: 'CLI', link: 'en/reference/cli-reference' },
{ text: 'Runtime API', link: 'en/reference/api-reference' },
{ text: 'Integrations API', link: 'en/reference/integrations-reference' },
{ text: 'Adapter API (experimental)', link: 'en/reference/adapter-reference' },
{ text: 'Routing Rules', link: 'en/core-concepts/routing' },
// ADD: Astro Component Syntax
// ADD: Markdown Syntax
Expand Down
139 changes: 139 additions & 0 deletions src/pages/en/guides/server-side-rendering.md
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
});
}
```
159 changes: 159 additions & 0 deletions src/pages/en/reference/adapter-reference.md
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.