Skip to content

Documentation · ARDOR

Providers ​

ARDOR coordinates data, authentication, and internationalization through providers configured in @configuration() classes. ArdorApplication resolves them from the container and supplies them to react-admin:

ts
import {
  type BaseArdorApplication as TArdorApplication,
  configuration,
  CoreBindings,
  DefaultAuthProvider,
  DefaultAuthService,
  DefaultI18nProvider,
  DefaultRestDataProvider,
  englishMessages,
  inject,
  provide,
} from '@venizia/ardor';

// The message keys this application adds, made known to `useTranslate`.
declare module '@venizia/ardor' {
  interface IUseTranslateKeysOverrides {
    'quickstart.title': unknown;
    'quickstart.expensive': unknown;
  }
}

// Every framework binding, declared. start() binds each @provide method under its key, SINGLETON,
// built on first use: react-admin, the auth provider and every hook share one data provider - its
// token, headers and single 401 refresh.
@configuration()
export class AdminConfiguration {
  constructor(
    @inject({ key: CoreBindings.APPLICATION_INSTANCE })
    private readonly application: TArdorApplication,
  ) {}

  @provide({ key: CoreBindings.REST_DATA_PROVIDER_OPTIONS })
  restDataProviderOptions() {
    return { url: '/api', noAuthPaths: ['/auth/sign-in'] };
  }

  @provide({ key: CoreBindings.AUTH_PROVIDER_OPTIONS })
  authProviderOptions() {
    // The sign-in path and body default to IGNIS's auth controller: POST /auth/sign-in.
    return { paths: { afterLogin: '/products' } };
  }

  @provide({ key: CoreBindings.I18N_PROVIDER_OPTIONS })
  i18nProviderOptions() {
    return {
      i18nSources: {
        en: {
          ...englishMessages,
          quickstart: { title: 'Products', expensive: 'Expensive products (>= 5,000)' },
        },
      },
      languages: [{ locale: 'en', name: 'English' }],
    };
  }

  @provide({ key: CoreBindings.DEFAULT_REST_DATA_PROVIDER })
  restDataProvider() {
    return this.application.instantiate(DefaultRestDataProvider).value(this.application);
  }

  @provide({ key: CoreBindings.DEFAULT_AUTH_SERVICE })
  authService() {
    return this.application.instantiate(DefaultAuthService);
  }

  @provide({ key: CoreBindings.DEFAULT_AUTH_PROVIDER })
  authProvider() {
    return this.application.instantiate(DefaultAuthProvider).value(this.application);
  }

  @provide({ key: CoreBindings.DEFAULT_I18N_PROVIDER })
  i18nProvider() {
    return this.application.instantiate(DefaultI18nProvider).value(this.application);
  }
}

Framework Providers ​

PieceWhat it doesDefault classBound keyOptions keyRequired or optionalReference
Data providerMaps react-admin data calls onto REST requests and provides custom HTTP sending methodsDefaultRestDataProviderCoreBindings.DEFAULT_REST_DATA_PROVIDERCoreBindings.REST_DATA_PROVIDER_OPTIONSRequiredREST data provider
Auth providerManages authentication sessions, credentials, token storage, and auth error redirectionDefaultAuthProviderCoreBindings.DEFAULT_AUTH_PROVIDERCoreBindings.AUTH_PROVIDER_OPTIONSOptionalAuth provider
i18n providerResolves translations, handles locale changes, and composes feature message bundlesDefaultI18nProviderCoreBindings.DEFAULT_I18N_PROVIDERCoreBindings.I18N_PROVIDER_OPTIONSRequiredInternationalization
Network service (transport)Executes HTTP requests via fetch, attaches headers and tracing ids, and recovers from 401 errorsDefaultNetworkRequestServiceNone (constructed by data provider)CoreBindings.REST_DATA_PROVIDER_OPTIONSRequiredNetwork
Permission providerEvaluates authorization checks for resources, actions, and records via canAccessNone (IPermissionProvider)CoreBindings.PERMISSION_PROVIDERNoneOptionalPermission provider
FeaturesBundles resources, custom routes, translation messages, and configuration classes for modular domainsNone (array of TFeature)CoreBindings.FEATURESNoneOptionalFeatures

Provider Flow into react-admin ​

Provider flow into react-adminThree configuration bindings each inject one provider into the container: the REST data provider, the auth provider (optional) and the i18n provider. The container hands each to CoreAdmin inside ArdorApplication as dataProvider, authProvider and i18nProvider.Configuration (@provide)IoC Container (Singletons)ArdorApplicationREST_DATA_PROVIDER_OPTIONSAUTH_PROVIDER_OPTIONSI18N_PROVIDER_OPTIONSDEFAULT_REST_DATA_PROVIDERDEFAULT_AUTH_PROVIDERDEFAULT_I18N_PROVIDERCoreAdminIDataProviderIAuthProvider (optional)I18nProvider(react-admin)injectsinjectsinjectsdataProviderauthProvideri18nProvider

ArdorApplication reads providers by key from the container when building props for react-admin; see Application. Components resolve them with useProvider or useInjectable (see Dependency Injection). When passed a { target } class, useProvider checks that the class is registered under the providers namespace (BindingNamespaces.PROVIDER), throwing only when the class is bound under a different namespace.

Replace One ​

To customize or replace a default provider, create a subclass that extends the default class, override the specific method, and bind the subclass under the corresponding CoreBindings key in a @configuration() class.

In DefaultRestDataProvider (packages/admin/src/providers/rest-data.ts), the helper and transport methods are public or protected methods of the class: request and requestList (protected), buildListFilter (protected, a delegate of ListFilterBuilder.build), toAffectedIds (protected), createNetworkService (protected), getDataSourceAuth (public), and getNetworkService (public).

The following complete file illustrates subclassing DefaultRestDataProvider to override getOne and append a derived field to the returned record, registering the replacement provider under CoreBindings.DEFAULT_REST_DATA_PROVIDER:

ts
import { type BaseArdorApplication, configuration, CoreBindings, DefaultRestDataProvider, inject, provide } from '@venizia/ardor';
import { type GetOneParams, type GetOneResult, type RaRecord } from 'ra-core';

export class CustomRestDataProvider extends DefaultRestDataProvider {
  override async getOne<RecordType extends RaRecord = RaRecord>(opts: {
    resource: string;
    params: GetOneParams<RecordType>;
  }): Promise<GetOneResult<RecordType>> {
    const result = await super.getOne(opts);
    return { ...result, data: { ...result.data, isLoaded: true } };
  }
}

@configuration()
export class CustomDataConfiguration {
  constructor(@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private readonly application: BaseArdorApplication) {}

  @provide({ key: CoreBindings.DEFAULT_REST_DATA_PROVIDER })
  restDataProvider() {
    return this.application.instantiate(CustomRestDataProvider).value(this.application);
  }
}

Calling this.application.instantiate(CustomRestDataProvider).value(this.application) resolves constructor dependencies (REST_DATA_PROVIDER_OPTIONS and APPLICATION_INFO) and returns the react-admin provider.

Leave One Out ​

Some administrative applications do not require authentication, authorization, or feature packages.

Authentication (No Sign-In) ​

An application with no sign-in leaves DEFAULT_AUTH_PROVIDER unbound; ArdorApplication then runs without an auth provider:

  1. Leave DEFAULT_AUTH_PROVIDER unbound: Do not declare @provide({ key: CoreBindings.DEFAULT_AUTH_PROVIDER }) or @provide({ key: CoreBindings.DEFAULT_AUTH_SERVICE }).
  2. ArdorApplication handles omission: When resolving CoreBindings.DEFAULT_AUTH_PROVIDER, ArdorApplication uses isOptional: true. When unbound, authProvider is omitted from the props passed to CoreAdmin.
  3. React-admin runs without auth: No sign-in screen is rendered, and all resource routes and custom routes are immediately accessible.
  4. Configure network options: In REST_DATA_PROVIDER_OPTIONS, set noAuthPathRegex: /./ so that DefaultNetworkRequestService sends every HTTP request without attempting to attach an Authorization header.

Permission Provider ​

DefaultAuthProvider injects CoreBindings.PERMISSION_PROVIDER with isOptional: true. In DefaultAuthProvider.value(), if permissionProvider is unbound, authProvider.canAccess is omitted, and react-admin allows every authorization check (useCanAccess resolves canAccess: true).

Features ​

BaseArdorApplication.features() returns an empty array ([]) by default. When no features are mounted, ArdorApplication mounts only the resources and custom routes provided directly via application props (<ArdorApplication resources={...} routes={...} />).

Next Steps ​