Skip to content

Commit

Permalink
docs(examples): add apollo federation example using @Directive
Browse files Browse the repository at this point in the history
  • Loading branch information
j committed Jul 10, 2019
1 parent e66dbff commit ec94ba0
Show file tree
Hide file tree
Showing 6 changed files with 451 additions and 0 deletions.
1 change: 1 addition & 0 deletions dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ require("ts-node/register/transpile-only");
// require("./examples/typeorm-lazy-relations/index.ts");
// require("./examples/using-container/index.ts");
// require("./examples/using-scoped-container/index.ts");
// require("./examples/apollo-federation/index.ts");
87 changes: 87 additions & 0 deletions examples/apollo-federation/accounts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
ObjectType,
Field,
Resolver,
Query,
buildSchema,
ID,
FieldResolver,
Root,
Directive,
} from "../../../src";
import federationDirectives from "@apollo/federation/dist/directives";
import { buildFederatedSchema } from "@apollo/federation";
import { specifiedDirectives } from "graphql";
import { ApolloServer } from "apollo-server";
import { plainToClass } from "class-transformer";
import { getMetadataStorage } from "../../../src/metadata/getMetadataStorage";

const buildTypeSchema = async () => {
getMetadataStorage().clear();

@ObjectType()
@Directive("key", { fields: "id" })
class User {
@Field(() => ID)
id: string;

@Field()
username: string;

@Field()
name: string;

@Field()
birthDate: string;
}

const users: User[] = plainToClass(User, [
{
id: "1",
name: "Ada Lovelace",
birthDate: "1815-12-10",
username: "@ada",
},
{
id: "2",
name: "Alan Turing",
birthDate: "1912-06-23",
username: "@complete",
},
]);

@Resolver(() => User)
class AccountsResolver {
@Query(() => User)
me(): User {
return users[0];
}

@FieldResolver(() => User, { nullable: true })
async __resolveReference(@Root() reference: Partial<User>): Promise<User | undefined> {
return users.find(u => u.id === reference.id);
}
}

return await buildSchema({
resolvers: [AccountsResolver],
directives: [...specifiedDirectives, ...federationDirectives],
skipCheck: true,
});
};

export async function listen(port: number): Promise<string> {
const schema = buildFederatedSchema(await buildTypeSchema());

const server = new ApolloServer({
schema,
tracing: false,
playground: true,
});

const { url } = await server.listen({ port });

console.log(`🚀 Accounts service ready at ${url}`);

return url;
}
35 changes: 35 additions & 0 deletions examples/apollo-federation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "reflect-metadata";
import { ApolloGateway } from "@apollo/gateway";
import { ApolloServer } from "apollo-server";
import * as accounts from "./accounts";
import * as reviews from "./reviews";
import * as products from "./products";
import * as inventory from "./inventory";

async function bootstrap() {
const serviceList = [
{ name: "accounts", url: await accounts.listen(3001) },
{ name: "reviews", url: await reviews.listen(3002) },
{ name: "products", url: await products.listen(3003) },
{ name: "inventory", url: await inventory.listen(3004) },
];

const gateway = new ApolloGateway({
serviceList,
});

const { schema, executor } = await gateway.load();

const server = new ApolloServer({
schema,
executor,
tracing: false,
playground: true,
});

server.listen({ port: 3000 }).then(({ url }) => {
console.log(`🚀 Apollo Gateway ready at ${url}`);
});
}

bootstrap();
101 changes: 101 additions & 0 deletions examples/apollo-federation/inventory/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import federationDirectives from "@apollo/federation/dist/directives";
import { buildFederatedSchema } from "@apollo/federation";
import { specifiedDirectives } from "graphql";
import { ApolloServer } from "apollo-server";
import { plainToClass } from "class-transformer";
import {
ObjectType,
Field,
Resolver,
buildSchema,
FieldResolver,
Root,
Directive,
} from "../../../src";
import { getMetadataStorage } from "../../../src/metadata/getMetadataStorage";

const buildTypeSchema = async () => {
getMetadataStorage().clear();

@ObjectType()
@Directive("extends")
@Directive("key", { fields: "upc" })
class Product {
@Field()
@Directive("external")
upc: string;

@Field()
@Directive("external")
weight: number;

@Field()
@Directive("external")
price: number;

@Field()
inStock: boolean;
}

interface Inventory {
upc: string;
inStock: boolean;
}

const inventory: Inventory[] = [
{ upc: "1", inStock: true },
{ upc: "2", inStock: false },
{ upc: "3", inStock: true },
];

@Resolver(() => Product)
class InventoryResolver {
@FieldResolver(() => Number)
@Directive("requires", { fields: "price weight" })
async shippingEstimate(@Root() product: Product): Promise<number> {
// free for expensive items
if (product.price > 1000) {
return 0;
}

// estimate is based on weight
return product.weight * 0.5;
}

@FieldResolver(() => Product, { nullable: true })
async __resolveReference(@Root() reference: Partial<Product>): Promise<Product | undefined> {
const found = inventory.find(i => i.upc === reference.upc);

if (!found) {
return;
}

return plainToClass(Product, {
...reference,
...found,
});
}
}

return await buildSchema({
resolvers: [InventoryResolver],
directives: [...specifiedDirectives, ...federationDirectives],
skipCheck: true,
});
};

export async function listen(port: number): Promise<string> {
const schema = buildFederatedSchema(await buildTypeSchema());

const server = new ApolloServer({
schema,
tracing: false,
playground: true,
});

const { url } = await server.listen({ port });

console.log(`🚀 Inventory service ready at ${url}`);

return url;
}
93 changes: 93 additions & 0 deletions examples/apollo-federation/products/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import federationDirectives from "@apollo/federation/dist/directives";
import { buildFederatedSchema } from "@apollo/federation";
import { specifiedDirectives } from "graphql";
import { ApolloServer } from "apollo-server";
import { plainToClass } from "class-transformer";
import {
ObjectType,
Field,
Resolver,
Query,
buildSchema,
FieldResolver,
Arg,
Root,
Directive,
} from "../../../src";
import { getMetadataStorage } from "../../../src/metadata/getMetadataStorage";

const buildTypeSchema = async () => {
getMetadataStorage().clear();

@ObjectType()
@Directive("key", { fields: "upc" })
class Product {
@Field()
upc: string;

@Field()
name: string;

@Field()
price: number;

@Field()
weight: number;
}

const products: Product[] = plainToClass(Product, [
{
upc: "1",
name: "Table",
price: 899,
weight: 100,
},
{
upc: "2",
name: "Couch",
price: 1299,
weight: 1000,
},
{
upc: "3",
name: "Chair",
price: 54,
weight: 50,
},
]);

@Resolver(() => Product)
class ProductsResolver {
@Query(() => [Product])
async topProducts(@Arg("first", { defaultValue: 5 }) first: number): Promise<Product[]> {
return products.slice(0, first);
}

@FieldResolver(() => Product, { nullable: true })
async __resolveReference(@Root() reference: Partial<Product>): Promise<Product | undefined> {
return products.find(p => p.upc === reference.upc);
}
}

return await buildSchema({
resolvers: [ProductsResolver],
directives: [...specifiedDirectives, ...federationDirectives],
skipCheck: true,
});
};

export async function listen(port: number): Promise<string> {
const schema = buildFederatedSchema(await buildTypeSchema());

const server = new ApolloServer({
schema,
tracing: false,
playground: true,
});

const { url } = await server.listen({ port });

console.log(`🚀 Products service ready at ${url}`);

return url;
}
Loading

0 comments on commit ec94ba0

Please sign in to comment.