Skip to main content
Version: 4.0.0

Apollo GraphQL

GraphQL is useful when clients need to query only the fields they need, aggregate multiple backend data sources, or expose APIs through a unified query language. Midway provides Apollo Server based GraphQL HTTP endpoints through @midwayjs/apollo, while keeping resolvers integrated with Midway dependency injection and request context.

Related information:

Description
Available for standard projects
Available for Serverless
Available for integrated apps
Contains independent main framework
Contains independent logs

Install dependencies

The Apollo component includes the Apollo Server and GraphQL runtime dependencies.

$ npm i @midwayjs/apollo@4 --save

If you use Koa or Express, install the matching web framework component:

$ npm i @midwayjs/koa@4 --save
# or
$ npm i @midwayjs/express@4 --save

Or add the dependencies to package.json and reinstall.

{
"dependencies": {
"@midwayjs/apollo": "^4.0.0",
"@midwayjs/koa": "^4.0.0"
}
}

Enable component

Import Apollo and one web framework component in src/configuration.ts.

import { Configuration } from '@midwayjs/core';
import * as koa from '@midwayjs/koa';
import * as apollo from '@midwayjs/apollo';

@Configuration({
imports: [
koa,
apollo,
],
})
export class MainConfiguration {}

Minimal Example

Configure Apollo in src/config/config.default.ts.

export const apollo = {
path: '/graphql',
typeDefs: `
type Query {
hello: String!
}
`,
resolvers: {
Query: {
hello: () => 'hello midway',
},
},
};

Send a query to /graphql:

query {
hello
}

Response:

{
"data": {
"hello": "hello midway"
}
}

Use Midway Context

The third resolver argument is the active Midway request context itself. You do not need to unwrap context.ctx. Use context.requestContext directly for request-scoped dependencies.

import { UserService } from '../service/user';

export const apollo = {
typeDefs: `
type Query {
userName(id: ID!): String
}
`,
resolvers: {
Query: {
userName: async (_parent, args, context) => {
const userService = await context.requestContext.getAsync(UserService);
const user = await userService.findById(args.id);
return user?.name;
},
},
},
};

In Koa, context is the Midway-augmented Koa ctx. In Express, it is the Midway-augmented Express request/context. Midway attaches requestContext, logger, getLogger(), setAttr(), getAttr(), and getApp().

Resolver Classes

@midwayjs/apollo re-exports GraphQL resolver decorators, so you can import them directly from the Apollo component.

import { Inject } from '@midwayjs/core';
import { Args, Context, Query, Resolver } from '@midwayjs/apollo';
import { UserService } from '../service/user';

@Resolver()
export class UserResolver {
@Inject()
userService: UserService;

@Query('userName')
async userName(@Args('id') id: string, @Context() context) {
const user = await this.userService.findById(id);
context.logger.info('query user %s', id);
return user?.name;
}
}

Available parameter decorators:

DecoratorDescription
@Parent()Injects the parent resolver value
@Args()Injects GraphQL arguments
@Context()Injects the Midway request context
@Info()Injects GraphQL resolve info

Without a field name, the full object is injected. With a field name, only that field is injected, for example @Args('id').

Subscriptions

The component supports GraphQL Subscription through the graphql-ws protocol. It is disabled by default and can be enabled in config.

export const apollo = {
path: '/graphql',
subscriptions: true,
typeDefs: `
type Query {
hello: String!
}

type Subscription {
counter: Int!
}
`,
};

Resolver methods must return an AsyncIterable.

import { Resolver, Subscription } from '@midwayjs/apollo';

@Resolver()
export class CounterResolver {
@Subscription('counter')
async *counter() {
yield {
counter: 1,
};
}
}

When enabled, the WebSocket endpoint defaults to the same path as the HTTP GraphQL endpoint, for example ws://127.0.0.1:7001/graphql. You can also configure a dedicated subscription path.

export const apollo = {
path: '/graphql',
subscriptions: {
path: '/graphql',
connectionInitWaitTimeout: 3000,
},
};

Use Schema Files

You can configure typePaths to load .graphql files instead of writing all schema definitions inline in typeDefs.

export const apollo = {
path: '/graphql',
typePaths: ['./schema.graphql', './graphql/**/*.graphql'],
};

Relative paths in typePaths are resolved from the application baseDir. Both exact file paths and glob patterns are supported. You can also use typeDefs and typePaths together; the component merges them during startup.

Apollo Options

Apollo Server specific options live under apollo.apollo so they do not mix with shared GraphQL fields.

export const apollo = {
typeDefs,
resolvers,
apollo: {
introspection: true,
plugins: [],
},
};

GraphiQL

GraphiQL is enabled by default in development. Visiting /graphql in a browser opens the GraphiQL page. It is disabled by default in production.

export const apollo = {
graphiql: true,
};

You can disable it explicitly:

export const apollo = {
graphiql: false,
};

HTTP Behavior

By default, only GET and POST requests are handled. Other methods sent to the GraphQL path return 405 Method Not Allowed.

export const apollo = {
methods: ['POST'],
};

Extend Context

Use contextFactory to add fields for each GraphQL request.

export const apollo = {
typeDefs,
resolvers,
contextFactory: async context => {
return {
currentUserId: context.headers['x-user-id'],
};
},
};

Then read it in resolvers:

const userId = context.currentUserId;

Built-in fields such as requestContext, logger, and getApp are reserved and should not be overwritten.