or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

analytics.mdapi.mdauth.mddatastore.mdindex.mdnotifications.mdserver.mdstorage.mdutilities.md
tile.json

server.mddocs/

Server Context

Server-side support for Next.js, Node.js, and other server environments with proper context isolation, OAuth handling, and server-specific utilities for Amplify operations.

Capabilities

Core Import

import { 
  runWithAmplifyServerContext,
  createKeyValueStorageFromCookieStorageAdapter,
  createAWSCredentialsAndIdentityIdProvider,
  createUserPoolsTokenProvider
} from "aws-amplify/adapter-core";

Server Context Execution

Run Amplify operations within a server context with proper request isolation.

/**
 * Execute operations within an Amplify server context
 * @param context - Server context configuration
 * @param operation - Function to execute within the context
 * @returns Promise with the result of the operation
 */
function runWithAmplifyServerContext<T>(
  context: AmplifyServer,
  operation: (contextSpec: AmplifyServerContextSpec) => T | Promise<T>
): Promise<T>;

interface AmplifyServer {
  config: ResourcesConfig;
  cookies?: CookiesAdapter;
  headers?: Record<string, string>;
  request?: {
    url: string;
    method?: string;
    headers?: Record<string, string>;
  };
}

interface AmplifyServerContextSpec {
  token: {
    value: Symbol;
  };
}

interface CookiesAdapter {
  get(name: string): CookieValue | undefined;
  getAll(): Record<string, CookieValue>;
  set(name: string, value: string, options?: CookieOptions): void;
  delete(name: string, options?: CookieOptions): void;
}

interface CookieValue {
  name: string;
  value: string;
}

interface CookieOptions {
  domain?: string;
  expires?: Date;
  httpOnly?: boolean;
  maxAge?: number;
  path?: string;
  priority?: 'low' | 'medium' | 'high';
  sameSite?: boolean | 'lax' | 'strict' | 'none';
  secure?: boolean;
}

Server Context Usage:

import { runWithAmplifyServerContext } from "aws-amplify/adapter-core";
import { getCurrentUser } from "aws-amplify/auth/server";
import { cookies } from "next/headers";

// Next.js API route example
export async function GET(request: Request) {
  return await runWithAmplifyServerContext({
    config: {
      Auth: {
        Cognito: {
          userPoolId: process.env.USER_POOL_ID!,
          userPoolClientId: process.env.USER_POOL_CLIENT_ID!,
          region: process.env.AWS_REGION!
        }
      }
    },
    cookies: {
      get: (name) => {
        const cookie = cookies().get(name);
        return cookie ? { name: cookie.name, value: cookie.value } : undefined;
      },
      getAll: () => {
        const allCookies: Record<string, CookieValue> = {};
        cookies().getAll().forEach(cookie => {
          allCookies[cookie.name] = { name: cookie.name, value: cookie.value };
        });
        return allCookies;
      },
      set: (name, value, options) => {
        cookies().set(name, value, options);
      },
      delete: (name, options) => {
        cookies().delete(name);
      }
    }
  }, async (contextSpec) => {
    try {
      const user = await getCurrentUser();
      return Response.json({ user });
    } catch (error) {
      return Response.json({ error: 'Not authenticated' }, { status: 401 });
    }
  });
}

Storage Adapters

Create storage adapters for server environments using cookies and other storage mechanisms.

/**
 * Create key-value storage from cookie storage adapter
 * @param cookieStorageAdapter - Cookie storage implementation
 * @returns KeyValueStorageInterface for server use
 */
function createKeyValueStorageFromCookieStorageAdapter(
  cookieStorageAdapter: CookieStorageAdapter
): KeyValueStorageInterface;

interface CookieStorageAdapter {
  get(name: string): string | undefined;
  set(name: string, value: string, options?: CookieOptions): void;
  delete(name: string): void;
}

interface KeyValueStorageInterface {
  setItem(key: string, value: string): Promise<void> | void;
  getItem(key: string): Promise<string | null> | string | null;
  removeItem(key: string): Promise<void> | void;
  clear(): Promise<void> | void;
}

Storage Adapter Example:

import { createKeyValueStorageFromCookieStorageAdapter } from "aws-amplify/adapter-core";
import { cookies } from "next/headers";

// Create storage adapter using Next.js cookies
const cookieStorage = createKeyValueStorageFromCookieStorageAdapter({
  get: (name) => cookies().get(name)?.value,
  set: (name, value, options) => {
    cookies().set(name, value, {
      ...options,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict'
    });
  },
  delete: (name) => {
    cookies().delete(name);
  }
});

// Use in server context
await cookieStorage.setItem('user_preference', 'dark_mode');
const preference = await cookieStorage.getItem('user_preference');

Provider Creation

Create AWS credentials and token providers for server-side authentication.

/**
 * Create AWS credentials and identity ID provider
 * @param config - Provider configuration
 * @returns Credentials provider for server use
 */
function createAWSCredentialsAndIdentityIdProvider(
  config: CredentialsProviderConfig
): CredentialsAndIdentityIdProvider;

/**
 * Create user pools token provider
 * @param config - Token provider configuration
 * @returns Token provider for server use
 */
function createUserPoolsTokenProvider(
  config: TokenProviderConfig
): TokenProvider;

interface CredentialsProviderConfig {
  authConfig: AuthConfig;
  credentialsStorage: KeyValueStorageInterface;
  identityStorage: KeyValueStorageInterface;
}

interface TokenProviderConfig {
  authConfig: AuthConfig;
  keyValueStorage: KeyValueStorageInterface;
}

interface CredentialsAndIdentityIdProvider {
  getCredentialsAndIdentityId(): Promise<CredentialsAndIdentityId>;
  clearCredentialsAndIdentityId(): Promise<void>;
}

interface TokenProvider {
  getTokens(): Promise<AuthTokens>;
  clearTokens(): Promise<void>;
}

OAuth Server Utilities

Server-side OAuth flow utilities for handling redirects and state management.

/**
 * Generate OAuth state parameter
 * @returns Random state string for OAuth flow
 */
function generateState(): string;

/**
 * Get OAuth redirect URL
 * @param config - OAuth configuration
 * @returns Complete redirect URL for OAuth provider
 */
function getRedirectUrl(config: OAuthConfig): string;

/**
 * Generate PKCE code verifier for OAuth
 * @returns Code verifier string
 */
function generateCodeVerifier(): string;

/**
 * Validate OAuth state parameter
 * @param receivedState - State received from OAuth provider
 * @param expectedState - Expected state value
 * @returns Boolean indicating if state is valid
 */
function validateState(receivedState: string, expectedState: string): boolean;

interface OAuthConfig {
  provider: string;
  clientId: string;
  redirectUri: string;
  scopes: string[];
  state?: string;
  codeChallenge?: string;
  codeChallengeMethod?: string;
}

OAuth Server Example:

import { 
  generateState, 
  getRedirectUrl, 
  generateCodeVerifier,
  validateState 
} from "aws-amplify/adapter-core";

// Initiate OAuth flow
export async function initiateOAuth() {
  const state = generateState();
  const codeVerifier = generateCodeVerifier();
  
  // Store state and code verifier securely
  await storeOAuthData({ state, codeVerifier });
  
  const redirectUrl = getRedirectUrl({
    provider: 'Google',
    clientId: process.env.GOOGLE_CLIENT_ID!,
    redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/auth/callback`,
    scopes: ['openid', 'profile', 'email'],
    state,
    codeChallenge: await generateCodeChallenge(codeVerifier),
    codeChallengeMethod: 'S256'
  });
  
  return Response.redirect(redirectUrl);
}

// Handle OAuth callback
export async function handleOAuthCallback(request: Request) {
  const url = new URL(request.url);
  const receivedState = url.searchParams.get('state');
  const authCode = url.searchParams.get('code');
  
  const storedData = await getStoredOAuthData();
  
  if (!validateState(receivedState!, storedData.state)) {
    return Response.json({ error: 'Invalid state' }, { status: 400 });
  }
  
  // Exchange code for tokens using stored code verifier
  // ... token exchange logic
}

Auth Storage Utilities

Utilities for managing authentication storage keys and configuration.

/**
 * Create storage keys for authentication data
 * @param identifier - User or session identifier
 * @returns Object with authentication storage keys
 */
function createKeysForAuthStorage(identifier: string): AuthStorageKeys;

/**
 * Authentication storage key prefix
 */
const AUTH_KEY_PREFIX: string;

/**
 * Default max age for authentication cookies (in seconds)
 */
const DEFAULT_AUTH_TOKEN_COOKIES_MAX_AGE: number;

interface AuthStorageKeys {
  accessToken: string;
  idToken: string;
  refreshToken: string;
  clockDrift: string;
  signInDetails: string;
  userData: string;
}

Auth Storage Example:

import { 
  createKeysForAuthStorage,
  AUTH_KEY_PREFIX,
  DEFAULT_AUTH_TOKEN_COOKIES_MAX_AGE 
} from "aws-amplify/adapter-core";

// Create storage keys for a user
const userId = 'user123';
const authKeys = createKeysForAuthStorage(userId);

console.log(authKeys);
// {
//   accessToken: 'amplify_auth_user123_accessToken',
//   idToken: 'amplify_auth_user123_idToken',
//   refreshToken: 'amplify_auth_user123_refreshToken',
//   clockDrift: 'amplify_auth_user123_clockDrift',
//   signInDetails: 'amplify_auth_user123_signInDetails',
//   userData: 'amplify_auth_user123_userData'
// }

// Set token with default expiration
cookies().set(authKeys.accessToken, tokenValue, {
  maxAge: DEFAULT_AUTH_TOKEN_COOKIES_MAX_AGE,
  secure: true,
  httpOnly: true,
  sameSite: 'strict'
});

Server-Side API Operations

Server-specific versions of API, auth, and storage operations.

// Available from specific server imports
import { getCurrentUser, fetchAuthSession } from "aws-amplify/auth/server";
import { generateClient } from "aws-amplify/api/server";
// Or use the data alias: import { generateClient } from "aws-amplify/data/server";
import { getProperties, getUrl, list, remove, copy } from "aws-amplify/storage/server";

// Note: uploadData and downloadData are NOT available on server-side
// Use client-side operations for file upload/download functionality

Server API Usage:

import { runWithAmplifyServerContext } from "aws-amplify/adapter-core";
import { getCurrentUser } from "aws-amplify/auth/server";
import { generateClient } from "aws-amplify/api/server";

// Server-side API call
export async function serverAction() {
  return await runWithAmplifyServerContext({
    config: amplifyConfig,
    cookies: cookiesAdapter
  }, async (contextSpec) => {
    // Get current user on server
    const user = await getCurrentUser();
    
    // Make GraphQL query on server
    const client = generateClient();
    const result = await client.graphql({
      query: `query GetUserData($userId: ID!) {
        getUser(id: $userId) {
          id
          name
          email
        }
      }`,
      variables: { userId: user.userId }
    });
    
    return result.data.getUser;
  });
}

Middleware Integration

Integration with Next.js middleware and other server frameworks.

// Next.js middleware example
import { NextRequest, NextResponse } from "next/server";
import { runWithAmplifyServerContext } from "aws-amplify/adapter-core";
import { fetchAuthSession } from "aws-amplify/auth/server";

export async function middleware(request: NextRequest) {
  const response = NextResponse.next();
  
  // Check authentication on protected routes
  if (request.nextUrl.pathname.startsWith('/protected')) {
    try {
      await runWithAmplifyServerContext({
        config: amplifyConfig,
        cookies: {
          get: (name) => request.cookies.get(name),
          getAll: () => {
            const cookies: Record<string, CookieValue> = {};
            request.cookies.getAll().forEach(cookie => {
              cookies[cookie.name] = cookie;
            });
            return cookies;
          },
          set: (name, value, options) => {
            response.cookies.set(name, value, options);
          },
          delete: (name) => {
            response.cookies.delete(name);
          }
        }
      }, async () => {
        const session = await fetchAuthSession();
        if (!session.tokens) {
          throw new Error('No valid session');
        }
      });
    } catch (error) {
      // Redirect to login if not authenticated
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }
  
  return response;
}

export const config = {
  matcher: ['/protected/:path*']
};

Types

// Server context types
interface AmplifyServer {
  config: ResourcesConfig;
  cookies?: CookiesAdapter;
  headers?: Record<string, string>;
  request?: ServerRequest;
}

interface ServerRequest {
  url: string;
  method?: string;
  headers?: Record<string, string>;
  body?: any;
}

// Legacy configuration support
interface LegacyConfig {
  [key: string]: any;
}

// Amplify outputs format (deprecated)
interface AmplifyOutputs {
  version: string;
  auth?: any;
  data?: any;
  storage?: any;
  custom?: any;
}

// Error types
class ServerError extends Error {
  name: 'ServerError';
  message: string;
  statusCode?: number;
  cause?: Error;
}

class ServerContextError extends ServerError {
  name: 'ServerContextError';
}

Error Handling

Server operations can encounter specific server-side errors:

import { runWithAmplifyServerContext, ServerError } from "aws-amplify/adapter-core";

try {
  await runWithAmplifyServerContext(context, async () => {
    // Server operations
  });
} catch (error) {
  if (error instanceof ServerError) {
    switch (error.name) {
      case 'ServerContextError':
        console.log('Server context error:', error.message);
        break;
      case 'ConfigurationError': 
        console.log('Server configuration error:', error.message);
        break;
      default:
        console.log('Server error:', error.message);
        break;
    }
  } else {
    console.log('Unexpected error:', error);
  }
}

Best Practices

Context Management

  • Always use runWithAmplifyServerContext for server operations
  • Provide proper cookie adapters for session management
  • Handle context isolation properly in concurrent requests
  • Clean up resources after request completion

Security

  • Use secure, HTTP-only cookies for authentication tokens
  • Implement proper CSRF protection
  • Validate OAuth state parameters
  • Use HTTPS in production environments

Performance

  • Cache server context when possible
  • Implement proper error boundaries
  • Use appropriate timeout values
  • Monitor server resource usage

Framework Integration

  • Follow framework-specific patterns (Next.js middleware, Express middleware, etc.)
  • Implement proper request/response handling
  • Use framework-native cookie and header management
  • Integrate with framework authentication patterns