Skip to content

Commit

Permalink
nestjs federation compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Mar 3, 2023
1 parent 8466662 commit 6b5af54
Show file tree
Hide file tree
Showing 13 changed files with 815 additions and 17 deletions.
9 changes: 9 additions & 0 deletions examples/nestjs-apollo-federation-compatibility/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM node:18
WORKDIR /web
COPY package.json schema.graphql tsconfig.json tsconfig.build.json ./
COPY src ./src
RUN npm install
RUN npm run build
EXPOSE 4001
USER node
CMD node dist/main
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
products:
# must be relative to the root of the project
build: implementations/nestjs
ports:
- 4001:4001
28 changes: 28 additions & 0 deletions examples/nestjs-apollo-federation-compatibility/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "examples-nestjs-apollo-federation-compatibility",
"version": "1.0.0",
"description": "Apollo Federation implemented with GraphQL Yoga running as a Nest.js driver.",
"private": true,
"scripts": {
"build": "nest build",
"prebuild": "rimraf dist",
"start": "nest start"
},
"dependencies": {
"@apollo/subgraph": "^2.3.2",
"@envelop/apollo-federation": "^3.0.6",
"@envelop/core": "^3.0.6",
"@graphql-yoga/nestjs": "0.3.1",
"@nestjs/common": "^9.3.9",
"@nestjs/core": "^9.3.9",
"@nestjs/graphql": "^10.2.0",
"graphql": "^16.6.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0"
},
"devDependencies": {
"@nestjs/cli": "^9.2.0",
"rimraf": "^4.1.2",
"typescript": "^4.9.5"
}
}
83 changes: 83 additions & 0 deletions examples/nestjs-apollo-federation-compatibility/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3"
import: [
"@composeDirective"
"@extends"
"@external"
"@key"
"@inaccessible"
"@interfaceObject"
"@override"
"@provides"
"@requires"
"@shareable"
"@tag"
]
)
@link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"])
@composeDirective(name: "@custom")

directive @custom on OBJECT

type Product
@custom
@key(fields: "id")
@key(fields: "sku package")
@key(fields: "sku variation { id }") {
id: ID!
sku: String
package: String
variation: ProductVariation
dimensions: ProductDimension
createdBy: User @provides(fields: "totalProductsCreated")
notes: String @tag(name: "internal")
research: [ProductResearch!]!
}

type DeprecatedProduct @key(fields: "sku package") {
sku: String!
package: String!
reason: String
createdBy: User
}

type ProductVariation {
id: ID!
}

type ProductResearch @key(fields: "study { caseNumber }") {
study: CaseStudy!
outcome: String
}

type CaseStudy {
caseNumber: ID!
description: String
}

type ProductDimension @shareable {
size: String
weight: Float
unit: String @inaccessible
}

extend type Query {
product(id: ID!): Product
deprecatedProduct(sku: String!, package: String!): DeprecatedProduct
@deprecated(reason: "Use product query instead")
}

extend type User @key(fields: "email") {
averageProductsCreatedPerYear: Int
@requires(fields: "totalProductsCreated yearsOfEmployment")
email: ID! @external
name: String @override(from: "users")
totalProductsCreated: Int @external
yearsOfEmployment: Int! @external
}

type Inventory @interfaceObject @key(fields: "id") {
id: ID!
deprecatedProducts: [DeprecatedProduct!]!
}
29 changes: 29 additions & 0 deletions examples/nestjs-apollo-federation-compatibility/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
YogaFederationDriver,
YogaFederationDriverConfig,
} from '@graphql-yoga/nestjs'
import { Module } from '@nestjs/common'
import { GraphQLModule } from '@nestjs/graphql'
import { DeprecatedProductsResolver } from './deprecated-products.resolver'
import { InventoryResolver } from './inventory.resolver'
import { ProductResearchResolver } from './product-research.resolver'
import { ProductsResolver } from './products.resolver'
import { UsersResolver } from './users.resolver'

@Module({
imports: [
GraphQLModule.forRoot<YogaFederationDriverConfig>({
driver: YogaFederationDriver,
typePaths: ['**/*.graphql'],
path: '/',
}),
],
providers: [
UsersResolver,
ProductsResolver,
ProductResearchResolver,
DeprecatedProductsResolver,
InventoryResolver,
],
})
export class AppModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Args,
Query,
ResolveField,
Resolver,
ResolveReference,
} from '@nestjs/graphql'

interface User {
email: string
name: string
totalProductsCreated: number
}

interface DeprecatedProduct {
sku: string
package: string
reason: string
createdBy: User
}

const user = {
email: 'support@apollographql.com',
name: 'Jane Smith',
totalProductsCreated: 1337,
}

const deprecatedProduct = {
sku: 'apollo-federation-v1',
package: '@apollo/federation-v1',
reason: 'Migrate to Federation V2',
}

@Resolver('DeprecatedProduct')
export class DeprecatedProductsResolver {
constructor() {}

@Query()
deprecatedProduct(
@Args('sku') sku: string,
@Args('package') packageName: string,
) {
return sku === deprecatedProduct.sku &&
packageName === deprecatedProduct.package
? deprecatedProduct
: null
}

@ResolveField('createdBy')
getCreatedBy() {
return user
}

@ResolveReference()
resolveReference(reference: DeprecatedProduct) {
if (
reference.sku === deprecatedProduct.sku &&
reference.package === deprecatedProduct.package
) {
return deprecatedProduct
} else {
return null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Resolver, ResolveReference } from '@nestjs/graphql'

interface DeprecatedProduct {
sku: string
package: string
reason: string
}

interface Inventory {
id: string
deprecatedProducts: [DeprecatedProduct]
}

const inventory = {
id: 'apollo-oss',
deprecatedProducts: [
{
sku: 'apollo-federation-v1',
package: '@apollo/federation-v1',
reason: 'Migrate to Federation V2',
},
],
}

@Resolver('Inventory')
export class InventoryResolver {
constructor() {}

@ResolveReference()
resolveReference(reference: Inventory) {
if (reference.id == inventory.id) {
return inventory
} else {
return null
}
}
}
11 changes: 11 additions & 0 deletions examples/nestjs-apollo-federation-compatibility/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Logger } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'

async function bootstrap() {
const logger = new Logger()
const app = await NestFactory.create(AppModule, { logger })
await app.listen(4001)
logger.log('Nest.js server listening on http://localhost:4001')
}
bootstrap()
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ResolveField, Resolver, ResolveReference } from '@nestjs/graphql'

interface CaseStudy {
caseNumber: string
description: string
}

interface ProductResearch {
study: CaseStudy
outcome: string
}

const productResearch = [
{
study: {
caseNumber: '1234',
description: 'Federation Study',
},
},
{
study: {
caseNumber: '1235',
description: 'Studio Study',
},
},
]

@Resolver('ProductResearch')
export class ProductResearchResolver {
constructor() {}

@ResolveField()
getStudy() {
return productResearch[0].study
}

@ResolveReference()
resolveReference(reference: ProductResearch) {
return productResearch.find(
(p) => reference.study.caseNumber === p.study.caseNumber,
)
}
}
Loading

0 comments on commit 6b5af54

Please sign in to comment.