Back to blog

Introducing support for server-side rendering

Server-side rendering (SSR) is now fully supported with Appwrite allowing more flexibility to build how you want.

We're excited to introduce support for server-side rendering (SSR) authentication patterns. This change enhances the developer experience when building with popular frameworks that provide SSR as an option, such as Next.js, SvelteKit, Nuxt, Astro, Remix, and more.

Until now, Appwrite’s authentication system has been optimized for client-side rendering, which worked well with single page applications (SPAs), but had limitations on how SSR could be implemented. While it was possible to implement SSR authentication, it was hacky and undocumented. In the latest release, we are officially introducing support for SSR with new methods and workflows to the server SDK's.

Understanding past limitations

To understand the new changes and workflows, let's first take a step back and address the challenges we aimed to solve in this new release.

When building applications with SSR, we need a way to generate and store a session secret server-side to protect API routes and pages and a way to make authenticated requests.

The problem we faced was that there was no way to access a session secret when using authentication methods. When using methods such as createEmailPasswordSession, Appwrite’s web SDK automatically stores the session in the browser's cookies and does not make it available to you. For client-side rendering, this is a non-issue since we don't need to access the session manually. However, when it comes to SSR we need a way to access a session and set it in the server's cookies for subsequent requests.

Getting started with SSR

As you get started with SSR it’s important to note that all methods we will be working with are based on Appwrite’s server SDKs. In the examples below, we will use the Node JS SDK.

It’s also recommended that as you follow along with this article, you do not install any client SDK’s as that may lead to confusion. All examples use node-appwrite.

Bash
npm install node-appwrite

Create sessions server-side

To solve this issue, all existing server SDK methods that create a session now return a secret attribute. The following methods are:

  • account.createEmailPasswordSession(email, password)

  • account.createAnonymousSession()

  • account.createSession(userId, token)

You can use these methods now to create a session, and to set a session cookie on your domain.

React
const session = account.createEmailPasswordSession(email, password)

console.log(session.secret) // Output: 'eyJpZCI...sdfahfkjjy'

Using session secrets

With a session cookie set, we can now authenticate users and protect routes.

When using the Appwrite SDK, you can use the new setSession method to authenticate a user for any request.

React
client.setSession(session.secret)
const currentUser = await account.get()
Appwrite client security

It’s important to note that the client instance should be re-created for every request. Failure to do so on your server SDK could result in leaked/shared session cookies between request

Admin and Session Clients

With the new authentication patterns for SSR, you’ll need to create two different types of instances of an Appwrite client when initializing the SDK. An admin client for performing admin request, and a session client for performing authenticated request on behalf of an end user.

Admin Client

The admin client will need to be initialized with an API key in order to bypass rate limits when performing unauthenticated request or when we need to perform actions that bypass permissions. Without an API key our server will be rate limited when trying to make request to certain endpoints.

React
import { Client } from "node-appwrite" 

const adminClient = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1') 
    .setProject('<YOUR_PROJECT_ID>')           
    .setKey('<YOUR_API_KEY>')     

Session Client

A session client will allow us to make requests as an authenticated end-user with the setSession helper method.

React
const sessionClient = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1') 
    .setProject('<YOUR_PROJECT_ID>')          

const session = req.cookies.session 

if (session) {
    sessionClient.setSession(session)
}   

const user = await account.get()

Seeing it in action

Using Next.js, let’s see how we can create an application with two endpoints:

  • /api/signin - creates a session and sets a session cookie

  • /api/user - retrieves session from cookies and authenticates the request

We'll use an admin client to create sessions and a session client to perform authenticated requests.

Creating admin and session client

First, we’ll need to configure our SDK and create two methods to initiate our clients in /src/appwrite.js. We'll use environment variables to store our Appwrite endpoint, project ID, and API key.

React
import { Client, Account } from "node-appwrite"
import { parseCookie } from "next/dist/compiled/@edge-runtime/cookies";

export function createAdminClient() => {
    const client = new Client()
        .setEndpoint(process​.env.PUBLIC_APPWRITE_ENDPOINT)
        .setProject(process​.env.PUBLIC_APPWRITE_PROJECT)
        .setKey(process​.env.APPWRITE_API_KEY)

    return {
        get account() {
            return new Account(client)
        }
    }
}

export function  createSessionClient(headers) {
    const client = new Client()
        .setEndpoint(process​.env.PUBLIC_APPWRITE_ENDPOINT)
        .setProject(process​.env.PUBLIC_APPWRITE_PROJECT)

    const cookies = parseCookie(headers.get('cookie') ?? '')
    const session = cookies.get('my-session-cookie') ?? ''
    if (session) {
        client.setSession(session.secret)
    }

    return {
		get account() {
			return new Account(client)
		}
	}
}

Creating a session

Now, in the /signin route, we can use the admin client to generate a session and set a cookie.

React
import { createAdminClient } from "@/lib/appwrite"
import { cookies } from 'next/headers'

export async function POST(request){
    const { account } = await  createAdminClient()

    const { email, password } = await request.json()
    const session = await account.createEmailPasswordSession(email, password)
   
    cookies().set('session', session.secret, {
        httpOnly: true,
        secure: true,
        sameSite: 'strict',
        expires: new Date(session.expire),
        path: '/',
    })

    return Response.redirect('/api/user')
}

Authenticating requests

With a session set, we can now use the session client to authenticate a user and return the user object. This session client can now be used on all requests that require an authenticated user.

React
import { createSessionClient } from "@/lib/appwrite"
import { headers } from 'next/headers'

export async function GET(request){
    const { account } = await createSessionClient(headers)
    try {
        const user = await account.get()
        return Response.json(user)
    } catch (error) {
        return Response.json(error)
    }
}

Resources

Visit our documentation to learn more about SSR, join us on Discord to be part of the discussion, visit our blog and YouTube channel to learn more, or visit our GitHub repository to see our source code.

SSR will be available as part of the Appwrite 1.5 release on GitHub and Cloud in March 2024.

Go back to Init

Subscribe to our newsletter

Sign up to our company blog and get the latest insights from Appwrite. Learn more about engineering, product design, building community, and tips & tricks for using Appwrite.

Copyright © 2024 Appwrite