repo_description: The flexible backend for all your projects 🐰 Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth & more.
repo_topics: api app cms composable data-visualization database directus graphql headless-cms javascript mariadb mssql mysql no-code node postgresql sql sqlite typescript vue
code: import type { AuthenticationClient, DirectusClient, RequestOptions, RestClient, RestCommand } from '@directus/sdk';
  import { authentication, createDirectus, rest } from '@directus/sdk';
  import { type FetchContext, ofetch } from 'ofetch';
  import { requestQueue } from './api';
  import { SDK_AUTH_REFRESH_BEFORE_EXPIRES } from './constants';
  import { useRequestsStore } from './stores/requests';
  import { getPublicURL } from '@/utils/get-root-path';
  
  export type SdkClient = DirectusClient<unknown> & AuthenticationClient<unknown> & RestClient<unknown>;
  
  type OptionsWithId = FetchContext['options'] & { id: string };
  
  const baseClient = ofetch.create({
  	retry: 0,
  	ignoreResponseError: true,
  	async onRequest({ request, options }) {
  		const requestsStore = useRequestsStore();
  		const id = requestsStore.startRequest();
  		(options as OptionsWithId).id = id;
  		const path = getUrlPath(request);
  
  		return new Promise((resolve) => {
  			if (path && path === '/auth/refresh') {
  				requestQueue.pause();
  				return resolve();
  			}
  
  			requestQueue.add(() => resolve());
  		});
  	},
  	async onResponse({ options }) {
  		const requestsStore = useRequestsStore();
  		const id = (options as OptionsWithId).id;
  		if (id) requestsStore.endRequest(id);
  	},
  	async onResponseError({ options }) {
  		const requestsStore = useRequestsStore();
  
  		// Note: Cancelled requests don't respond with the config
  		const id = (options as OptionsWithId).id;
  		if (id) requestsStore.endRequest(id);
  	},
  	async onRequestError({ options }) {
  		const requestsStore = useRequestsStore();
  
  		// Note: Cancelled requests don't respond with the config
  		const id = (options as OptionsWithId).id;
  		if (id) requestsStore.endRequest(id);
  	},
  });
  
  export const sdk: SdkClient = createDirectus(getPublicURL(), { globals: { fetch: baseClient.native } })
  	.with(authentication('session', { credentials: 'include', msRefreshBeforeExpires: SDK_AUTH_REFRESH_BEFORE_EXPIRES }))
  	.with(rest({ credentials: 'include' }));
  
  export default sdk;
  
  function getUrlPath(request: FetchContext['request']): string | null {
  	const uri = typeof request === 'string' ? request : request.url;
  
  	try {
  		return new URL(uri).pathname;
  	} catch {
  		return null;
  	}
  }
  
  /**
   * Creates a RestCommand for the SDK
   *
   * This helper function allows sending a RestCommand when the endpoint path is dynamic
   *
   * @example
   * Basic usage with sdk.request
   * ```typescript
   * import sdk, { requestEndpoint } from '@/sdk';
   * import { Item } from '@directus/types';
   * import { getEndpoint } from '@directus/utils';
   *
   * const posts = await sdk.request(
   * requestEndpoint(getEndpoint(collection), {
   * method: 'GET',
   * params: { fields: ['id', 'title', 'content'] },
   * });
   * ```
   */
  export function requestEndpoint<Output = unknown>(
  	path: string,
  	options: Omit<RequestOptions, 'body' | 'path'> & { body?: Record<string, unknown> },
  ): RestCommand<Output, never> {
  	let body: undefined | string;
  
  	if (options.body) {
  		body = JSON.stringify(options.body);
  	}
  
  	return () => ({
  		...options,
  		path,
  		body,
  	});
  }
  