LocalStack LogoLocalStack Icon

Build Knowledge Graph Applications with LocalStack and TypeDB Extension

Learn how to build knowledge graph applications with TypeDB extension for LocalStack. You'll learn how to install and use the extension with a sample application to understand the TypeDB schema, data and query relationships, and how LocalStack and TypeDB fit together.

Build Knowledge Graph Applications with LocalStack and TypeDB Extension

Introduction

TypeDB is a polymorphic database designed for complex data relationships. Don’t worry if you aren’t sure what that means yet; I’ll explain in more detail in a bit, but what’s important is that it lets you express nested structures, inheritance, and inference directly in your schema. If you’re building an application dealing with knowledge graphs or access control systems, TypeDB is a natural fit.

We’re excited to introduce a TypeDB extension for LocalStack that brings TypeDB into your local AWS development workflow. With this extension, LocalStack users can now build and test these relationship-heavy applications without needing to provision external infrastructure. With the TypeDB extension installed, a fully functional TypeDB server spins up inside your LocalStack environment. Your application code connects to TypeDB the same way it would in production, without any code changes or external database setup.

In this post, we’ll set up the TypeDB extension and build a user and group management service using Lambda, API Gateway, and TypeDB. This example app would show how nested group memberships work and how TypeQL queries can be used to resolve transitive relationships recursively. By the end, you’ll have a working local setup with the app deployed to LocalStack via Terraform, featuring a web user interface that provides a clear picture of how TypeDB and LocalStack integrate.

What is TypeDB?

TypeDB is an open-source database that organises data as entities, relations, and attributes rather than rows or nodes. You define a schema using TypeQL, the database’s query language, and all data must conform to that schema, similar to how objects in code must match their class definitions. This strict typing catches errors early and makes queries predictable.

TypeDB uses the schema to understand your data model and optimise queries accordingly. Relations in TypeDB are first-class citizens: a “membership” relation between a user and a group is a concrete thing you can query, extend, and attach attributes to. This differs from foreign keys in SQL (which are just pointers) or edges in graph databases (which are typically lightweight connections). When your domain has multi-way relationships or nested hierarchies, TypeDB lets you model them directly instead of working around database limitations.

TypeDB also supports functions and inference in the schema. You can define recursive functions that traverse relationships. For example, a function that returns all groups a user belongs to, including indirect memberships through nested groups. The database evaluates these at query time, so you write the logic once in the schema rather than implementing it in application code. This makes TypeDB particularly useful for access control, organisational hierarchies, and knowledge graphs where transitive relationships matter.

Why run TypeDB as a LocalStack Extension

LocalStack Extensions let you run additional services inside the LocalStack container, sharing the same network and lifecycle as your emulated AWS resources. For applications that combine AWS services with TypeDB, like a Lambda function that queries a knowledge graph, or an API Gateway fronting a TypeDB-backed service, the extension eliminates the need to manage a separate database instance. You can start LocalStack, and the TypeDB server comes up automatically, with your application code connecting to it at typedb.localhost.localstack.cloud:4566.

Running TypeDB as an extension allows you to:

  • Skip Docker Compose setup or separate containers. Install the extension once, and TypeDB is available whenever LocalStack runs.
  • Connect your application to the same hostname locally and in CI, simplifying configuration across environments.
  • Let Lambda functions, Step Functions, or any AWS service emulated by LocalStack interact with TypeDB in the same local network.
  • Spin up a fresh TypeDB instance with each LocalStack restart, useful for running tests against a clean database state.
  • Develop and test TypeDB-powered applications entirely offline, without provisioning any external infrastructure.

You can install the TypeDB extension using the LocalStack CLI or using the Extensions Library on the LocalStack Web Application.

How to use the TypeDB extension for LocalStack

Let’s walk through getting the extension running and deploying a sample application. We’ll install the TypeDB extension, start LocalStack, and then deploy a user/group management service that demonstrates TypeDB’s relationship modelling capabilities.

Prerequisites

Before starting, make sure you have the following installed:

Start your LocalStack container with your LOCALSTACK_AUTH_TOKEN environment variable set. You can use the following command to start LocalStack:

Terminal window
export LOCALSTACK_AUTH_TOKEN=your-auth-token
localstack start

Step 1: Install the TypeDB extension for LocalStack

To install the extension, you can either use the LocalStack CLI or the Extensions Library on the LocalStack Web Application.

To install the extension using the LocalStack CLI, start your LocalStack container, configure your Auth Token as an environment variable and run the following command:

Terminal window
localstack extensions install localstack-extension-typedb

You will see the following output after the installation of the extension is successful:

Terminal window
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
Name Summary Version Author Plugin name
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
localstack-extension-typedb LocalStack Extension: TypeDB on LocalStack 0.1.2 LocalStack + TypeDB team typedb
└─────────────────────────────┴────────────────────────────────────────────┴─────────┴──────────────────────────┴─────────────┘

Alternatively, you can navigate to the Extensions Library on the LocalStack Web Application. The library allows the installation and management of Extensions as simple as the click of a button.

Install TypeDB extension on LocalStack Web Application

Click on the + Install button on the Web Application to install the extension on your local machine.

Step 2: Clone the sample application

We’ve prepared a sample application that demonstrates TypeDB’s capabilities with AWS services. Clone the repository to your local machine:

Terminal window
git clone https://github.com/typedb-osi/typedb-localstack-demo.git
cd typedb-localstack-demo

The project structure looks like this:

Terminal window
├── app/
├── lambda/
├── handler.py # Lambda function code
├── schema.tql # TypeDB schema definition
├── typedb_http_driver.py # HTTP client for TypeDB
└── requirements.txt
└── web/
├── index.html # Web UI
├── script.js
└── style.css
├── main.tf # Terraform configuration
└── Makefile # Build and deploy commands

The application is a user and group management service. Users can belong to groups, and groups can contain other groups (nested memberships). The interesting part is querying all groups a user belongs to, including indirect memberships through nested groups. TypeDB handles this elegantly through its recursive functions.

Step 3: Understanding the TypeDB schema

Before deploying, let’s look at how the data model works. Open app/lambda/schema.tql. This file defines the entire structure of our database.

3.1: Entities and inheritance

Here, we define a principal as an abstract entity, something that can be a member of a group. Both user and group inherit from principal. This means a group can contain users and other groups, enabling nested hierarchies. The @key annotation makes user-name and group-name unique identifiers.

entity principal @abstract,
owns name,
plays membership:member;
entity user sub principal,
owns email @unique @card(1..),
owns user-name @key;
entity group sub principal,
owns group-name @key,
plays membership:container;

3.2: Relations as first-class citizens

Here, the membership relation connects a container (a group) with a member (a user or another group). Unlike a foreign key in SQL, this relation is a concrete object in the database. You can query it, filter it, and extend it with attributes if needed.

relation membership,
relates container,
relates member;

3.3: Recursive functions for transitive queries

This is where TypeDB gets powerful. The schema includes two functions that recursively traverse the membership graph:

fun group-members($group: group) -> { principal }:
match
{
membership (container: $group, member: $member);
} or {
membership (container: $group, member: $group-member);
$group-member isa group;
let $member in group-members($group-member);
};
return { $member };

The group-member function returns all members of a group: direct members and members of nested sub-groups. It works by:

  1. Finding direct members of the group
  2. For any member that is itself a group, recursively calling group-members on that sub-group
  3. Returning the combined results.

Similarly, get-groups returns all groups a principal belongs to, including indirect memberships:

fun get-groups($principal: principal) -> { group }:
match
{
membership (member: $principal, container: $group);
} or {
membership (member: $principal, container: $container);
let $group in get-groups($container);
};
return { $group };

SQL databases would need recursive CTEs or multiple application-level queries to handle this. TypeDB puts the logic in the schema instead, executing it at query time.

Step 4: Deploy the application to LocalStack

With LocalStack running and the TypeDB extension installed, let’s install dependencies, deploy the infrastructure, and verify everything works.

4.1: Install Dependencies

First, set up a Python virtual environment and install the required packages:

Terminal window
make install

This creates a .venv directory and installs localstack, pytest, requests, terraform-local, and the TypeDB driver. The virtual environment keeps these dependencies isolated from your system Python.

4.2: Deploy the infrastructure

Now deploy the Lambda function and API Gateway to LocalStack:

Terminal window
make tf-deploy

This command:

  • Copies the Lambda code to the build/ directory
  • Installs Python dependencies inside a Docker container (for Lambda compatibility)
  • Runs tflocal init and tflocal apply to deploy the infrastructure

The Terraform configuration creates a Lambda function (user-service) that handles all API requests, an API Gateway with endpoints for users, groups, and memberships, alongside related IAM roles and permissions.

4.3: Test the Deployment

Once deployed, you’ll see output like this:

Terminal window
Apply complete! Resources: 60 added, 0 changed, 0 destroyed.
Outputs:
api_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test"
group_all_groups_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/{group_name}/all-groups"
group_all_members_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/{group_name}/all-members"
group_groups_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/{group_name}/groups"
group_members_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/{group_name}/members"
groups_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups"
reset_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/reset"
user_all_groups_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/users/{username}/all-groups"
user_groups_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/users/{username}/groups"
users_endpoint = "http://users-api.execute-api.localhost.localstack.cloud:4566/test/users"

The repository includes integration tests that verify the API endpoints work correctly. Run them with:

Terminal window
make test-lambda

This executes the pytest suite in tests/test_lambda.py, which creates users and groups, builds membership hierarchies, and verifies that both direct and transitive queries return the expected results. If all tests pass, your deployment is working correctly.

Step 5: Create data and query relationships

Let’s create a user, a few groups, and build a nested hierarchy: jane → dev-team → platform → acme-corp.

5.1: Create a user and groups

Run these commands to create Jane and the base groups:

Terminal window
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/users \
-H "Content-Type: application/json" -d '{"username": "jane", "email": "jane@acme.io"}'
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups \
-H "Content-Type: application/json" -d '{"group_name": "dev-team"}'
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups \
-H "Content-Type: application/json" -d '{"group_name": "platform"}'
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups \
-H "Content-Type: application/json" -d '{"group_name": "acme-corp"}'

5.2: Build the hierarchy

Add Jane to the dev team, then nest the groups:

Terminal window
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/dev-team/members \
-H "Content-Type: application/json" -d '{"username": "jane"}'
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/platform/members \
-H "Content-Type: application/json" -d '{"group_name": "dev-team"}'
curl -X POST http://users-api.execute-api.localhost.localstack.cloud:4566/test/groups/acme-corp/members \
-H "Content-Type: application/json" -d '{"group_name": "platform"}'

5.3: Run the Queries

Now query Jane’s direct groups:

Terminal window
curl http://users-api.execute-api.localhost.localstack.cloud:4566/test/users/jane/groups

This returns just “dev-team”. Next, query all transitive groups:

Terminal window
curl http://users-api.execute-api.localhost.localstack.cloud:4566/test/users/jane/all-groups

This returns all three groups even though Jane is only a direct member of dev-team. TypeDB’s get-groups function recursively traversed the hierarchy in a single query, with no loops or multiple round-trips in application code.

Step 6: Explore with Web UI

For a visual representation of the data, start the web interface:

Terminal window
make web-ui

This serves a simple web application at http://localhost:3000. The UI lets you:

  • View all users and groups in the sidebar.
  • Click on a user to see their direct and indirect group memberships.
  • Click on a group to see its direct and indirect members (both users and sub-groups).
  • Create new users and groups.
  • Add members to groups.

Web UI

The web interface makes the transitive relationships easy to visualise. When you click on “company”, you’ll see that its direct members include only “engineering”, but its indirect members include “backend-team” and “alice”, all resolved by TypeDB’s recursive functions.

Conclusion

The TypeDB extension for LocalStack brings a powerful polymorphic database into your local development environment with minimal setup. In this tutorial, we built a user and group management service showing how TypeDB handles schema-based data modelling and first-class relations. The recursive functions that resolve transitive relationships at query time eliminate the need for complex application logic.

When your application needs to model complex relationships, the TypeDB extension keeps that logic in the schema rather than scattered across your codebase. TypeDB’s expressive schema pairs well with LocalStack’s local development approach. You can iterate on your data model and test transitive queries before pushing to production.

Learn More


Harsh Mishra
Harsh Mishra
Engineer at LocalStack
Harsh Mishra is an Engineer at LocalStack and AWS Community Builder. Harsh has previously worked at HackerRank, Red Hat, and Quansight, and specialized in DevOps, Platform Engineering, and CI/CD pipelines.
Brian Rinaldi
Brian Rinaldi
Head of Developer Relations at LocalStack
Brian Rinaldi leads the Developer Relations team at LocalStack. Brian has over 25 years experience as a developer – mostly for the web – and over a decade in Developer Relations for companies like Adobe, Progress Software and LaunchDarkly.