@nhost/nhost-js
Version:
Nhost JavaScript SDK
441 lines (440 loc) • 16.5 kB
JavaScript
import { generateServiceUrl } from './';
import { createAPIClient as createAuthClient, } from './auth';
import { attachAccessTokenMiddleware, sessionRefreshMiddleware, updateSessionFromResponseMiddleware, withAdminSessionMiddleware, } from './fetch';
import { createAPIClient as createFunctionsClient, } from './functions';
import { createAPIClient as createGraphQLClient, } from './graphql';
import { detectStorage, refreshSession, SessionStorage, } from './session/';
import { createAPIClient as createStorageClient, } from './storage';
/**
* Built-in configuration for client-side applications.
* Includes automatic session refresh, token attachment, and session updates.
*/
export const withClientSideSessionMiddleware = ({ auth, storage, graphql, functions, sessionStorage, }) => {
const mwChain = [
sessionRefreshMiddleware(auth, sessionStorage),
updateSessionFromResponseMiddleware(sessionStorage),
attachAccessTokenMiddleware(sessionStorage),
];
for (const mw of mwChain) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
/**
* Built-in configuration for server-side applications.
* Includes token attachment and session updates, but NOT automatic session refresh
* to prevent race conditions in server contexts.
*/
export const withServerSideSessionMiddleware = ({ auth, storage, graphql, functions, sessionStorage, }) => {
const mwChain = [
updateSessionFromResponseMiddleware(sessionStorage),
attachAccessTokenMiddleware(sessionStorage),
];
for (const mw of mwChain) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
/**
* Configuration for admin clients with elevated privileges.
* Applies admin session middleware to storage, graphql, and functions clients only.
*
* **Security Warning**: Never use this in client-side code. Admin secrets grant
* unrestricted access to your entire database.
*
* @param adminSession - Admin session options including admin secret, role, and session variables
* @returns Configuration function that sets up admin middleware
*/
export function withAdminSession(adminSession) {
return ({ storage, graphql, functions }) => {
const adminMiddleware = withAdminSessionMiddleware(adminSession);
storage.pushChainFunction(adminMiddleware);
graphql.pushChainFunction(adminMiddleware);
functions.pushChainFunction(adminMiddleware);
};
}
/**
* Configuration for adding custom chain functions to all clients.
* Useful for adding custom middleware like logging, caching, or custom headers.
*
* @param chainFunctions - Array of chain functions to apply to all clients
* @returns Configuration function that sets up custom middleware
*/
export function withChainFunctions(chainFunctions) {
return ({ auth, storage, graphql, functions }) => {
for (const mw of chainFunctions) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
}
/**
* Main client class that provides unified access to all Nhost services.
* This class serves as the central interface for interacting with Nhost's
* authentication, storage, GraphQL, and serverless functions capabilities.
*/
export class NhostClient {
/**
* Authentication client providing methods for user sign-in, sign-up, and session management.
* Use this client to handle all authentication-related operations.
*/
auth;
/**
* Storage client providing methods for file operations (upload, download, delete).
* Use this client to manage files in your Nhost storage.
*/
storage;
/**
* GraphQL client providing methods for executing GraphQL operations against your Hasura backend.
* Use this client to query and mutate data in your database through GraphQL.
*/
graphql;
/**
* Functions client providing methods for invoking serverless functions.
* Use this client to call your custom serverless functions deployed to Nhost.
*/
functions;
/**
* Storage implementation used for persisting session information.
* This handles saving, retrieving, and managing authentication sessions across requests.
*/
sessionStorage;
/**
* Create a new Nhost client. This constructor is reserved for advanced use cases.
* For typical usage, use [createClient](#createclient) or [createServerClient](#createserverclient) instead.
*
* @param auth - Authentication client instance
* @param storage - Storage client instance
* @param graphql - GraphQL client instance
* @param functions - Functions client instance
* @param sessionStorage - Storage implementation for session persistence
*/
constructor(auth, storage, graphql, functions, sessionStorage) {
this.auth = auth;
this.storage = storage;
this.graphql = graphql;
this.functions = functions;
this.sessionStorage = sessionStorage;
}
/**
* Get the current session from storage.
* This method retrieves the authenticated user's session information if one exists.
*
* @returns The current session or null if no session exists
*
* @example
* ```ts
* const session = nhost.getUserSession();
* if (session) {
* console.log('User is authenticated:', session.user.id);
* } else {
* console.log('No active session');
* }
* ```
*/
getUserSession() {
return this.sessionStorage.get();
}
/**
* Refresh the session using the current refresh token
* in the storage and update the storage with the new session.
*
* This method can be used to proactively refresh tokens before they expire
* or to force a refresh when needed.
*
* @param marginSeconds - The number of seconds before the token expiration to refresh the session. If the token is still valid for this duration, it will not be refreshed. Set to 0 to force the refresh.
*
* @returns The new session or null if there is currently no session or if refresh fails
*
* @example
* ```ts
* // Refresh token if it's about to expire in the next 5 minutes
* const refreshedSession = await nhost.refreshSession(300);
*
* // Force refresh regardless of current token expiration
* const forcedRefresh = await nhost.refreshSession(0);
* ```
*/
async refreshSession(marginSeconds = 60) {
return refreshSession(this.auth, this.sessionStorage, marginSeconds);
}
/**
* Clear the session from storage.
*
* This method removes the current authentication session, effectively logging out the user.
* Note that this is a client-side operation and doesn't invalidate the refresh token on
* the server, which can be done with `nhost.auth.signOut({refreshToken: session.refreshTokenId})`.
* If the middle `updateSessionFromResponseMiddleware` is used, the session will be removed
* from the storage automatically and calling this method is not necessary.
*
* @example
* ```ts
* // Log out the user
* nhost.clearSession();
* ```
*/
clearSession() {
this.sessionStorage.remove();
}
}
/**
* Creates and configures a new Nhost client instance with custom configuration.
*
* This is the main factory function for creating Nhost clients. It instantiates
* all service clients (auth, storage, graphql, functions) and applies the provided
* configuration functions to set up middleware and other customizations.
*
* @param options - Configuration options for the client
* @returns A configured Nhost client
*
* @example
* ```ts
* // Create a basic client with no middleware
* const nhost = createNhostClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: []
* });
*
* // Create a client with custom configuration
* const nhost = createNhostClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: [
* withClientSideSessionMiddleware,
* withChainFunctions([customLoggingMiddleware])
* ]
* });
*
* // Create an admin client
* const nhost = createNhostClient({
* subdomain,
* region,
* configure: [
* withAdminSession({
* adminSecret: "nhost-admin-secret",
* role: "user",
* sessionVariables: {
* "user-id": "54058C42-51F7-4B37-8B69-C89A841D2221",
* },
* }),
* ],
* });
* ```
*/
export function createNhostClient(options = {}) {
const { subdomain, region, authUrl, storageUrl, graphqlUrl, functionsUrl, storage = detectStorage(), configure = [], } = options;
const sessionStorage = new SessionStorage(storage);
// Determine base URLs for each service
const authBaseUrl = generateServiceUrl('auth', subdomain, region, authUrl);
const storageBaseUrl = generateServiceUrl('storage', subdomain, region, storageUrl);
const graphqlBaseUrl = generateServiceUrl('graphql', subdomain, region, graphqlUrl);
const functionsBaseUrl = generateServiceUrl('functions', subdomain, region, functionsUrl);
// Create all clients
const auth = createAuthClient(authBaseUrl);
const storageClient = createStorageClient(storageBaseUrl, []);
const graphqlClient = createGraphQLClient(graphqlBaseUrl, []);
const functionsClient = createFunctionsClient(functionsBaseUrl, []);
// Apply configuration functions
for (const configFn of configure) {
configFn({
auth,
storage: storageClient,
graphql: graphqlClient,
functions: functionsClient,
sessionStorage,
});
}
// Return an initialized NhostClient
return new NhostClient(auth, storageClient, graphqlClient, functionsClient, sessionStorage);
}
/**
* Creates and configures a new Nhost client instance optimized for client-side usage.
*
* This helper method instantiates a fully configured Nhost client by:
* - Instantiating the various service clients (auth, storage, functions and graphql)
* - Auto-detecting and configuring an appropriate session storage (localStorage in browsers, memory otherwise)
* - Setting up a sophisticated middleware chain for seamless authentication management:
* - Automatically refreshing tokens before they expire
* - Attaching authorization tokens to all service requests
* - Updating the session storage when new tokens are received
*
* This method includes automatic session refresh middleware, making it ideal for
* client-side applications where long-lived sessions are expected.
*
* @param options - Configuration options for the client
* @returns A configured Nhost client
*
* @example
* ```ts
* // Create client using Nhost cloud default URLs
* const nhost = createClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1'
* });
*
* // Create client with custom service URLs
* const customNhost = createClient({
* authUrl: 'https://auth.example.com',
* storageUrl: 'https://storage.example.com',
* graphqlUrl: 'https://graphql.example.com',
* functionsUrl: 'https://functions.example.com'
* });
*
* // Create client using cookies for storing the session
* import { CookieStorage } from "@nhost/nhost-js/session";
*
* const nhost = createClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* storage: new CookieStorage({
* secure: import.meta.env.ENVIRONMENT === 'production',
* })
* });
*
* // Create client with additional custom middleware
* const nhost = createClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: [customLoggingMiddleware]
* });
* ```
*/
export function createClient(options = {}) {
const storage = options.storage ?? detectStorage();
return createNhostClient({
...options,
storage,
configure: [withClientSideSessionMiddleware, ...(options.configure ?? [])],
});
}
/**
* Creates and configures a new Nhost client instance optimized for server-side usage.
*
* This helper method instantiates a fully configured Nhost client specifically designed for:
* - Server components (in frameworks like Next.js or Remix)
* - API routes and middleware
* - Backend services and server-side rendering contexts
*
* Key differences from the standard client:
* - Requires explicit storage implementation (must be provided)
* - Disables automatic session refresh middleware (to prevent race conditions in server contexts)
* - Still attaches authorization tokens and updates session storage from responses
*
* The server client is ideal for short-lived request contexts where session tokens
* are passed in (like cookie-based authentication flows) and automatic refresh
* mechanisms could cause issues with concurrent requests.
*
* @param options - Configuration options for the server client (requires storage implementation)
* @returns A configured Nhost client optimized for server-side usage
*
* @example
* ```ts
* // Example with cookie storage for Next.js API route or server component
* import { cookies } from 'next/headers';
*
* const nhost = createServerClient({
* region: process.env["NHOST_REGION"] || "local",
* subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
* storage: {
* // storage compatible with Next.js server components
* get: (): StoredSession | null => {
* const s = cookieStore.get(key)?.value || null;
* if (!s) {
* return null;
* }
* const session = JSON.parse(s) as StoredSession;
* return session;
* },
* set: (value: StoredSession) => {
* cookieStore.set(key, JSON.stringify(value));
* },
* remove: () => {
* cookieStore.delete(key);
* },
* },
* });
*
* // Example with cookie storage for Next.js middleware
* const nhost = createServerClient({
* region: process.env["NHOST_REGION"] || "local",
* subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
* storage: {
* // storage compatible with Next.js middleware
* get: (): StoredSession | null => {
* const raw = request.cookies.get(key)?.value || null;
* if (!raw) {
* return null;
* }
* const session = JSON.parse(raw) as StoredSession;
* return session;
* },
* set: (value: StoredSession) => {
* response.cookies.set({
* name: key,
* value: JSON.stringify(value),
* path: "/",
* httpOnly: false, //if set to true we can't access it in the client
* secure: process.env.NODE_ENV === "production",
* sameSite: "lax",
* maxAge: 60 * 60 * 24 * 30, // 30 days in seconds
* });
* },
* remove: () => {
* response.cookies.delete(key);
* },
* },
* });
*
* // Example for express reading session from a cookie
*
* import express, { Request, Response } from "express";
* import cookieParser from "cookie-parser";
*
* app.use(cookieParser());
*
* const nhostClientFromCookies = (req: Request) => {
* return createServerClient({
* subdomain: "local",
* region: "local",
* storage: {
* get: (): StoredSession | null => {
* const s = req.cookies.nhostSession || null;
* if (!s) {
* return null;
* }
* const session = JSON.parse(s) as StoredSession;
* return session;
* },
* set: (_value: StoredSession) => {
* throw new Error("It is easier to handle the session in the client");
* },
* remove: () => {
* throw new Error("It is easier to handle the session in the client");
* },
* },
* });
* };
*
* // Example with additional custom middleware
* const nhost = createServerClient({
* region: process.env["NHOST_REGION"] || "local",
* subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
* storage: myStorage,
* configure: [customLoggingMiddleware]
* });
* ```
*/
export function createServerClient(options) {
return createNhostClient({
...options,
configure: [withServerSideSessionMiddleware, ...(options.configure ?? [])],
});
}
//# sourceMappingURL=nhost.js.map