@ambiten/adapter-graphql
v2.0.2
Published
Ambiten adapter for GraphQL
Downloads
227
Readme
Overview
@ambiten/adapter-graphql connects GraphQL execution to the Ambiten runtime.
The adapter establishes an Ambiten execution boundary around the actual GraphQL operation so runtime context remains active while resolvers, nested asynchronous calls, application services, and models execute.
This allows tenant identity, request metadata, logging, instrumentation, and other execution-scoped state to move through the application without being passed manually through every resolver.
The package currently provides integrations for:
- Apollo Server
- GraphQL Yoga
The GraphQL server may change.
The Ambiten execution model does not.
Installation
Install the GraphQL adapter:
npm install @ambiten/adapter-graphqlMost applications will also use the Ambiten core runtime:
npm install @ambiten/core @ambiten/adapter-graphqlInstall the GraphQL server separately according to your application.
Apollo Server
npm install @apollo/server graphqlGraphQL Yoga
npm install graphql-yoga graphql@ambiten/adapter-graphql does not require Ambiten applications to adopt a particular GraphQL server internally.
Apollo Server
Use createApolloAdapter() to establish the Ambiten runtime around Apollo GraphQL execution.
import {
ApolloServer
} from "@apollo/server";
import {
startStandaloneServer
} from "@apollo/server/standalone";
import {
createApolloAdapter
} from "@ambiten/adapter-graphql";
import {
MultiTenantManager
} from "@ambiten/core";
const server =
new ApolloServer({
typeDefs,
resolvers
});
const adapter =
createApolloAdapter();
adapter.install(
server,
{
tenancy: {
header:
"x-tenant-id",
validate:
async (
tenantId
) => {
const tenant =
await MultiTenantManager
.resolveTenant(
tenantId
);
if (!tenant) {
throw new Error(
`Tenant with ID "${tenantId}" not found.`
);
}
return true;
}
}
}
);
await startStandaloneServer(
server,
{
listen: {
port: 4000
}
}
);The application does not need to interact with adapter-runtime internals.
The execution flow becomes:
HTTP Request
↓
Apollo Server
↓
createApolloAdapter()
↓
Ambiten Adapter Runtime
↓
Tenant Resolution
↓
AmbitenContext
↓
Resolvers
↓
Services
↓
AmbitenModel
↓
MongoDBThe Ambiten context remains active until GraphQL execution completes.
GraphQL Yoga
Use createYogaAdapter() as a Yoga plugin.
import {
createSchema,
createYoga
} from "graphql-yoga";
import {
createYogaAdapter
} from "@ambiten/adapter-graphql";
import {
MultiTenantManager
} from "@ambiten/core";
const yoga =
createYoga({
schema:
createSchema({
typeDefs,
resolvers
}),
plugins: [
createYogaAdapter({
tenancy: {
header:
"x-tenant-id",
validate:
async (
tenantId
) => {
const tenant =
await MultiTenantManager
.resolveTenant(
tenantId
);
if (!tenant) {
throw new Error(
`Tenant with ID "${tenantId}" not found.`
);
}
return true;
}
}
})
]
});Yoga queries, mutations, subscriptions, and supported streaming execution enter the same Ambiten runtime model.
Runtime Flow
Regardless of the GraphQL server, the execution architecture remains consistent:
GraphQL Request
↓
Apollo Server / GraphQL Yoga
↓
Ambiten GraphQL Adapter
↓
Adapter Runtime
↓
Tenant Resolution
↓
AmbitenContext
↓
Resolver
↓
Application Service
↓
AmbitenModel
↓
Effective ModelContext
↓
Tenant Infrastructure
↓
MongoDBThe adapter owns the framework execution boundary.
Application code remains focused on GraphQL and business logic.
Resolver Execution
Resolvers do not need to manually forward tenant identity or request metadata through application layers.
export const resolvers = {
Query: {
users:
async () => {
return UserModel.find(
{}
);
}
},
Mutation: {
createUser:
async (
_parent,
args
) => {
return UserModel.create(
args.input
);
}
}
};Conceptually:
Resolver
↓
Service
↓
Nested async work
↓
AmbitenModel
↓
MongoDBThe active Ambiten execution context remains available throughout the chain.
Multi-Tenancy
Tenant identity can be resolved at the GraphQL ingress boundary.
createApolloAdapter()
.install(
server,
{
tenancy: {
header:
"x-tenant-id",
validate:
async (
tenantId
) => {
const tenant =
await MultiTenantManager
.resolveTenant(
tenantId
);
return Boolean(
tenant
);
}
}
}
);or with Yoga:
createYogaAdapter({
tenancy: {
header:
"x-tenant-id",
validate:
async (
tenantId
) => {
const tenant =
await MultiTenantManager
.resolveTenant(
tenantId
);
return Boolean(
tenant
);
}
}
});Once resolved, the tenant identity becomes part of the active Ambiten execution.
Models and services can then participate in tenant-aware infrastructure resolution without manually receiving the tenant ID through resolver arguments.
Tenant resolution does not replace authentication or authorization.
Applications remain responsible for deciding whether a caller is permitted to act for a resolved tenant.
Application Context
Ambiten does not replace the normal GraphQL context object.
Application-specific values such as:
- authenticated users
- DataLoaders
- API clients
- feature flags
- domain services
can continue to use the framework's normal GraphQL context.
Ambiten separately maintains execution-scoped runtime state such as:
tenantId
requestId
dbName
collectionName
debug
logger metadata
runtime metadataConceptually:
GraphQL Context
→ application data
→ authenticated user
→ loaders
→ services
AmbitenContext
→ execution identity
→ tenant identity
→ request metadata
→ infrastructure stateThe two contexts can coexist.
Concurrent Execution
Ambiten uses execution-scoped context propagation so concurrent GraphQL operations remain isolated.
Operation A
tenant-a
request-a
↓
resolver chain
↓
tenant-a remains active
Operation B
tenant-b
request-b
↓
resolver chain
↓
tenant-b remains activeOne operation does not need to manually protect itself from another operation's runtime state.
Subscriptions and Streaming
The GraphQL adapter supports runtime continuity for subscription and streaming execution.
A subscription may outlive the initial GraphQL execution call, so Ambiten restores the already-resolved execution snapshot when the asynchronous iterator continues.
Conceptually:
GraphQL Subscription
↓
Tenant resolved once
↓
Execution snapshot
↓
Async iterator
↓
next()
↓
Ambiten execution restored
↓
resolver / service / model workThis preserves execution identity without treating each emitted payload as a new ingress request.
Transactions
Automatic GraphQL-operation-wide transactions are intentionally not supported through:
enableTransactions: trueGraphQL may complete an operation successfully at the transport level while still reporting resolver failures in the GraphQL result.
For that reason, transaction ownership should remain aligned with the business workflow rather than the entire GraphQL operation.
Use an explicit transaction boundary inside the mutation or application service that requires atomicity.
Mutation Resolver
↓
Application Service
↓
Explicit Transaction Boundary
↓
Model A
↓
Model B
↓
Commit / RollbackThis keeps transaction semantics aligned with the actual unit of work.
Framework-Neutral Design
@ambiten/adapter-graphql intentionally keeps its runtime integration independent from GraphQL framework type hierarchies.
Internally, framework-specific requests are normalized into Ambiten's adapter request contract:
Apollo ─────┐
│
Yoga ───────┤
↓
AmbitenRequestLike
↓
Adapter Runtime
↓
AmbitenContextThis reduces coupling between Ambiten and framework release cycles while preserving a stable execution model.
Applications remain free to choose and upgrade their GraphQL server independently.
Legacy Context Factories
Earlier versions exposed:
createApolloContextFactory()
createYogaContextFactory()These APIs may remain available for compatibility, but they are not the recommended integration path for new applications.
Creating a GraphQL context object is not the same as keeping the Ambiten runtime active while resolvers execute.
For new applications, use:
createApolloAdapter()or:
createYogaAdapter()These APIs establish the runtime around actual GraphQL execution.
Execution Boundary
The central rule is simple:
GraphQL context creation
≠
GraphQL executionAmbiten therefore establishes its runtime around:
GraphQL Operation
↓
Resolvers
↓
Services
↓
Modelsrather than only around the earlier context-construction lifecycle.
This ensures execution state remains available where application work actually happens.
Documentation
Complete documentation is available at:
https://docs.ambiten.dev
Related Packages
- @ambiten/core
- @ambiten/adapter-runtime
- @ambiten/adapter-types
- @ambiten/logger
- @ambiten/create
License
MIT
