UNPKG

@omneedia/ssr

Version:

Use the omneedia JavaScript library in popular server-side rendering (SSR) frameworks.

212 lines (202 loc) 7.6 kB
import { createClient, omneediaClient } from '@omneedia/client-js'; import type { GenericSchema, OmneediaClientOptions, } from '@omneedia/client-js/dist/module/lib/types'; import { VERSION } from './version'; import { DEFAULT_COOKIE_OPTIONS, combineChunks, createChunks, deleteChunks, isBrowser, isChunkLike, } from './utils'; import { createStorageFromOptions, applyServerStorage } from './cookies'; import type { CookieOptionsWithName, CookieMethodsServer, CookieMethodsServerDeprecated, } from './types'; /** * @deprecated Please specify `getAll` and `setAll` cookie methods instead of * the `get`, `set` and `remove`. These will not be supported in the next major * version. */ export function createServerClient< Database = any, SchemaName extends string & keyof Database = 'public' extends keyof Database ? 'public' : string & keyof Database, Schema extends GenericSchema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any, >( omneediaUrl: string, omneediaKey: string, options: OmneediaClientOptions<SchemaName> & { cookieOptions?: CookieOptionsWithName; cookies: CookieMethodsServerDeprecated; cookieEncoding?: 'raw' | 'base64url'; } ): omneediaClient<Database, SchemaName, Schema>; /** * Creates a omneedia Client for use on the server-side of a server-side * rendering (SSR) framework. * * There are two categories of uses for this function: use in middlewares and * use in pages, components or routes. * * **Use in middlewares.** * * Middlewares are functions that run before any rendering logic is executed on * the server-side. They typically have access to request headers (cookies) and * can modify both the request and response headers. * * In most SSR frameworks, to use omneedia correctly you *must set up a * middleware* and use this function in it. * * When using this in a middleware, the `cookie` option must be configured to * use both `getAll` and `setAll`. Alternatively you can use the `get`, `set` * and `remove` functions. The latter are deprecated **and not recommended** * for most use cases due to being difficult to use properly and they do not * cover important edge cases. In future major versions of the library, the * option to configure `get`, `set` and `remove` will be removed. * * **IMPORTANT:** Failing to implement `getAll` and `setAll` correctly (or the * deprecated `get`, `set` and `remove`) including omitting them **will cause * significant and difficult to debug authentication issues**. They will * manifest as: random logouts, early session termination, JSON parsing errors, * increased number of refresh token requests, or relying on garbage state. * * **Use in pages, components or routes.** * * To use omneedia features server-side rendered in pages, components or routes * (a.k.a. actions / APIs) you must create a client with this funciton. Not all * frameworks allow the ability to set cookies or response headers when pages * or components are rendered. In those cases you _can omit `setAll` (or the * deprecated `set`, `remove`) cookie option methods_. **It is strongly * recommended that if the ability to set cookies and response headers is * present, you should configure the `setAll` (or the deprecated `set` and * `remove`) cookie access methods.** * * **IMPORTANT:** If the ability to set cookies or response headers is not * available **middleware or an equivalent must be used.** Failing to do this * will cause significant and difficult to debug authentication issues. * * When `setAll` (or the deprecated `set`, `remove`) cookie methods are not * configured, the omneedia Client will emit a warning if it is used in a way * that requires setting cookies. If you see this warning, it usually means * that you are using the omneedia Client in a wrong way: * * - You should have, but did not configure a middleware client. * - There is a bug in your middleware function. * - You are using features of the omneedia Client that change the User, e.g. * by calling `omneedia.auth.updateUser()` on the server. * * Please consult the latest omneedia guides for advice on how to avoid common * pitfalls depending on SSR framework. * * @param omneediaUrl The URL of the omneedia project. * @param omneediaKey The `anon` API key of the omneedia project. * @param options Various configuration options. */ export function createServerClient< Database = any, SchemaName extends string & keyof Database = 'public' extends keyof Database ? 'public' : string & keyof Database, Schema extends GenericSchema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any, >( omneediaUrl: string, omneediaKey: string, options: OmneediaClientOptions<SchemaName> & { cookieOptions?: CookieOptionsWithName; cookies: CookieMethodsServer; cookieEncoding?: 'raw' | 'base64url'; } ): omneediaClient<Database, SchemaName, Schema>; export function createServerClient< Database = any, SchemaName extends string & keyof Database = 'public' extends keyof Database ? 'public' : string & keyof Database, Schema extends GenericSchema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any, >( omneediaUrl: string, omneediaKey: string, options: OmneediaClientOptions<SchemaName> & { cookieOptions?: CookieOptionsWithName; cookies: CookieMethodsServer | CookieMethodsServerDeprecated; cookieEncoding?: 'raw' | 'base64url'; } ): omneediaClient<Database, SchemaName, Schema> { if (!omneediaUrl || !omneediaKey) { throw new Error( `Your project's URL and Key are required to create a omneedia client!\n\nCheck your omneedia project's API settings to find these values\n\nhttps://omneedia.com/dashboard/project/_/settings/api` ); } const { storage, getAll, setAll, setItems, removedItems } = createStorageFromOptions( { ...options, cookieEncoding: options?.cookieEncoding ?? 'base64url', }, true ); const client = createClient<Database, SchemaName, Schema>( omneediaUrl, omneediaKey, { ...options, global: { ...options?.global, headers: { ...options?.global?.headers, 'X-Client-Info': `omneedia-ssr/${VERSION}`, }, }, auth: { ...(options?.cookieOptions?.name ? { storageKey: options.cookieOptions.name } : null), ...options?.auth, flowType: 'pkce', autoRefreshToken: false, detectSessionInUrl: false, persistSession: true, storage, }, } ); client.auth.onAuthStateChange(async (event: any) => { // The SIGNED_IN event is fired very often, but we don't need to // apply the storage each time it fires, only if there are changes // that need to be set -- which is if setItems / removeItems have // data. const hasStorageChanges = Object.keys(setItems).length > 0 || Object.keys(removedItems).length > 0; if ( hasStorageChanges && (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED' || event === 'USER_UPDATED' || event === 'PASSWORD_RECOVERY' || event === 'SIGNED_OUT' || event === 'MFA_CHALLENGE_VERIFIED') ) { await applyServerStorage( { getAll, setAll, setItems, removedItems }, { cookieOptions: options?.cookieOptions ?? null, cookieEncoding: options?.cookieEncoding ?? 'base64url', } ); } }); return client; }