Server Side Integration

Prev Next

Unfortunately, there is no "one size fits all" solution because each framework handle SSR differently (and even in a single framework there is more than one way of doing things).

But in general, the idea is to use a cookie based flow:

  1. Create a new XGenClient instance for each server-side request

  2. "Load/Feed" your xg.authStore with data from the request cookie

  3. Perform your application server-side actions

  4. Before returning the response to the client, update the cookie with the latest xg.authStore state

All BaseAuthStore instances have 2 helper methods that should make working with cookies a little bit easier:

// update the store with the parsed data from the cookie string
xg.authStore.loadFromCookie('xgen_token=...;xgen_user=...;xgen_session=...');

// exports the store data as cookie, with option to extend the default SameSite, Secure, HttpOnly, Path and Expires attributes
xg.authStore.exportToCookie({ httpOnly: false }); // Output: 'xgen_token=...;xgen_user=...;xgen_session=...'


Below you can find an example of how to integrate with Express:

Express

One way to integrate with Express could be to create the XGen client in a [middleware](https://expressjs.com/en/guide/using-middleware.html) and pass it to the other routes using the res.locals.

Note: Your server will need to handle CORS

  • set the allowed origin and credentials: true on your server

  • Make sure to include credentials: include when making the fetch call

  • When calling xg.authStore.exportToCookie() make sure you pass the option to switch sameSite: false

    • xg.authStore.exportToCookie({secure: true, sameSite: false, httpOnly: true, path: '/'})

// src/index.ts
import express from 'express';
import XGenClient, { type ClientParams } from '@xgenai/sdk-core';

// locals typescript
declare global {
	namespace Express {
		interface Locals {
			xg: XGenClient;
		}
	}
}

const clientParams: ClientParams = {
	key: 'client-key',
	secret: 'client-secret',
	clientId: 'client-id',
	trackerId: 'tracker-id',
};

export const app = express();

const port = 3000;

// Middleware - creates a new client and loads the cookie info (user, session, token data)
app.use((req, res, next) => {
	const xg = new XGenClient(clientParams);
	if (xg) {
		res.locals.xg = xg;
	}

	xg.authStore.loadFromCookie(req.headers.cookie || '');

	next();
});

// Routes
app.get('/', (_, res) => {
	res.send('Healthy!');
});

app.get('/products', async (_, res) => {
	try {
		const recommendations = await res.locals.xg.recommend.getResultsById({
			elementId: '<element_id>',
		});

		res.setHeader('Set-Cookie', res.locals.xg.authStore.exportToCookie());

		res.send(recommendations);
	} catch (err) {
		console.error(err);
		res.send(err);
	}

});

// Server
app.listen(port, () => {
	console.logExample app listening on port ${port});
});


Below you can find an example of how to integrate with SvelteKit SSR:

SvelteKit

One way to integrate with SvelteKit SSR could be to create the XGen client in a hook handle and pass it to the other server-side actions using the event.locals.

// src/hooks.server.js
import XGenClient from '@xgenai/sdk-core';

/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  event.locals.xg = new XGenClient({/*params*/});

  // load the store data from the request cookie string
  event.locals.xg.authStore.loadFromCookie(event.request.headers.get('cookie') || '');

  const response = await resolve(event);

  // send back the default 'xgen_token', 'xgen_user', 'xgen_session' cookies to the client with the latest store state
  response.headers.append('set-cookie', event.locals.xg.authStore.exportToCookie());

  return response;
}

And then, in some of your server-side actions, you could directly access the previously created event.locals.xg instance:

// src/routes/recommendations/+server.js

/**

* Creates a `POST /recommendations` server-side endpoint

*

* @type {import('./$types').RequestHandler}

*/

export async function POST({ request, locals }) {
  const { email, password } = await request.json();
  const results = await xg.recommend.getResultsById({elementId: '<element_id>'})
  return new Response(results);
}

For proper locals.xg type detection, you can also add XGenClient in your your global types definition:

// src/app.d.ts

import XGenClient from '@xgenai/sdk-core';

declare global {
  declare namespace App {
    interface Locals {
      xg: XGenClient
    }
  }
}