Create powerful GraphQL APIs with your AdonisJS Application
Request Feature
·
Report Bug
·
Request Modification
Table of Contents
This package provides a seamless integration of GraphQL into your AdonisJS application using Apollo Server and TypeGraphQL. It supports multiple GraphQL schemas with separate endpoints, making it perfect for scenarios like separating public and admin APIs, or versioning your GraphQL endpoints.
Key Features:
- 🚀 Easy integration with AdonisJS 6+
- 🔄 Support for multiple GraphQL schemas with different endpoints
- 🎯 Type-safe with TypeScript and TypeGraphQL
- 🔐 Built-in authentication and authorization with AdonisJS Auth and Bouncer
- 🎮 GraphQL Playground support for development
- 📡 WebSocket support for real-time subscriptions
- 🔧 Auto-loading resolvers with glob patterns
- 📝 Built-in validation with VineJS integration
- 🕒 Custom scalars (LuxonDateTime included)
- 🎨 Flexible Apollo Server configuration per schema
We welcome contributions and improvements to this module. Don't hesitate to submit features and improvements ;)
For now the package isn't published to npm, but you can install it from the GitHub registry and can be installed in any project.
-
You need to create a
.npmrcfile at the root of your project with the following content:@lithium-apps:registry=https://npm.pkg.github.com
-
For the login process you need to set a personal access token with the
read:packagesscope. Then you can login to the GitHub registry with the following command:pnpm login --registry=https://npm.pkg.github.com --scope=@lithium-apps
-
Install the package using the following command:
pnpm install @lithium-apps/adonis-graphql
-
Configure the package:
node ace configure @lithium-apps/adonis-graphql
This will:
- Add the GraphQL provider to your
adonisrc.ts - Create a
config/graphql.tsconfiguration file - Create a
start/graphql.tsfile for registering resolvers - Create a demo resolver in
app/graphql/resolvers/demo_resolver.ts - Install required dependencies (graphql, type-graphql, graphql-scalars)
- Add the
#graphql/*import alias to yourpackage.json
After installation, the package creates a configuration file at config/graphql.ts:
import app from '@adonisjs/core/services/app'
import { defineConfig } from '@lithium-apps/adonis-graphql'
export default defineConfig({
main: {
path: '/graphql',
apollo: {
introspection: !app.inProduction,
playground: !app.inProduction,
},
emitSchemaFile: true,
}
})For a single GraphQL API, use the default configuration:
- Create a resolver:
// app/graphql/resolvers/user_resolver.ts
import { Resolver, Query, Arg } from '@lithium-apps/adonis-graphql'
@Resolver()
export default class UserResolver {
@Query(() => String)
hello(@Arg('name') name: string): string {
return `Hello ${name}!`
}
}- Register the resolver in
start/graphql.ts:
import graphql from '@lithium-apps/adonis-graphql/services/main'
graphql.use('main').resolvers([
() => import('#graphql/resolvers/demo_resolver'),
() => import('#graphql/resolvers/user_resolver'),
])For multiple GraphQL APIs (e.g., public and admin), update your configuration:
- Update
config/graphql.ts:
import app from '@adonisjs/core/services/app'
import { defineConfig } from '@lithium-apps/adonis-graphql'
export default defineConfig({
public: {
path: '/graphql',
apollo: {
introspection: !app.inProduction,
playground: !app.inProduction,
},
emitSchemaFile: true,
// Auto-load resolvers matching this pattern
resolverPatterns: [
'./app/graphql/public/*_resolver.js'
]
},
admin: {
path: '/admin/graphql',
apollo: {
introspection: !app.inProduction,
playground: !app.inProduction,
},
emitSchemaFile: true,
resolverPatterns: [
'./app/graphql/admin/*_resolver.js'
]
}
})- Register resolvers for each schema:
// start/graphql.ts
import graphql from '@lithium-apps/adonis-graphql/services/main'
// Public API resolvers
await graphql.use('public').resolvers([
() => import('#graphql/public/user_resolver'),
() => import('#graphql/public/post_resolver'),
])
// Admin API resolvers
await graphql.use('admin').resolvers([
() => import('#graphql/admin/admin_resolver'),
() => import('#graphql/admin/analytics_resolver'),
])Basic resolver:
import { Resolver, Query, Mutation, Arg } from 'type-graphql'
@Resolver()
export default class UserResolver {
@Query(() => String)
hello(@Arg('name') name: string): string {
return `Hello ${name}!`
}
@Mutation(() => Boolean)
createUser(@Arg('email') email: string): boolean {
// Your logic here
return true
}
}With HttpContext:
import { Resolver, Query, Ctx } from 'type-graphql'
import type { HttpContext } from '@adonisjs/core/http'
@Resolver()
export default class UserResolver {
@Query(() => String)
async currentUser(@Ctx() ctx: HttpContext): Promise<string> {
await ctx.auth.check()
return ctx.auth.user?.email || 'Guest'
}
}Using the CurrentUser decorator:
import { Resolver, Query } from 'type-graphql'
import { CurrentUser } from '@lithium-apps/adonis-graphql'
@Resolver()
export default class UserResolver {
@Query(() => String)
profile(@CurrentUser() user: any): string {
return `Welcome ${user.email}!`
}
}Using authorization with Bouncer:
import { Resolver, Query, Authorized } from 'type-graphql'
@Resolver()
export default class AdminResolver {
@Authorized(['viewAdminPanel']) // Bouncer ability name
@Query(() => String)
adminData(): string {
return 'Secret admin data'
}
@Authorized() // Just requires authentication
@Query(() => String)
protectedData(): string {
return 'Protected data'
}
}Using VineJS validation:
import { Resolver, Mutation, Arg } from 'type-graphql'
import { validateArgs } from '@lithium-apps/adonis-graphql/decorators/vine/main'
import vine from '@vinejs/vine'
const createUserSchema = vine.compile(
vine.object({
email: vine.string().email(),
name: vine.string().minLength(2),
})
)
@Resolver()
export default class UserResolver {
@validateArgs(createUserSchema)
@Mutation(() => Boolean)
createUser(
@Arg('email') email: string,
@Arg('name') name: string
): boolean {
// Arguments are automatically validated
return true
}
}Using built-in LuxonDateTime scalar:
import { Resolver, Query, Field, ObjectType } from 'type-graphql'
import { DateTime } from 'luxon'
@ObjectType()
class Post {
@Field()
title: string
@Field(() => DateTime)
createdAt: DateTime
}
@Resolver()
export default class PostResolver {
@Query(() => Post)
latestPost(): Post {
return {
title: 'Latest Post',
createdAt: DateTime.now()
}
}
}- ✅ Multiple GraphQL Schemas: Create separate APIs for different purposes (public, admin, etc.)
- ✅ Automatic Endpoint Creation: All configured schemas are automatically instantiated and started
- ✅ Type-Safe: Full TypeScript support with TypeGraphQL decorators
- ✅ Authentication & Authorization: Built-in support for AdonisJS Auth and Bouncer
- ✅ GraphQL Playground: Interactive API explorer for development
- ✅ Auto-Loading: Load resolvers automatically using glob patterns
- ✅ WebSocket Support: Real-time subscriptions with GraphQL subscriptions
- ✅ Single HTTP Server: All schemas share the same HTTP server
- ✅ Flexible Configuration: Independent Apollo Server configuration per schema
- ✅ VineJS Integration: Built-in validation decorators for arguments
- ✅ Custom Scalars: Includes LuxonDateTime scalar, easily extensible
- ✅ Container Integration: Full dependency injection support
- ✅ Error Handling: Comprehensive error handling with custom error types
Custom scalars configuration:
import { defineConfig } from '@lithium-apps/adonis-graphql'
import { DateTimeScalar } from 'graphql-scalars'
export default defineConfig({
main: {
path: '/graphql',
apollo: {
introspection: true,
playground: true,
},
scalarsMap: [
{ type: Date, scalar: DateTimeScalar },
],
emitSchemaFile: true,
}
})WebSocket subscriptions:
import { defineConfig } from '@lithium-apps/adonis-graphql'
import { RedisPubSub } from 'graphql-redis-subscriptions'
export default defineConfig({
main: {
path: '/graphql',
apollo: {
introspection: true,
playground: true,
},
// Enable subscriptions by providing a PubSub instance
pubSub: new RedisPubSub({
connection: {
host: '127.0.0.1',
port: 6379,
}
}),
emitSchemaFile: true,
}
})Resolver patterns for auto-loading:
import { defineConfig } from '@lithium-apps/adonis-graphql'
export default defineConfig({
api: {
path: '/api/graphql',
resolverPatterns: [
'./app/graphql/api/**/*_resolver.js',
'./app/graphql/shared/*_resolver.js'
],
apollo: {
introspection: true,
playground: true,
},
emitSchemaFile: true,
}
})- Kylian Mallet - @Kylian-Mallet - kylian.mallet@sklav.group