
Intermediate workshop
Nx for Scalable Architecture
Master Nx to enforce architecture, speed up your development workflow and improve code quality
Learn more
This article is the third part of a series that explores integrating Ory, within the NestJS framework. In the previous articles, we introduced Ory and its core components and discussed creating dedicated NestJS libraries to simplify Ory integration. In this post, we will build upon the concepts and libraries developed in the previous articles and dive into the practical aspects of integrating Ory in a NestJS application.
What is better than a concrete example to demonstrate the integration? Our demonstration app is a web-based platform (REST API) called CatFosterCatFoster
Note: The CatFoster application is a simplified example that demonstrates the integration of Ory in a NestJS application. The application will not cover frontend development and deployment, focusing solely on showcasing the backend implementation of the authentication and authorization features of Ory.
The journey to integrate Ory in the CatFoster
Design Phase: We will outline the architecture, user flows, entities, and permissions for the CatFoster
Implementation Phase: We will create a new Nx workspace, set up a NestJS application, configure Ory Kratos and Ory Keto using Docker Compose, and implement the necessary modules, services, controllers, and entities to integrate Ory into the CatFoster application.
Testing Phase: We will start with manual testing and then write end-to-end tests for the application by running the application locally. This phase will show how to set up an Ory environment for testing.
Note: If this is your first time working with Ory, I recommend reading the Introduction to Ory article to familiarize yourself with the core components and concepts of Ory.
User Authentication and Profile Management:
Ory Integration: Utilize Ory's authentication system to handle user registrations, logins, password resets, and profile management.
User Roles: There is one static user role, Admin
Cat Profiles:
Listing and Management: Cat owners can create profiles for their cats, including photos, descriptions, special care instructions, and availability for fostering. Admins can edit and delete cat profiles.
Search and Filters: Users looking to foster cats can search for them based on filters.
Fostering Matchmaking:
Requests and Approvals: Cat fosters can send fostering requests to cat owners, who can review and approve or deny them based on the foster's profiles.
Authorization Checks: Use Ory to manage authorization, ensuring that only cat owners can approve fostering requests and only registered users can send requests.

Self-service UI: This is the frontend where users can log in and manage their accounts. It communicates directly with Ory Kratos for authentication-related tasks.
Ory Kratos: Handles authentication. It's responsible for user login, account management, and session management. It interacts with the NestJS app via HTTP webhooks to replicate user data on signup.
HTTP Webhooks: Serve as the communication link between Ory Kratos and the NestJS app, ensuring the user is replicated in the local database upon signup.
NestJS App: The core of your application is handling business logic, CRUD operations with the Postgres database, authentication checks with Ory Kratos, and authorization with Ory Keto.
Ory Keto: Manages authorization, determining what authenticated users are allowed to do within the application.
Postgres: The database where user data (replicated from Ory on signup), cat profiles and fostering requests are stored. The NestJS app interacts with Postgres for all data storage and retrieval operations.
To visualize the user flow for the CatFoster

It all starts with the user creating a cat profile. Once the profile is created, the user (owner or member of the admin group) can update or delete it. The following sequence diagrams illustrate these flows.


This flow ensures that users can request to foster a cat only if they meet specific criteria, such as not being the cat's owner and not already fostering this cat. This mechanism helps prevent conflicts and ensures a smooth fostering process.

This flow ensures that only cat owners can approve fostering requests, maintaining control over who fosters their cats.

The diagram below represents the entities of CatProfileUserFostering
User: Represents users of the system, which can be cat owners, fosters, or both. Attributes include basic user information like idnameemailownedCatsCatProfilefosteringActivitiesFostering
CatProfile: Represents the profiles of cats available for fostering. Attributes include the cat's idnameagedescriptionownerIdUserphotosUrlsFostering
Fostering: Represents a fostering arrangement between a user (foster) and a cat. Attributes include idcatIdCatProfilefosterUserIdUserstartDateendDatestatus

In Ory Keto (the authorization component of Ory), developers can express relationships using the Ory Permission Language. You can compare them to DDD Aggregates, where relations between entities and permissions are defined based on the context of the user and the entity with which they interact.
Ory uses the following terminology:
Subjects: users or groups interacting with the system (e.g., User
Objects: entities in the system (e.g., CatProfileFostering
Relations: associations between objects (e.g., ownsparticipatesInincludedIn
Permissions: actions that users can perform on objects (e.g., editfosterapprove
To manage access control in the CatFoster
Note: The permissions will be implemented using Ory Permission Language Code in the following steps.

Note The relations are always defined as arrays to allow multiple users or groups to be associated with a specific entity. The permissions are defined as functions that receive a context object and return a boolean value based on the user's authorization level.
In the implementation phase, we will create a new Nx workspace containing our NestJS application, configure Ory Kratos and Ory Keto using Docker Compose, and set up the necessary modules, services, controllers, and entities to integrate Ory into the CatFoster application.
Create a new Nx workspace using the following command:
Install the NestJS plugin for Nx:
Add a new NestJS application to the workspace:
Install the required libraries:

Intermediate workshop
Master Nx to enforce architecture, speed up your development workflow and improve code quality
Learn moreWe will use Docker Compose to set up Ory Kratos and Ory Keto services. This setup will allow us to run these services locally and interact with them from our NestJS application.
To make the Ory services configuration process more straightforward and reusable for all environments (self-hosted and cloud), we will create separate (template) configuration files for Ory Kratos and Ory Keto that can be extended with environment-specific values.
As a convention, we will:
create an infra
prefix environment variables with the service name (e.g., kratos_keto_
Note: Ory Kratos and Ory Keto supports directly using environment variables for configuration. However, using configuration files can be more convenient for managing multiple environments and sharing configurations across different services and more importantly, to configure the Ory Network (the cloud offering of Ory) tenant.
Our Ory Kratos configuration will require four different files:
kratos-template.yaml:
In this file, we use the notation @@##
The configuration file template includes settings for Ory Kratos's self-service UI, authentication methods, error handling, and session management. These settings can be adjusted based on your application's requirements and security policies.
In the configuration file:
In dsn
In selfservice.flows.registration.after
In identity.schemas
Tips:
For a complete list of configuration options and their descriptions, refer to the Configuration Reference.
The easiest way to create a configuration file for Ory Kratos is to use the Configuration Editor tool. This tool provides a user-friendly interface to generate a configuration file based on your application's requirements and security policies.
identity.schema.json:
In this file, we set the JSON schema to represent the user identity, including the required properties, formats, and additional settings for authentication, verification, and recovery. Ory Kratos will use this schema to validate user data and enforce security policies.
Tips: You can learn more about the Ory Kratos identity schema and how to customize it for your application in the Ory Kratos documentation.
after-webhook.jsonnet:
This Jsonnet file defines the payload to be sent to the webhook server after a successful registration. In this example, we check if the user's email starts with "test-" and cancel the registration process. You can customize this file to include additional data or perform specific actions based on your application's requirements.
Tips: You can customize this file to include additional data or perform specific actions based on your application's requirements, see docs.
Ory Keto's configuration is less complex than Ory Kratos, as it focuses on defining relationships between entities and permissions based on the Ory Permission Language. We will create three files for Ory Keto:
keto-template.yaml:
The namespace.locationnamespaces.ts
Tips:
For a complete list of configuration options and their descriptions, refer to the Configuration Reference.
The easiest way to create a configuration file for Ory Keto is to use the Configuration Editor tool.
namespaces.ts:
This file defines the namespaces, relations, and permissions for Ory Keto. It includes the relationships between entities (e.g., UserCatProfileFosteringeditfosterapprove
The built-in types from the @ory/permission-namespace-types
To substitute the placeholders in the configuration files with actual values (from the .env
This script makes the workflow (much) more efficient and less error-prone than manually updating the configuration files. You should check it out in the tools/ory/generate-config.ts.
We can consume it with the package.json
Note: As a convention, I created a
directory at the root of the workspace to store the helper scripts and utilities. tools
docker-compose.yaml:
Our Docker Compose configuration will include services for Ory Kratos, Kratos self-service UI, Ory Keto, and a PostgreSQL database to store user and cat profile data. We will also include a service for MailSlurper, a fake SMTP server that we will use to test email notifications.
Note:
The
and kratos-migrateservices run the database migrations for Ory Kratos and Ory Keto, respectively, before starting the main services. keto-migrateThe
service runs the self-service UI for Ory Kratos, allowing users to interact with the authentication flows with a user-friendly interface. kratos-selfservice-ui-node
.env:
Create a .env.env.example
Note: The
and keto_dsnvalues are the connection strings for the PostgreSQL databases used by Ory Keto and Ory Kratos, respectively. These values should match the database services' configurations in the kratos_dsnfile. They are set to docker-compose.yamlby default, meaning the data will be stored in memory and lost when the service is restarted. memory
To test the configuration, run the following commands:
In the next steps, we will create the necessary modules, services, controllers, and entities in the NestJS application to integrate Ory Kratos and Ory Keto into the CatFoster application.
Each domain (User, CatProfile, Fostering) will have a scoped directory. Under the libsusercatfostering
However, entities will be shared in a single directory - entitieslibs

Intermediate workshop
Mastering NestJS: From Basics to Advanced Application Design
Learn moreEach domain will have an entity class using TypeORM decorators to define the database schema. Entities' primary and foreign keys will be UUIDs, and the relationships will be declared using the @ManyToOne@OneToMany@ManyToManysubjectsobjects
The entities library with Nx generators:
You can refer to the entity classes in the following files:
The @nestjs/config module will load environment variables and configuration files into the NestJS application. The configuration module will be responsible for loading the Ory Kratos and Ory Keto configuration files and making them available to the application.
To validate the configuration, we will create an environment-variables.ts
Note:
When using the cloud hosted Ory Network,
, ORY_KETO_ADMIN_URL, ORY_KETO_PUBLIC_URL, and ORY_KRATOS_PUBLIC_URLshould be configured with the Ory Network tenant URL. ORY_KRATOS_ADMIN_URLand ORY_KETO_API_KEYshould be set to the API key generated on the Ory Network tenant. ORY_KRATOS_API_KEY
is a custom API key used to authenticate requests from Ory Kratos webhooks. It should be the same as the one configured in the Ory Kratos configuration file via ORY_ACTION_API_KEYenvironment variable. selfservice_flows_registration_after_hook_config_auth_config_valueThe
function is consumed by the validateEnvironmentVariables, it uses the ConfigModulelibrary to validate the environment variables based on the class-validatorclass. If any validation errors occur, the function throws an error with the details of the validation errors. EnvironmentVariables
The UsersModule
The UsersController
To protect the routes, we will create the OryActionGuardOryAuthenticationGuard
Note:
The
and onSignUpmethods handle the requests from the Ory Kratos webhooks for user registration and sign-in. The onSignInmethod retrieves the current user information from the Ory Kratos session. getCurrentUserThe
authenticates requests from Ory Kratos webhooks using the OryActionGuardenvironment variable. The ORY_ACTION_API_KEYauthenticates users using the Kratos session passed in the request headers (cookie or authorization token) and sets the user information in the request object. OryAuthenticationGuardThe paths for the
and onSignUpmethods should match the paths defined in the Ory Kratos configuration file for the respective webhooks ( onSignInand selfservice_flows_login_after_hook_config_url) selfservice_flows_registration_after_hook_config_urlCheck the previous article for more details on the OryAuthenticationGuard
The UsersService
After signing up, create an internal user and bind it to the new identity under the metadata_public
Before signing in, check if the identity has a verified email address unless the identity schema does not require email verification.
Note:
In the
method modifies the identity before its storage in the Ory Kratos DB, see documentation here. Since the identity is not created yet, the identity id is set to onSignUp. 00000000-0000-0000-0000-000000000000In the
method, if the user's email address is not verified, an error is thrown to prevent login, see documentation. This logic is similar to the original onSignInhook in Ory Kratos. Unless the identity schema does not require email verification, the user can log in without a verified email address. We will use this logic to skip the email verification step for our end-to-end tests. The require_verified_addressclass is a custom error class that extends the OryWebhookErrorclass from NestJS. It formats the error response in the format expected by Ory Kratos webhooks allowing the error message to be displayed in the Self-Service UI. HttpException
The CatProfilesModule
The CatProfilesControllerOryAuthenticationGuardOryAuthorizationGuard
Note:
The
uses the CatProfilesControllerdecorator to define permissions that needs to be evaluated in OryPermissionChecks. OryAuthorizationGuardFor more details on the
check the previous article and the source code in the GitHub repository. OryAuthorizationGuard
The functions isOwnerPermissionisAdminPermissionExecutionContext
Note: The tuple will look like
for the CatProfile:<catProfileId>#owners@User:<currentUserId>function and isOwnerPermissionfor the Group:admin#members@User:<currentUserId>function. isAdminPermission
The CatProfilesServiceCatProfileSchema Repository
Note:
The
uses the CatProfilesServiceto create relationships between users and cat profiles in Ory Keto. The OryRelationshipsServiceand createAdminRelationshipmethods create relationships between the cat profile and the admin group and the user, respectively. The createOwnerRelationshipand deleteAdminRelationshipmethods delete the relationships when the cat profile is deleted. deleteOwnerRelationshipTo understand the relationship queries, refer to the Ory Keto example and the
documentation. @getlarge/keto-relations-parser
The FosteringModule
The FosteringControllerCatProfilesControllerOryAuthenticationGuardOryAuthorizationGuard
Note: The
, canRequestFosteringPermission, canReadFosteringPermissionand canApproveFosteringPermissionfactories will return the following relationship tuples: canRejectFosteringPermission
for the CatProfile <catProfileId>#foster@User:<userId>function canRequestFosteringPermission
for the Fostering: <fosteringId>#read@User:<userId>function canReadFosteringPermission
for the Fostering <fosteringId>#approve@User:<userId>function canApproveFosteringPermission
for the Fostering:<fosteringId>#reject@User:<userId>function canRejectFosteringPermission
The FosteringServiceFosteringSchema Repository
The user who requested the fostering can edit the request
The cat profile owner can approve or reject the fostering request
Both can read the fostering request
Note: The
and catProfileRelationQueryfactories will return the following relationship tuples: participantRelationQuery
for the Fostering:<fosteringId>#participants@User:<userId>function participantRelationQuery
for the Fostering:<fosteringId>#catProfiles@CatProfile:<catProfileId>function catProfileRelationQuery
The AppModuleUsersModuleCatProfilesModuleFosteringModule
ConfigModule
LoggerModule
TypeOrmModule
Note:
When running the application in test mode, the
will load the ConfigModulefile; this will allow us to use a different database for end-to-end testing. .env.testThe
is configured to connect to the PostgreSQL database using the TypeOrmModuleenvironment variable. POSTGRES_URL
We made it; we are the Ory champions! 🏆 But wait, we must test our application to ensure everything works as expected.
We will start with a round of manual tests to ensure the application behaves as expected. We will create users, assign users to the admin group, edit cat profiles, and manage fostering requests.
I built some CLI tools to help you with the manual tests:
kratos-cli will help you create users and generate session tokens to authenticate against the API.
keto-cli will help you to create relationships and check permissions.
First of all, start the services and the API:
Use the link in the email sent to the user:
check email in the local MailSlurper UI
open the latest email with the subject: Please verify your email address
click on the link 🎉
Then copy the session token (starting with ory_st) and paste it in the Authorization
Reproduce the steps above to create a new user and get the user_idketo-cli
It should log the generated relationship:
You can test the permissions by:
updating a cat profile that does not belong to you, using the cat_profile_id
updating a cat profile with an admin user => it should succeed.
You can test the permissions by:
requesting a fostering for a cat profile that does not belong to the current user => it should succeed.
requesting a fostering for a cat profile that belongs to the current user => it should be forbidden.
You can test the permissions by:
approving a fostering request targeting the current user's cat profile => it should succeed.
approving a fostering request targeting another user's cat profile => it should be forbidden.
To show you how to configure Ory for your automated tests, we will create end-to-end tests to manage cat profiles.
One of the great benefits of Ory is that you can quickly adapt the configuration for the testing environment and apply it to your Docker containers. For instance, we don't need to persist the data in the Kratos and Keto databases between tests. We can also turn off the email address verification in the Kratos configuration to simplify the testing process.
create .env.test
create apps/cat-fostering-api/.env.test
use Jest global setup to reconfigure Ory Kratos and Ory Keto and initialize the test database before running the tests
use Jest global teardown to clean up the test database and revert the Ory Kratos and Ory Keto configurations after running the tests
build factories to create users and cat profiles
write the end-to-end tests
start the application in test mode npx nx run cat-fostering-api:serve:testNODE_ENVtest
run the tests npx nx run cat-fostering-api-e2e:e2e
Create a .env.test.env.example
Create the apps/cat-fostering-api/.env.test
We will use the Jest files auto-generated by Nx to configure the global setup and teardown hooks.
To configure the global setup, update the apps/cat-fostering-api-e2e/src/support/global-setup.ts
Note:
The
function is a helper function to ensure the test database exists and is clean. It will use the same configuration as the application. createTestConnectionThe
will be used to destroy the connection in the global teardown. globalThis.__DB_CONNECTION__The
will be used to log the teardown message. globalThis.__TEARDOWN_MESSAGE__The
function is used to run the scripts to generate the Ory test configuration and restart the Docker containers. execSync
Continue with the global teardown configuration in the apps/cat-fostering-api-e2e/src/support/global-teardown.ts
Note: The global teardown will destroy the test database connection and revert the Ory configurations to the initial state.
The factories will wrap the CLI tools we used for the manual tests to create users and permissions. You can find them in the apps/cat-fostering-api-e2e/src/cat-fostering-api/helpers.ts
The end-to-end tests suite will use the factories to create users (including one admin) and cat profiles. To verify that the application creates relationships correctly, we will send HTTP requests to the API to test the following scenarios:
An (authenticated) user can access their profile
An (authenticated) user can create a cat profile
A user can update a cat profile they own
An admin can update any cat profile
A user cannot update a cat profile they do not own
If you have made it this far, congratulations! 🎉 You have successfully integrated Ory into your NestJS application. You have learned how to configure Ory Kratos and Ory Keto for multiple environments, create libraries to interact with the Ory services, and use the Ory APIs in your application.
The following step would be to deploy the application to a cloud provider and use the Ory Network instead of the local containers, which I highly recommend and will be the topic of the upcoming post.

After all this time using NestJS and Nx, I have identified some patterns I use to create libraries that are easy to use and maintain. I yearned to share these insights but needed a fitting use case.

Explore how to enhance your NestJS projects with custom libraries for Ory API integration, ensuring robust and secure authentication.

Explore the integration of Ory into NestJS for secure authentication. This guide covers creating libraries, practical implementation, and deployment to optimize app security and user management. Learn how to enhance your web applications efficiently

Explore the complete process of deploying a NestJS application to the Ory Network. This guide simplifies your transition to a production-ready setup, reducing costs and enhancing efficiency.

Learn to integrate Ory into your NestJS projects for enhanced authentication. This guide covers everything from initial setup to practical implementation and compliance with global standards.

If Signals were Angular’s “aha!” moment, Zoneless is the “oh wow—this feels snappy” moment. With Angular v21, new apps use zoneless change detection by default. No more zone.js magic under the hood—just explicit, predictable reactivity powered by Signals.