Create NestJS libraries to interact with Ory API
Tags: tutorial, javascript, nestjs, ory
Published: 2024-05-11 07:41:32

---
title: Create NestJS libraries to interact with Ory API
published: true
description: How to use create NestJS libraries for Ory SDK
tags: tutorial, javascript, nestjs, ory
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofv8gj23wj76krvyq2jb.png
# published_at: 2024-04-15 09:04 +0000
series: Integrating Ory in Production with NestJS
---

[Ory] offers excellent documentation but needs more support tools and in-depth examples of using its libraries in TypeScript and [NestJS] projects. I decided to contribute to it by creating a set of libraries to interact with APIs, which will (hopefully) make integration into your NestJS project easier. This post presents the ideal use case to divulge my routines for creating libraries in NestJS/Nx!

Feel free to skip the journey and go straight to the [code](https://github.com/getlarge/nestjs-ory-integration)!

> **Note**
> This article is part of a series on integrating Ory in production with NestJS. If you are interested in the other articles, you can find them [here](./README.md).

## The plan

I will follow the recipe from a previous [blog post](<(https://push-based.io/article/creating-nestjs-libraries-with-nx)>) to create the libraries.

This workspace is composed of three public libraries (at least for the moment) and one private library:

- **kratos-client-wrapper** is a set of NestJS modules that wraps [@ory/client] and, more particularly, the Frontend and Identity APIs, which are part of [Ory Kratos]
- **keto-client-wrapper** is also a set of NestJS modules that wraps [@ory/client]'s Permission and Relationship APIs, which are part of [Ory Keto]
- **base-client-wrapper** is an internal NestJS module that provides a base class that the Ory client wrappers can extend (similar to the `BaseApi` class in @ory/client)
- **keto-relations-parser** is a node library that allows manipulating relations tuples using [Ory Permission Language] notation. This library is an improved version of this [existing lib](https://github.com/nidomiro/relation-tuple-parser/).

> **Edit**
> Recently, I created three extra packages, `keto-cli` and `kratos-cli`, to interact with the Ory APIs from the command line and `hydra-client-wrapper`. They are not covered in this article.

——

Now, let’s focus on the Ory API integration. Ory already provides an auto-generated client based on their Open API specifications, which uses [axios] under the hood to send HTTP requests. How can we make this an even better experience for NestJS users? I would say:

- Easily importable and configurable module and services
- Mockable dependencies for testing
- Module and services with clear boundaries and simple interfaces
- Error handling that is consistent across all modules and services
- A way to automatically retry requests in case of rate-limiting
- NestJS Guards to check user sessions and permissions on endpoints

## Module organization

Ory services usually have a public API and an admin API.

Ory Kratos' public API allows users to register accounts, log in, and check user sessions. On the other hand, the admin API enables the management of users (called **identities** in Ory), authentication methods, and sessions. Ory Keto's public API allows end users to check permissions while the admin API manages relationships between entities (called **namespaces** in Ory).

A logical path to split our modules is to follow this organization; it would result in the following :

| Ory service | Library               | Public API           | Admin API              |
| ----------- | --------------------- | -------------------- | ---------------------- |
| Ory Kratos  | kratos-client-wrapper | OryFrontendModule    | OryIdentitiesModule    |
| Ory Keto    | keto-client-wrapper   | OryPermissionsModule | OryRelationshipsModule |

As said previously, I wanted to easily mock dependencies for testing; moreover, to improve error response handling for calls to Ory APIs, providing a custom axios instance can be helpful.

Luckily, the **BaseApi** class from `@ory/client`, which all APIs extend, allows you to pass a custom axios instance in the constructor.
One solution I thought of is injecting the `HttpService` from `@nestjs/axios` into the NestJS services and passing the `HttpService.axiosRef` to the **BaseApi** constructor. This Axios instance could have custom interceptors to wrap errors in a standard class and allow adding a retry strategy in case of rate-limiting.
Since all services implement that logic, I might as well build a generic class that all others extend; sometimes, strong coupling is for a good cause.

At the end of this article, the dependency graph will look like this:
![nx graph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eyldemdw1kfnwh3lcdqk.png 'Nx Graph output')

> **Note**
> This graph was generated using [Nx Graph](https://nx.dev/core-features/explore-graph) CLI.

## base-client-wrapper implementation

The starting point is the generic module, `OryBaseModule`, with its `OryBaseService` provider, and the first step will be to install dependencies:

```sh
npm i @ory/client @nestjs/axios
```

### Create interfaces

To configure and implement the retry logic, I needed to extend the `AxiosRequestConfig` interface, which `axios` does not support.
I created an `OryAxiosRequestConfig` interface that extends it and an `OryBaseModuleOptions` class that implements the `IOryBaseModuleOptions` interface. The latter is used to configure the `OryBaseModule` and is provided by the library consumer, in this case, the `kratos-client-wrapper` and `keto-client-wrapper` modules.

```ts
// from https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/base-client-wrapper/src/lib/ory-base.interfaces.ts
import type { AxiosError, AxiosRequestConfig } from 'axios';

export interface AxiosExtraRequestConfig {
  retries?: number;
  retryCondition?: (error: AxiosError) => boolean;
  retryDelay?: (error: AxiosError, retryCount: number) => number;
}

export type OryAxiosRequestConfig = AxiosRequestConfig &
  AxiosExtraRequestConfig;

export interface IOryBaseModuleOptions {
  axiosConfig?: OryAxiosRequestConfig;
}
//...
```

To make the `axios` types aware of the new options, I extended the `AxiosRequestConfig` interface using TypeScript's declaration merging feature.

```ts
// from https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/base-client-wrapper/src/lib/ory-base.module.ts
//...
declare module 'axios' {
  interface AxiosRequestConfig extends AxiosExtraRequestConfig {}
}
//...
```

### OryError

To improve error handling on the consumer side. I created an `OryError` class that extends the JS `Error` to wrap `AxiosError` and make them more readable.
It allows consumers to check if an error is an `OryError` and to access the `AxiosError` instance.

```ts
import type { AxiosError } from 'axios';

export function isAxiosError(error: unknown): error is AxiosError {
  return (
    (typeof error === 'object' &&
      !!error &&
      (error as AxiosError).isAxiosError) ??
    false
  );
}

export class OryError
  extends Error
  implements Pick<AxiosError, 'config' | 'toJSON' | 'isAxiosError'>
{
  constructor(
    readonly error: unknown,
    readonly defaultStatus = 500,
    readonly defaultReason = 'Error connecting to the Ory API'
  ) {
    super(OryError.parseError(error));
    Object.setPrototypeOf(this, OryError.prototype);
  }

  static parseError(error: unknown): string {
    if (isAxiosError(error)) {
      return error.cause?.message ?? error.message;
    }
    if (isOryError(error)) {
      return error.errorMessage;
    }
    if (error instanceof Error) {
      return error.message;
    }
    return 'Unknown error';
  }

  get isAxiosError(): boolean {
    return isAxiosError(this.error);
  }

  toJSON(): {
    message: string;
    statusCode: number;
    details: Record<string, unknown>;
  } {
    return {
      message: this.errorMessage,
      statusCode: this.statusCode,
      details: this.getDetails(),
    };
  }

  get config(): AxiosError['config'] {
    if (isAxiosError(this.error)) {
      return this.error.config;
    }
    if (isOryError(this.error)) {
      return this.error.config;
    }
    return undefined;
  }

  get errorMessage(): string {
    return OryError.parseError(this.error) ?? this.defaultReason;
  }

  get statusCode(): number {
    if (isAxiosError(this.error)) {
      return this.error.response?.status ?? this.defaultStatus;
    }
    if (isOryError(this.error)) {
      return this.error.statusCode;
    }
    return this.defaultStatus;
  }

  getDetails(): Record<string, unknown> {
    if (isAxiosError(this.error)) {
      return (this.error.response?.data ?? {}) as Record<string, unknown>;
    }
    if (isOryError(this.error)) {
      return this.error.getDetails();
    }
    return {};
  }

  serializeErrors(): { message: string; field?: string }[] {
    return [{ message: this.errorMessage }];
  }
}

export function isOryError(error: unknown): error is OryError {
  return error instanceof OryError;
}
```

### OryBaseService

`OryBaseService` is the base class for all services interacting with `Ory` APIs.
It requires injecting the `HttpService`, configuring the `axios` interceptor during module initialization, and providing the `axios` reference via a getter.

```ts
@Injectable()
export class OryBaseService implements OnModuleInit {
  readonly logger = new Logger(OryBaseService.name);

  constructor(@Inject(HttpService) private readonly httpService: HttpService) {}

  get axios() {
    return this.httpService.axiosRef;
  }

  onModuleInit(): void {
    this.axios.interceptors.response.use(
      (response) => response,
      async (error) => {
        if ((isAxiosError(error) || isOryError(error)) && error.config) {
          const { config } = error;
          const shouldRetry =
            typeof config.retryCondition === 'function'
              ? config.retryCondition(error)
              : false;
          if (config?.retries && shouldRetry) {
            const retryDelay =
              typeof config.retryDelay === 'function'
                ? config?.retryDelay(error, config.retries)
                : 250;
            config.retries -= 1;
            this.logger.debug(
              `Retrying request to ${config.url} in ${retryDelay}ms`
            );
            await new Promise((resolve) => setTimeout(resolve, retryDelay));
            return this.httpService.axiosRef(config);
          }
          const oryError = new OryError(error);
          return Promise.reject(oryError);
        }
        const oryError = new OryError(error);
        return Promise.reject(oryError);
      }
    );
  }
}
```

### OryBaseModule

The [`OryBaseModule`](https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/base-client-wrapper/src/lib/ory-base.module.ts) is a basic `DynamicModule` that follows the convention of using `forRoot` and `forRootAsync` static methods to configure the module.
It ensures that the `HttpService` provider is available for the `OryBaseService` in two ways:

- With the `forRoot` method, the options are immediately available, so I imported the `HttpModule` and provided the `OryBaseService` with the `HttpService` instance
- With the `forRootAsync` method, the options are provided asynchronously, so instead of importing the `HttpModule`, I created a custom `HttpService` provider that receives an Axios instance to its constructor, configured with the `OryBaseModuleOptions`.

> **Tip**
> Have a look at the [NestJS documentation](https://docs.nestjs.com/fundamentals/dynamic-modules) to know more about dynamic modules.

## kratos-client-wrapper implementation

For this package, I created two modules (see [Module organization](#module-organization)), both of which consume the `OryBaseModule`.

### Create OryIdentities and OryFrontend interfaces

Interfaces for these modules (`OryIdentitiesModuleOptions` and `OryFrontendModuleOptions`) extend `OryBaseInterface` and enable configuring the base path and the access token (for the Admin API via Ory Network) to access the Ory APIs.

### Create OryIdentitiesModule and OryFrontendModule

There is nothing new here. The setup is very similar to the `OryBaseModule` setup. The modules import the `OryBaseModule` and provide the `OryIdentitiesService` and `OryFrontendService`.

### Create OryIdentitiesService and OryFrontendService

Both services will depend on `OryBaseService` and their corresponding module options; they will extend `IdentityApi` and `FrontendApi` from **@ory/client**.

### Create an authentication guard

To make it easier to protect endpoints with Ory Kratos, I created a Guard that depends on `OryFrontendService` to fetch and validate the session from the cookie or session token. The Guard is a mixin allowing different configurations for each HTTP endpoint or microservice listener.

```ts
// from https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/kratos-client-wrapper/src/lib/ory-authentication.guard.ts
// ...
export const OryAuthenticationGuard = (
  options: Partial<OryAuthenticationGuardOptions> = defaultOptions
): Type<CanActivate> => {
  @Injectable()
  class AuthenticationGuard implements CanActivate {
    readonly logger = new Logger(AuthenticationGuard.name);

    constructor(readonly oryService: OryFrontendService) {}

    async canActivate(context: ExecutionContext): Promise<boolean> {
      const {
        cookieResolver,
        sessionTokenResolver,
        isValidSession,
        postValidationHook,
      } = {
        ...defaultOptions,
        ...options,
      };

      try {
        const cookie = cookieResolver(context);
        const xSessionToken = sessionTokenResolver(context);
        const { data: session } = await this.oryService.toSession({
          cookie,
          xSessionToken,
        });
        if (!isValidSession(session)) {
          return false;
        }
        if (typeof postValidationHook === 'function') {
          await postValidationHook.bind(this)(context, session);
        }
        return true;
      } catch (error) {
        this.logger.error(error);
        return false;
      }
    }
  }
  return mixin(AuthenticationGuard);
};
```

### Add unit tests

I wanted to be sure that the Ory clients use the custom Axios instance with the options provided by the `OryBaseModule`, so I created a rather minimalist unit tests suite for both services, which mock the `OryBaseService` and, by extension, the Axios instance.

```ts
import { OryBaseService } from '@getlarge/base-client-wrapper';
import { Test, TestingModule } from '@nestjs/testing';

import { OryIdentitiesModuleOptions } from './ory-identities.interfaces';
import { OryIdentitiesService } from './ory-identities.service';

describe('OryIdentitiesService', () => {
  let oryIdentitiesService: OryIdentitiesService;
  let oryBaseService: OryBaseService;
  const accessToken = 'ory_st_test';
  const dummyResponse = { data: 'test' };

  beforeEach(async () => {
    // the Test module is used to create a NestJS context with the provider we want to test and its dependencies
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        OryIdentitiesService,
        {
          provide: OryIdentitiesModuleOptions,
          useValue: {
            basePath: 'http://localhost',
            accessToken,
          },
        },
        // OryBaseService is mocked to avoid making actual HTTP requests and to spy on the axios instance
        {
          provide: OryBaseService,
          useValue: {
            axios: {
              request: jest.fn().mockResolvedValue(dummyResponse),
            },
          },
        },
      ],
    }).compile();

    oryIdentitiesService =
      module.get<OryIdentitiesService>(OryIdentitiesService);
    oryBaseService = module.get<OryBaseService>(OryBaseService);
  });

  it('should be defined', () => {
    expect(oryIdentitiesService).toBeDefined();
  });

  describe('Should use custom axios instance', () => {
    it('should call getIdentity ednpoint', async () => {
      const id = 'test';

      await oryIdentitiesService.getIdentity({ id });
      // when calling oryIdentitiesService method, the injected OryBaseService axios instance should be called
      expect(oryBaseService.axios.request).toHaveBeenCalledWith({
        url: `http://localhost/admin/identities/${id}`,
        method: 'GET',
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });
    });

    //...
  });
});
```

## keto-relations-parser implementation

As said earlier, the original goal was to improve the original library that allows parsing relation tuples with the following changes:

1. Replace [Antlr4](https://github.com/tunnelvisionlabs/antlr4ts) based parser with a simpler and more efficient Regex parser (it still needs to be tested in some edge cases)
2. Create a [fluent API](https://en.wikipedia.org/wiki/Fluent_interface) to construct a relation tuple object
3. Provide a set of helpers to convert RelationTuple to [Ory Keto] API parameters

After a [quick discussion](https://github.com/nidomiro/relation-tuple-parser/pull/26) with the author of the original library, it seemed like the Antlr4 parser was a bit overkill for the use case and that a more straightforward Regex match could be enough. The first task was to write a Regex that matched the Ory Permission Language notation.
Here are the test cases I used to validate the Regex:

```js
const validCases = [
  'namespace:object#relation@subject',
  'namespace:object#relation@(subject)',
  'namespace:object#relation@subjectNamespace:subjectObject#subjectRelation',
  'namespace:object#relation@(subjectNamespace:subjectObject#subjectRelation)',
  'namespace:object#relation@(subjectNamespace:subjectObject)',
  'namespace:object#relation@(subjectNamespace:subjectObject#)',
  'namespace:object#relation@(subjectNamespace:subjectObject#subjectRelation)',
];

const invalidSyntaxtTestCases = [
  'object#relation',
  'object@subject',
  'object#relation@subject@sdf',
  'object#relation@subjectObject#relation@sdf',
  'object#relation@subjectObject#relation',
  'namespace:object#relation@subjectObject#relation',
  'object#relation@namespace:subjectObject#relation',
  'object#relation@subjectId',
  'namespace:object#@subjectId',
  'namespace:#relation@subjectId',
  ':object#relation@subjectId',
  'namespace:object#relation@',
  'namespace:object#relation@:subjectObject#relation',
  'namespace:object#relation@namespace:#relation',
  'namespace:object#relation@id:',
  'namespace::object#relation@id',
  'namespace:object##relation@id',
  'namespace:object#relation@@id',
];
```

And the Regex is brought to you by OpenAI:

```re
/^([^:]+)(?::([^#]+))?(?:#([^@]+)(?:@([^:]+)(?::([^#]+))?(?:#([^()]+(?:\([^()]+\))?)?)?)?)?$/
```

Please let me know if there is a better way to write this Regex!

---

Another discussion with some co-workers made me realize the importance of providing a fluent API to construct the relation tuple. Using the notation supplied by the [Ory Permission Language], it was hard to verbalize the relationships.
This results in the following API:

```ts
import { RelationTupleBuilder } from '@getlarge/keto-relations-parser';

const tuple = new RelationTupleBuilder()
  .subject('User', '321')
  .isIn('owners')
  .of('Project', '321');

tuple.toString(); // 'Project:321#owners@User:321'

tuple.toJSON(); // { namespace: 'Project', id: '321', relation: 'owners', subject: { namespace: 'User', id: '321' } }

tuple.toHumanReadableString(); // 'User:321 is in owners of Project:321'
```

Finally, I provided a set of helpers to convert the RelationTuple to [Ory Keto] API parameters; this API seems to be a bit inconsistent in the way it expects the parameters, so I tried hiding it by making the RelationTuple interface the single model to interact with.

You can find those helpers [here](https://github.com/getlarge/nestjs-ory-integration/tree/main/packages/keto-relations-parser/src/lib/keto-converters).

## keto-client-wrapper implementation

The approach is the same as for the `kratos-client-wrapper` implementation, except that the modules are named `OryPermissionsModule` and `OryRelationshipsModule`.

### Create an authorization guard

For this Guard case, things are more complicated than the `OryAuthenticationGuard` as multiple layers of abstraction are required to empower developers to define permissions on their endpoints.
Hopefully, the expressiveness of the [Ory Permission Language] combined with the fluent API from `keto-relations-parser` allows us to check permissions elegantly.

> **Note**
> Want to know more about this notation and how it works? Check out the [Zanzibar Academy](https://zanzibar.academy).

#### Create OryPermissionChecks decorator

The relation tuple(s) must be dynamically constructed with the API endpoint parameters and passed to `OryPermissionService` to check permissions on Ory Keto. Ideally, the Guard should be able to handle multiple relation tuples and multiple checks, so the Guard should receive an array of relation tuples factories.
Also, it would be ideal to define those factories declaratively, so I decided to use a custom decorator, `OryPermissionChecks`. The factories are resolved using [Reflection](https://docs.nestjs.com/fundamentals/execution-context#reflection-and-metadata) and the [Execution Context](https://docs.nestjs.com/fundamentals/execution-context#executioncontext-class).

```ts
import { ExecutionContext, SetMetadata } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

const ORY_PERMISSION_CHECKS_METADATA_KEY = Symbol('OryPermissionChecksKey');

export type RelationTupleFactory = string | ((ctx: ExecutionContext) => string);

export type RelationTupleCondition = {
  type: 'AND' | 'OR';
  conditions: (RelationTupleFactory | RelationTupleCondition)[];
};

export type EnhancedRelationTupleFactory =
  | RelationTupleFactory
  | RelationTupleCondition;

/**
 * @description Decorator to add permission checks to a handler, will be consumed by the `OryAuthorizationGuard` {@link OryAuthorizationGuard} using the `getOryPermissionChecks` {@link getOryPermissionChecks} function
 * @param relationTupleFactories
 * @returns
 */
export const OryPermissionChecks = (
  ...relationTupleFactories: EnhancedRelationTupleFactory[]
) => {
  return SetMetadata(
    ORY_PERMISSION_CHECKS_METADATA_KEY,
    relationTupleFactories
  );
};

export const getOryPermissionChecks = (
  reflector: Reflector,
  handler: Parameters<Reflector['get']>[1]
): EnhancedRelationTupleFactory[] | null => {
  const oryPermissions =
    reflector.get<
      EnhancedRelationTupleFactory[],
      typeof ORY_PERMISSION_CHECKS_METADATA_KEY
    >(ORY_PERMISSION_CHECKS_METADATA_KEY, handler) ?? [];
  return oryPermissions.length > 0 ? oryPermissions : null;
};
```

> **Note**
> The permissions to checks can be composed of multiple relation tuples and multiple conditions. The conditions can be nested and combined with `AND` or `OR` logical operators. This allows for a high level of expressiveness when defining permissions.

#### Create OryAuthorizationGuard

The Guard is a mixin that depends on:

- `OryPermissionChecks`, this decorator enables to store the relation tuple(s) that represents the permission(s) required to access the given endpoint
- `OryPermissionService`, check the permission(s) via Ory Keto HTTP API.

The most important part of the Guard is the `evaluateConditions` method, which resolves the relation tuple(s) and checks the permissions via the Ory Keto API. The method is recursive and can handle nested conditions.

```ts
// from https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/keto-client-wrapper/src/lib/ory-authorization.guard.ts
// ...
export const OryAuthorizationGuard = (
  options: Partial<OryAuthorizationGuardOptions> = {}
): Type<IAuthorizationGuard> => {
  @Injectable()
  class AuthorizationGuard implements CanActivate {
    constructor(
      readonly reflector: Reflector,
      readonly oryService: OryPermissionsService
    ) {}

    get options(): OryAuthorizationGuardOptions {
      return {
        ...defaultOptions,
        ...options,
      };
    }

    async evaluateConditions(
      factory: EnhancedRelationTupleFactory,
      context: ExecutionContext
    ): Promise<{
      allowed: boolean;
      relationTuple: string | string[];
    }> {
      if (typeof factory === 'string' || typeof factory === 'function') {
        const { unauthorizedFactory } = this.options;

        const relationTuple =
          typeof factory === 'string' ? factory : factory(context);
        const result = createPermissionCheckQuery(
          parseRelationTuple(relationTuple).unwrapOrThrow()
        );

        if (result.hasError()) {
          throw unauthorizedFactory(context, result.error);
        }

        try {
          const { data } = await this.oryService.checkPermission(result.value);
          return { allowed: data.allowed, relationTuple };
        } catch (error) {
          throw unauthorizedFactory(context, error);
        }
      }
      const evaluatedConditions = await Promise.all(
        factory.conditions.map((cond) => this.evaluateConditions(cond, context))
      );
      const results = evaluatedConditions.flatMap(({ allowed }) => allowed);
      const allowed =
        factory.type === 'AND' ? results.every(Boolean) : results.some(Boolean);

      return {
        allowed,
        relationTuple: evaluatedConditions.flatMap(
          ({ relationTuple }) => relationTuple
        ),
      };
    }

    async canActivate(context: ExecutionContext): Promise<boolean> {
      const factories =
        getOryPermissionChecks(this.reflector, context.getHandler()) ?? [];
      if (!factories?.length) {
        return true;
      }
      const { postCheck, unauthorizedFactory } = this.options;
      for (const factory of factories) {
        const { allowed, relationTuple } = await this.evaluateConditions(
          factory,
          context
        );

        if (postCheck) {
          postCheck(relationTuple, allowed);
        }
        if (!allowed) {
          throw unauthorizedFactory(
            context,
            new Error(`Unauthorized access for ${relationTuple}`)
          );
        }
      }
      return true;
    }
  }
  return mixin(AuthorizationGuard);
};
```

> **Note**
>
> The unit tests for the Guard [here](https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/keto-client-wrapper/src/lib/ory-authorization.guard.spec.ts), can give you a better understanding of how it works.

## End2end testing

To ensure that the libraries work well together and that the Guard is successfully activated to protect endpoints, I created the following setup:

- Custom Docker images for Kratos and Keto
- A mocked application that uses the libraries
- Some end-to-end tests using NestJS testing utilities and Jest

The setup refers to the `keto-client-wrapper`, but the same applies to the `kratos-client-wrapper`.

### Build custom docker images

I created a Dockerfile for each service. The Dockerfile for Keto is simple; it copies the configuration and the namespaces files, and sets the health check and the command to run the service.

```dockerfile
FROM oryd/keto:v0.11.1

COPY ./keto.yaml /home/ory/keto.yaml
COPY ./namespaces.ts /home/ory/namespaces.ts

HEALTHCHECK --interval=3s --timeout=3s --start-period=2s --retries=3 CMD [ 'wget -nv --spider -t1 http://localhost:4466/health/ready || exit 1' ]

CMD ["serve", "--config", "/home/ory/keto.yaml"]
```

> **Note**
> The health check will help us ensure the container is ready to accept requests before running tests

Then, we can add an Nx target to make publishing to GHCR easy:

```json
// ...
"docker-push": {
  "executor": "nx:run-commands",
  "options": {
    "command": "docker buildx build ./test --platform linux/amd64,linux/arm64 -t ghcr.io/getlarge/nestjs-ory-integration/keto --push",
    "cwd": "packages/keto-client-wrapper"
  }
}
```

Finally, the image is published with `nx run keto-client-wrapper:docker-push`.

### Mock an application

Here is a simple `Controller` that interacts with the library and provides an endpoint that returns a 200 if the user has the required permissions to access the resource or a 403 if not.

```ts
@Controller('Example')
export class ExampleController {
  @OryPermissionChecks((ctx) => {
    const req = ctx.switchToHttp().getRequest();
    const currentUserId = req.headers['x-current-user-id'] as string;
    const resourceId = req.params.id;
    return new RelationTupleBuilder()
      .subject('User', currentUserId)
      .isIn('owners')
      .of('Toy', resourceId)
      .toString();
  })
  @UseGuards(
    OryAuthorizationGuard({
      postCheck(relationTuple, isPermitted) {
        Logger.log('relationTuple', relationTuple);
        Logger.log('isPermitted', isPermitted);
      },
    })
  )
  @Get(':id')
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  getExample(@Param('id') id?: string) {
    return {
      message: 'OK',
    };
  }
}
```

### Write e2e tests

In this test, I create a relation (`owners`) between a subject `User` and a resource `Toy` in Ory Keto and check if the user can access the resource via the endpoint.
The Keto policies are defined in [namespaces.ts](https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/keto-client-wrapper/test/namespaces.ts).

```ts
// from https://github.com/getlarge/nestjs-ory-integration/blob/main/packages/keto-client-wrapper/test/keto-client-wrapper.spec.ts

describe('Keto client wrapper E2E', () => {
  let app: INestApplication;
  let oryPermissionService: OryPermissionsService;
  let oryRelationshipsService: OryRelationshipsService;
  const dockerComposeFile = resolve(join(__dirname, 'docker-compose.yaml'));

  const route = '/Example';

  const createOryRelation = async (object: string, subjectObject: string) => {
    const relationTuple = relationTupleBuilder()
      .subject('User', subjectObject)
      .isIn('owners')
      .of('Toy', object)
      .toJSON();
    await oryRelationshipsService.createRelationship({
      createRelationshipBody:
        createRelationQuery(relationTuple).unwrapOrThrow(),
    });
    const { data } = await oryPermissionService.checkPermission(
      createPermissionCheckQuery(relationTuple).unwrapOrThrow()
    );
    expect(data.allowed).toEqual(true);
  };

  beforeAll(() => {
    if (!process.env['CI']) {
      execSync(`docker-compose -f ${dockerComposeFile} up -d --wait`, {
        stdio: 'ignore',
      });
    }
  });

  afterAll(() => {
    if (!process.env['CI']) {
      execSync(`docker-compose -f ${dockerComposeFile} down`, {
        stdio: 'ignore',
      });
    }
  });

  beforeEach(async () => {
    // before each test, we create a NestJS context with the modules we want to test and their dependencies
    const module: TestingModule = await Test.createTestingModule({
      imports: [
        // we ensure that loading modules with both forRoot and forRootAsync methods works
        OryPermissionsModule.forRoot({
          basePath: 'http://localhost:44660',
        }),
        OryRelationshipsModule.forRootAsync({
          useFactory: () => ({
            basePath: 'http://localhost:44670',
            accessToken: '',
          }),
        }),
      ],
      providers: [ExampleService],
      controllers: [ExampleController],
    }).compile();

    oryPermissionService = module.get<OryPermissionsService>(
      OryPermissionsService
    );
    oryRelationshipsService = module.get<OryRelationshipsService>(
      OryRelationshipsService
    );

    app = module.createNestApplication();
    await app.init();
  });

  afterEach(() => {
    return app?.close();
  });

  it('should pass authorization when relation exists in Ory Keto', async () => {
    const object = 'car';
    const subjectObject = 'Bob';
    await createOryRelation(object, subjectObject);

    const { body } = await request(app.getHttpServer())
      .get(`${route}/${object}`)
      .set({
        'x-current-user-id': subjectObject,
      });
    expect(body).toEqual({ message: 'OK' });
  });

  it('should fail authorization when relation does not exist in Ory Keto', async () => {
    const object = 'car';
    const subjectObject = 'Alice';

    const { body } = await request(app.getHttpServer())
      .get(`${route}/${object}`)
      .set({
        'x-current-user-id': subjectObject,
      });
    expect(body).toEqual({
      error: 'Forbidden',
      message: 'Forbidden resource',
      statusCode: 403,
    });
  });
});
```

> **Note**
> Before starting the tests and when running locally, Ory services are started with `docker compose` with the `wait` option to ensure services are up and running and stopped after the tests are done. In CI, the services are already running, so the tests can be run directly.

### Conclusion

I hope this article will offer a better understanding of Ory APIs and concrete interactions with NestJS. The libraries are available on [GitHub](https://github.com/getlarge/nestjs-ory-integration).

In this series's next article, I will show you how to integrate the libraries into a NestJS application and configure Ory Kratos and Ory Keto in a production environment. Stay tuned! 🚀

[NestJS]: https://nestjs.com
[Ory]: https://www.ory.sh/
[Ory Keto]: https://github.com/ory/keto
[Ory Kratos]: https://github.com/ory/kratos
[Ory Permission Language]: https://www.ory.sh/docs/keto/reference/ory-permission-language
[axios]: https://github.com/axios/axios
[@ory/client]: https://www.npmjs.com/package/@ory/client