npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qumra/app-react-router

v1.0.5

Published

Qumra app integration for React Router 7 - Authentication, session management, and embedded app support

Readme

@qumra/app-react-router

npm version License: ISC

Build Qumra apps with React Router 7 — authentication, session management, and embedded app support.

A first-class React Router 7 integration for the Qumra apps platform. Provides server-side factories, authentication middleware, session management, and admin GraphQL client support for building secure, scalable embedded applications.

Installation

Install the package and its peer dependencies:

npm install @qumra/app-react-router react react-dom react-router

Or with yarn:

yarn add @qumra/app-react-router react react-dom react-router

Or with pnpm:

pnpm add @qumra/app-react-router react react-dom react-router

Quick Start

1. Initialize the Qumra App

Create a qumra.server.ts file to configure your app:

import { qumraApp } from '@qumra/app-react-router/server';
import { PrismaSessionStorage } from '@qumra/app-react-router';

export const qumra = qumraApp({
  apiKey: process.env.QUMRA_API_KEY!,
  accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
  refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
  sessionStorage: new PrismaSessionStorage(prisma),
  hooks: {
    afterAuth: async (context) => {
      // Custom logic after successful authentication
      console.log('User authenticated:', context.admin.userId);
    },
  },
});

2. Define a Route with Admin Context

Use the qumra factory in your route loaders to authenticate requests:

// routes/api/dashboard.tsx
import { json, type LoaderFunctionArgs } from 'react-router';
import { qumra } from '../qumra.server';

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const admin = await qumra.authenticate(request);

  // Fetch admin data using the GraphQL client
  const user = await admin.graphql(`
    query {
      user {
        id
        email
      }
    }
  `);

  return json({ user });
};

export default function Dashboard() {
  const { user } = useLoaderData<typeof loader>();
  return <div>Welcome, {user.email}!</div>;
}

App Provider

Wrap your root route with the QumraAppProvider to enable session management and authentication state across your React tree:

// root.tsx
import { QumraAppProvider } from '@qumra/app-react-router';
import { Outlet } from 'react-router';

export default function Root() {
  return (
    <QumraAppProvider>
      <Outlet />
    </QumraAppProvider>
  );
}

Authentication

Admin Authentication in Loaders

The qumra.authenticate() method validates the request and returns an admin context with full access to platform APIs:

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const admin = await qumra.authenticate(request);

  // Access admin properties
  console.log(admin.userId);
  console.log(admin.appInstallationId);
  console.log(admin.shopId);

  return json({ /* ... */ });
};

When authentication fails, an AuthenticationError is thrown with appropriate error details. Use an error boundary to handle these gracefully.

Webhook Handling

Handle incoming webhooks from the Qumra platform:

// routes/webhooks.tsx
import { json, type ActionFunctionArgs } from 'react-router';
import { qumra } from '../qumra.server';

export const action = async ({ request }: ActionFunctionArgs) => {
  if (request.method === 'POST') {
    const webhook = await qumra.webhooks.process(request);

    switch (webhook.topic) {
      case 'app/installed':
        // Handle app installation
        console.log(`App installed for shop: ${webhook.shopId}`);
        break;
      case 'app/uninstalled':
        // Handle app uninstallation
        console.log(`App uninstalled from shop: ${webhook.shopId}`);
        break;
    }

    return json({ success: true });
  }
};

Boundary Utilities

Error Boundary

Catch and handle errors gracefully using the exported error boundary. This automatically renders appropriate error pages for different error types:

// routes/root.tsx
import { boundary } from '@qumra/app-react-router/server';
import { useRouteError } from 'react-router';

export function ErrorBoundary() {
  const error = useRouteError();
  return boundary.error(error);
}

The error boundary handles:

  • AuthenticationError — Invalid or missing credentials
  • InvalidSessionError — Session validation failures
  • SessionStorageError — Database or storage errors
  • Generic HTTP errors and unexpected exceptions

Headers Management

Automatically set security headers (CSP, X-Frame-Options) on all responses:

// routes/root.tsx
import { boundary } from '@qumra/app-react-router/server';

export const headers = boundary.headers;

App Distribution

Control how your app is distributed across the Qumra ecosystem using the AppDistribution enum:

import { AppDistribution } from '@qumra/app-react-router/server';

export const qumra = qumraApp({
  // ...
  distribution: AppDistribution.AppStore, // or AppDistribution.PrivateTable
});

Distribution Types

  • AppStore: Your app is publicly listed and installable by any Qumra user. Uses OAuth token exchange for authentication.
  • PrivateTable: Your app is restricted to specific tables or organizations. Uses pre-configured access tokens.

Example: Conditional Logic by Distribution

const config = {
  apiKey: process.env.QUMRA_API_KEY!,
  accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
  refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: process.env.APP_DISTRIBUTION === 'public'
    ? AppDistribution.AppStore
    : AppDistribution.PrivateTable,
};

export const qumra = qumraApp(config);

Private App Example

For private apps with pre-configured tokens:

const qumra = qumraApp({
  apiKey: process.env.QUMRA_API_KEY!,
  accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
  refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: AppDistribution.PrivateTable,
  adminAccessToken: process.env.QUMRA_ADMIN_TOKEN,
});

Package Exports

The @qumra/app-react-router package is organized into three export paths for flexibility and tree-shaking:

@qumra/app-react-router (default)

Main entry point with commonly used exports:

import {
  QumraAppProvider,
  PrismaSessionStorage,
  SessionStorageError,
  InvalidSessionError,
  getSessionToken,
  parseSessionToken,
} from '@qumra/app-react-router';

@qumra/app-react-router/server

Server-side utilities for route loaders and actions:

import {
  qumraApp,
  boundary,
  AppDistribution,
} from '@qumra/app-react-router/server';

Includes:

  • qumraApp() — App factory function
  • boundary.error — Error boundary handler
  • boundary.headers — Security headers helper
  • AppDistribution — Distribution enum
  • Type definitions for server contexts

@qumra/app-react-router/react

React-specific exports (hooks, context providers):

import { QumraAppProvider, useQumraApp } from '@qumra/app-react-router/react';

GraphQL Client

Execute authenticated GraphQL queries and mutations using the admin GraphQL client. The client is available on the admin context returned by qumra.authenticate():

const admin = await qumra.authenticate(request);

const result = await admin.graphql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      email
      role
    }
  }
`, {
  variables: {
    id: 'user-123',
  },
});

The GraphQL client automatically includes authentication headers and handles error responses:

try {
  const result = await admin.graphql(query);
  console.log(result.data);
} catch (error) {
  if (error instanceof GraphQLError) {
    console.error('GraphQL error:', error.message);
  }
}

Use GraphQL to fetch store data, manage resources, and trigger platform actions securely.

Hooks

afterAuth

Customize behavior after successful authentication by providing an afterAuth hook in your app configuration. This hook runs on every authenticated request:

export const qumra = qumraApp({
  apiKey: process.env.QUMRA_API_KEY!,
  accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
  refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
  sessionStorage: new PrismaSessionStorage(prisma),
  hooks: {
    afterAuth: async (context) => {
      const { admin, request } = context;

      // Log authentication events
      console.log(`User ${admin.userId} authenticated`);

      // Sync user data to your database
      await syncUserData(admin.userId, admin.shopId);

      // Validate permissions or perform initialization
      const userRole = await getUserRole(admin.userId);
      if (!userRole) {
        throw new Error('User role not found');
      }
    },
  },
});

The afterAuth hook receives an AfterAuthOptions context with:

  • admin — The authenticated admin context with API access (userId, shopId, appInstallationId, graphql())
  • request — The original HTTP request
  • response — The HTTP response object for setting headers

Return a rejected promise or throw to stop authentication.

Qumra Ecosystem

This package is part of the comprehensive Qumra apps ecosystem. Here are the core packages:

| Package | Purpose | |---------|---------| | @qumra/app-sdk | Core SDK — auth, session, security | | @qumra/app-react-router | React Router 7 integration | | @qumra/app-session-storage-prisma | Prisma session storage | | @qumra/app-session-storage-mongodb | MongoDB session storage | | @qumra/jisr | App Bridge for iframe communication | | @qumra/manara | Design system & components | | @qumra/riwaq | UI Extensions SDK |

Requirements

  • Node.js: 18.x or higher
  • React: >=18.0.0
  • React DOM: >=18.0.0
  • React Router: ^7.0.0
  • @qumra/app-sdk: ^0.3.0

Configuration Types

Full TypeScript support with comprehensive type definitions:

interface QumraAppConfigArg {
  apiKey: string;
  accessTokenSecret: string;
  refreshTokenSecret: string;
  sessionStorage: SessionStorage;
  distribution?: AppDistribution;
  adminAccessToken?: string;
  hooks?: HooksConfig;
}

interface HooksConfig {
  afterAuth?: (context: AfterAuthOptions) => Promise<void>;
}

interface AfterAuthOptions {
  admin: AdminContext;
  request: Request;
  response: Response;
}

interface AdminContext {
  userId: string;
  shopId: string;
  appInstallationId: string;
  graphql(query: string, options?: GraphQLOptions): Promise<any>;
}

Support

License

ISC © Qumra