├── .eslintrc.js
├── .funcignore
├── .gitignore
├── LICENSE
├── README.md
├── host.json
├── local.settings.json
├── main
├── function.json
├── index.ts
└── sample.dat
├── package-lock.json
├── package.json
├── proxies.json
├── schema.gql
├── src
├── app.module.ts
├── common
│ └── scalars
│ │ └── date.scalar.ts
├── main.azure.ts
├── main.ts
└── recipes
│ ├── dto
│ ├── new-recipe.input.ts
│ └── recipes.args.ts
│ ├── models
│ └── recipe.ts
│ ├── recipes.module.ts
│ ├── recipes.resolver.ts
│ └── recipes.service.ts
├── template.zip
├── tsconfig.build.json
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/eslint-recommended',
10 | 'plugin:@typescript-eslint/recommended',
11 | 'prettier',
12 | 'prettier/@typescript-eslint',
13 | ],
14 | root: true,
15 | env: {
16 | node: true,
17 | jest: true,
18 | },
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/no-explicit-any': 'off',
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/.funcignore:
--------------------------------------------------------------------------------
1 | *.js.map
2 | *.ts
3 | .git*
4 | .vscode
5 | local.settings.json
6 | test
7 | tsconfig.json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # IDE
5 | /.idea
6 | /.awcache
7 | /.vscode
8 |
9 | # misc
10 | npm-debug.log
11 |
12 | # example
13 | /quick-start
14 |
15 | # tests
16 | /test
17 | /coverage
18 | /.nyc_output
19 |
20 | # dist
21 | /dist
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Andrew Moss
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Deploying Serverless NestJS GraphQL API to Azure Functions
6 |
7 | Getting `nest/azure-func-http` and `nest/graphql` to play well together is tricky. There are several GH issues and SO posts on this topic with little in the way of a solution. Additionally, none of the official nest.js docs or samples contain a configuration with both graphql and azure functions.
8 |
9 | The one source of reliable information on this topic is a blog post by trilon.io [here](https://trilon.io/blog/deploy-nestjs-azure-functions). This is a good tutorial on creating a Nest.js REST api with `nest/azure-func-http`. However the tutorial steps to not carry over directly when creating a GraphQl API.
10 |
11 | This repo and tutorial is a minimal example of a working integration of `nest/azure-func-http` and `nest/graphql`. I hope this helps some folks out!
12 |
13 | ## Starting Point
14 |
15 | I started this repo with the boilerplate from [23-type-graphql](https://github.com/nestjs/nest/tree/master/sample/23-type-graphql). This is a working repo with Typescript, GraphQL, and Nest but NOT `nest/azure-func-http`
16 |
17 | ## Adding azure-func-http
18 |
19 | ```bash
20 | $ nest add @nestjs/azure-func-http
21 | ```
22 |
23 | This will install the function app boilerplate in the repo. Here is where this tutorial deviates from the trilion.io tutorial. Several of the default azure function configurations need to be altered along with some of your nest app code.
24 |
25 | ## Steps
26 |
27 | ### 1. Change build script in `package.json`
28 |
29 | ```diff
30 | - "build": "nest build"
31 | + "build": "rimraf dist && tsc -p tsconfig.build.json"
32 | ```
33 |
34 | ### 2. Remove the include from your tsconfig.json
35 |
36 | ```diff
37 | {
38 | "compilerOptions": {
39 | "module": "commonjs",
40 | "declaration": true,
41 | "removeComments": true,
42 | "allowSyntheticDefaultImports": true,
43 | "emitDecoratorMetadata": true,
44 | "experimentalDecorators": true,
45 | "target": "es2017",
46 | "sourceMap": true,
47 | "outDir": "./dist",
48 | "baseUrl": "./",
49 | "incremental": true,
50 | "skipLibCheck": true
51 | },
52 | - "include": ["src/**/*"],
53 | "exclude": ["node_modules", "dist"]
54 | }
55 | ```
56 |
57 | These two steps create seperate `/src` and `/main` directories in `/dist`.
58 |
59 | - `/src` is for your source code
60 | - `/main` is the entry point for the function app
61 |
62 | ### 3. Adjust Nest.js App
63 |
64 | At this point the azure function will run but it will not resolve your GraphQL requests! Some changes need to be made to the nest app itself.
65 |
66 | main.ts
67 |
68 | ```diff
69 | import { ValidationPipe } from "@nestjs/common";
70 | import { NestFactory } from "@nestjs/core";
71 | import { AppModule } from "./app.module";
72 |
73 | async function bootstrap() {
74 | const app = await NestFactory.create(AppModule);
75 | app.useGlobalPipes(new ValidationPipe());
76 | + app.enableCors();
77 | + app.setGlobalPrefix("api");
78 | await app.listen(3000);
79 | }
80 | bootstrap();
81 | ```
82 |
83 | app.module.ts
84 |
85 | ```diff
86 | import { Module } from "@nestjs/common";
87 | import { GraphQLModule } from "@nestjs/graphql";
88 | import { RecipesModule } from "./recipes/recipes.module";
89 |
90 | @Module({
91 | imports: [
92 | RecipesModule,
93 | GraphQLModule.forRoot({
94 | installSubscriptionHandlers: true,
95 | + context: ({ req }) => ({ req }),
96 | autoSchemaFile: "schema.gql",
97 | + useGlobalPrefix: true
98 | })
99 | ]
100 | })
101 | export class AppModule {}
102 | ```
103 |
104 | 3. Adjust function app config
105 |
106 | host.json
107 |
108 | ```diff
109 | {
110 | "version": "2.0",
111 | + "extensions": {
112 | + "http": {
113 | + "routePrefix": "api"
114 | + }
115 | }
116 | }
117 | ```
118 |
119 | index.ts
120 |
121 | ```diff
122 | import { Context, HttpRequest } from "@azure/functions";
123 | import { AzureHttpAdapter } from "@nestjs/azure-func-http";
124 | import { createApp } from "../src/main.azure";
125 |
126 | export default function(context: Context, req: HttpRequest): void {
127 | + context.res = {
128 | + headers: {
129 | + "Content-Type": "application/json"
130 | + }
131 | + };
132 | AzureHttpAdapter.handle(createApp, context, req);
133 | }
134 |
135 | ```
136 |
137 | ### Your GraphQL function app is good to go!!
138 |
139 | ```bash
140 | $ npm run build && func host start
141 | ```
142 |
143 | ### Testing out the app
144 |
145 | Add a sample body to the create method in `recipies.service.ts` for testing.
146 |
147 | recipies.service.ts
148 |
149 | ```diff
150 | async create(data: NewRecipeInput): Promise {
151 | + return {
152 | + id: "sample",
153 | + title: data.title,
154 | + description: data.description,
155 | + creationDate: new Date(),
156 | + ingredients: data.ingredients
157 | + } as Recipe;
158 | - return {} as any;
159 | }
160 | ```
161 |
162 | fire up http://localhost:7071/api/graphql and run a mutation
163 |
164 | ```graphql
165 | mutation($newRecipeData: NewRecipeInput!) {
166 | addRecipe(newRecipeData: $newRecipeData) {
167 | creationDate
168 | }
169 | }
170 | ```
171 |
172 | query variables
173 |
174 | ```json
175 | {
176 | "newRecipeData": {
177 | "title": "Salad",
178 | "description": "Im trying to be healthy and im disappointed in my self",
179 | "ingredients": ["leaves", "sadness"]
180 | }
181 | }
182 | ```
183 |
184 | you should get back something like....
185 |
186 | ```json
187 | {
188 | "data": {
189 | "addRecipe": {
190 | "creationDate": 1582340836192
191 | }
192 | }
193 | }
194 | ```
195 |
196 | ---
197 |
198 | ## Deploying to Azure Functions
199 |
200 | > _The battle has been won but the war has just begun_
201 |
202 |
203 | 1. Create resource in Azure Portal
204 |
205 | Navigate to your azure account and create a new `Function App`. The `Create Function App` workflow contains five tabs: Basics, Hosting, Monitoring, Tags, and Review + create. Settings in `Basics` and `Hosting` need to be adjusted, the other tabs can be left at their defaults.
206 |
207 | > Basics
208 |
209 | 
210 |
211 | > Hosting
212 |
213 | 
214 |
215 |
216 | Upon completing the Basics and Hosting, press `Review + create` to deploy the function app.
217 |
218 |
219 | 2. Deploy your code to the Function App
220 |
221 | Once created, head back to your editor and deploy your Nest Graphql App to this newly created Function App.
222 |
223 | ```bash
224 | $ npm run build && func azure functionapp publish
225 | ```
226 |
227 | This will create an output similar to the following:
228 |
229 | 
230 |
231 | **Note**: This step requires `Azure Functions Core Tools v3` for local development and deployment of Azure Functions. Instructions on how to get this on your development machine can be found [here](https://github.com/Azure/azure-functions-core-tools)
232 |
233 | 3. Use the deployed graphql endpoint
234 |
235 | Wait a few minutes for the application to propagate then head to the deployed graphql endpoint: `https://.azurewebsites.net/api/graphql`
236 |
237 | You should then be treated to the graphql playground.
238 |
239 | 
240 |
241 |
242 | ## Notes on Deployment
243 |
244 | Getting the function to run remotely on azure is not clear cut. I have found that the best configuration options are Function app V3 and WEBSITES_NODE_DEFAULT_VERSION set to ~12
245 |
246 | Documentation on how to changes these Azure related setting can be found [here](https://docs.microsoft.com/en-us/azure/azure-functions/)
247 |
248 | If you are using vscode there is some helpful extensions for deploying function apps. Your can read about that [here](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-vs-code?pivots=programming-language-typescript)
249 |
250 | Included in this repo is a `template.zip`. This contains the templated deployment parameters from the `Deploying to Azure Functions` section of this article.
251 |
--------------------------------------------------------------------------------
/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "extensionBundle": {
4 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
5 | "version": "[1.*, 2.0.0)"
6 | },
7 | "extensions": {
8 | "http": {
9 | "routePrefix": "api"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/local.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IsEncrypted": false,
3 | "Values": {
4 | "AzureWebJobsStorage": "",
5 | "FUNCTIONS_WORKER_RUNTIME": "node"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/main/function.json:
--------------------------------------------------------------------------------
1 | {
2 | "bindings": [
3 | {
4 | "authLevel": "anonymous",
5 | "type": "httpTrigger",
6 | "direction": "in",
7 | "name": "req",
8 | "route": "{*segments}"
9 | },
10 | {
11 | "type": "http",
12 | "direction": "out",
13 | "name": "res"
14 | }
15 | ],
16 | "scriptFile": "../dist/main/index.js"
17 | }
18 |
--------------------------------------------------------------------------------
/main/index.ts:
--------------------------------------------------------------------------------
1 | import { Context, HttpRequest } from "@azure/functions";
2 | import { AzureHttpAdapter } from "@nestjs/azure-func-http";
3 | import { createApp } from "../src/main.azure";
4 |
5 | export default function(context: Context, req: HttpRequest): void {
6 | context.res = {
7 | headers: {
8 | "Content-Type": "application/json"
9 | }
10 | };
11 | AzureHttpAdapter.handle(createApp, context, req);
12 | }
13 |
--------------------------------------------------------------------------------
/main/sample.dat:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Azure"
3 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nestjs-graphql-azure-functions",
3 | "version": "1.0.0",
4 | "description": "Sample nestjs app with graphql deployed to an azure function app",
5 | "author": "Andrew Moss",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/agmoss/nestjs-graphql-azure-functions.git"
10 | },
11 | "keywords": [
12 | "nestjs",
13 | "graphql",
14 | "azure-functions",
15 | "serverless"
16 | ],
17 | "scripts": {
18 | "prebuild": "rimraf dist",
19 | "build": "rimraf dist && tsc -p tsconfig.build.json",
20 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
21 | "start": "nest start",
22 | "start:dev": "nest start --watch",
23 | "start:debug": "nest start --debug --watch",
24 | "start:prod": "node dist/main",
25 | "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix",
26 | "test": "jest",
27 | "test:watch": "jest --watch",
28 | "test:cov": "jest --coverage",
29 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
30 | "test:e2e": "echo 'No e2e tests implemented yet.'",
31 | "start:azure": "npm run build && func host start"
32 | },
33 | "dependencies": {
34 | "@azure/functions": "^1.0.3",
35 | "@nestjs/azure-func-http": "^0.4.2",
36 | "@nestjs/common": "6.11.8",
37 | "@nestjs/core": "6.11.8",
38 | "@nestjs/graphql": "6.5.5",
39 | "@nestjs/platform-express": "6.11.8",
40 | "apollo-server-express": "2.10.1",
41 | "class-transformer": "0.2.3",
42 | "class-validator": "0.11.0",
43 | "graphql": "14.6.0",
44 | "graphql-subscriptions": "1.1.0",
45 | "reflect-metadata": "0.1.13",
46 | "rxjs": "6.5.4",
47 | "type-graphql": "0.17.6"
48 | },
49 | "devDependencies": {
50 | "@nestjs/cli": "6.14.2",
51 | "@nestjs/schematics": "6.9.4",
52 | "@nestjs/testing": "6.11.8",
53 | "@types/express": "4.17.2",
54 | "@types/node": "10.17.3",
55 | "@types/supertest": "2.0.8",
56 | "jest": "25.1.0",
57 | "prettier": "1.19.1",
58 | "supertest": "4.0.2",
59 | "ts-jest": "25.2.1",
60 | "ts-loader": "6.2.1",
61 | "ts-node": "8.6.2",
62 | "tsconfig-paths": "3.9.0",
63 | "@typescript-eslint/eslint-plugin": "2.20.0",
64 | "@typescript-eslint/parser": "2.20.0",
65 | "eslint": "6.8.0",
66 | "eslint-config-prettier": "6.10.0",
67 | "eslint-plugin-import": "2.20.1",
68 | "typescript": "3.7.2"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/proxies.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/proxies",
3 | "proxies": {}
4 | }
5 |
--------------------------------------------------------------------------------
/schema.gql:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------
2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
4 | # -----------------------------------------------
5 |
6 | """Date custom scalar type"""
7 | scalar Date
8 |
9 | type Mutation {
10 | addRecipe(newRecipeData: NewRecipeInput!): Recipe!
11 | removeRecipe(id: String!): Boolean!
12 | }
13 |
14 | input NewRecipeInput {
15 | title: String!
16 | description: String
17 | ingredients: [String!]!
18 | }
19 |
20 | type Query {
21 | recipe(id: String!): Recipe!
22 | recipes(skip: Int = 0, take: Int = 25): [Recipe!]!
23 | }
24 |
25 | type Recipe {
26 | id: ID!
27 | title: String!
28 | description: String
29 | creationDate: Date!
30 | ingredients: [String!]!
31 | }
32 |
33 | type Subscription {
34 | recipeAdded: Recipe!
35 | }
36 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { GraphQLModule } from "@nestjs/graphql";
3 | import { RecipesModule } from "./recipes/recipes.module";
4 |
5 | @Module({
6 | imports: [
7 | RecipesModule,
8 | GraphQLModule.forRoot({
9 | installSubscriptionHandlers: true,
10 | context: ({ req }) => ({ req }),
11 | autoSchemaFile: "schema.gql",
12 | useGlobalPrefix: true
13 | })
14 | ]
15 | })
16 | export class AppModule {}
17 |
--------------------------------------------------------------------------------
/src/common/scalars/date.scalar.ts:
--------------------------------------------------------------------------------
1 | import { CustomScalar, Scalar } from '@nestjs/graphql';
2 | import { Kind } from 'graphql';
3 |
4 | @Scalar('Date', type => Date)
5 | export class DateScalar implements CustomScalar {
6 | description = 'Date custom scalar type';
7 |
8 | parseValue(value: number): Date {
9 | return new Date(value); // value from the client
10 | }
11 |
12 | serialize(value: Date): number {
13 | return value.getTime(); // value sent to the client
14 | }
15 |
16 | parseLiteral(ast: any): Date {
17 | if (ast.kind === Kind.INT) {
18 | return new Date(ast.value);
19 | }
20 | return null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main.azure.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication, ValidationPipe } from "@nestjs/common";
2 | import { NestFactory } from "@nestjs/core";
3 | import { AppModule } from "./app.module";
4 |
5 | export async function createApp(): Promise {
6 | const app = await NestFactory.create(AppModule);
7 | app.useGlobalPipes(new ValidationPipe());
8 | app.enableCors();
9 | app.setGlobalPrefix("api");
10 | await app.init();
11 | return app;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { ValidationPipe } from "@nestjs/common";
2 | import { NestFactory } from "@nestjs/core";
3 | import { AppModule } from "./app.module";
4 |
5 | async function bootstrap() {
6 | const app = await NestFactory.create(AppModule);
7 | app.useGlobalPipes(new ValidationPipe());
8 | app.enableCors();
9 | app.setGlobalPrefix("api");
10 | await app.listen(3000);
11 | }
12 | bootstrap();
13 |
--------------------------------------------------------------------------------
/src/recipes/dto/new-recipe.input.ts:
--------------------------------------------------------------------------------
1 | import { IsOptional, Length, MaxLength } from 'class-validator';
2 | import { Field, InputType } from 'type-graphql';
3 |
4 | @InputType()
5 | export class NewRecipeInput {
6 | @Field()
7 | @MaxLength(30)
8 | title: string;
9 |
10 | @Field({ nullable: true })
11 | @IsOptional()
12 | @Length(30, 255)
13 | description?: string;
14 |
15 | @Field(type => [String])
16 | ingredients: string[];
17 | }
18 |
--------------------------------------------------------------------------------
/src/recipes/dto/recipes.args.ts:
--------------------------------------------------------------------------------
1 | import { Max, Min } from 'class-validator';
2 | import { ArgsType, Field, Int } from 'type-graphql';
3 |
4 | @ArgsType()
5 | export class RecipesArgs {
6 | @Field(type => Int)
7 | @Min(0)
8 | skip: number = 0;
9 |
10 | @Field(type => Int)
11 | @Min(1)
12 | @Max(50)
13 | take: number = 25;
14 | }
15 |
--------------------------------------------------------------------------------
/src/recipes/models/recipe.ts:
--------------------------------------------------------------------------------
1 | import { Field, ID, ObjectType } from 'type-graphql';
2 |
3 | @ObjectType()
4 | export class Recipe {
5 | @Field(type => ID)
6 | id: string;
7 |
8 | @Field()
9 | title: string;
10 |
11 | @Field({ nullable: true })
12 | description?: string;
13 |
14 | @Field()
15 | creationDate: Date;
16 |
17 | @Field(type => [String])
18 | ingredients: string[];
19 | }
20 |
--------------------------------------------------------------------------------
/src/recipes/recipes.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DateScalar } from '../common/scalars/date.scalar';
3 | import { RecipesResolver } from './recipes.resolver';
4 | import { RecipesService } from './recipes.service';
5 |
6 | @Module({
7 | providers: [RecipesResolver, RecipesService, DateScalar],
8 | })
9 | export class RecipesModule {}
10 |
--------------------------------------------------------------------------------
/src/recipes/recipes.resolver.ts:
--------------------------------------------------------------------------------
1 | import { NotFoundException } from '@nestjs/common';
2 | import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql';
3 | import { PubSub } from 'apollo-server-express';
4 | import { NewRecipeInput } from './dto/new-recipe.input';
5 | import { RecipesArgs } from './dto/recipes.args';
6 | import { Recipe } from './models/recipe';
7 | import { RecipesService } from './recipes.service';
8 |
9 | const pubSub = new PubSub();
10 |
11 | @Resolver(of => Recipe)
12 | export class RecipesResolver {
13 | constructor(private readonly recipesService: RecipesService) {}
14 |
15 | @Query(returns => Recipe)
16 | async recipe(@Args('id') id: string): Promise {
17 | const recipe = await this.recipesService.findOneById(id);
18 | if (!recipe) {
19 | throw new NotFoundException(id);
20 | }
21 | return recipe;
22 | }
23 |
24 | @Query(returns => [Recipe])
25 | recipes(@Args() recipesArgs: RecipesArgs): Promise {
26 | return this.recipesService.findAll(recipesArgs);
27 | }
28 |
29 | @Mutation(returns => Recipe)
30 | async addRecipe(
31 | @Args('newRecipeData') newRecipeData: NewRecipeInput,
32 | ): Promise {
33 | const recipe = await this.recipesService.create(newRecipeData);
34 | pubSub.publish('recipeAdded', { recipeAdded: recipe });
35 | return recipe;
36 | }
37 |
38 | @Mutation(returns => Boolean)
39 | async removeRecipe(@Args('id') id: string) {
40 | return this.recipesService.remove(id);
41 | }
42 |
43 | @Subscription(returns => Recipe)
44 | recipeAdded() {
45 | return pubSub.asyncIterator('recipeAdded');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/recipes/recipes.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { NewRecipeInput } from "./dto/new-recipe.input";
3 | import { RecipesArgs } from "./dto/recipes.args";
4 | import { Recipe } from "./models/recipe";
5 |
6 | @Injectable()
7 | export class RecipesService {
8 | /**
9 | * MOCK
10 | * Put some real business logic here
11 | * Left for demonstration purposes
12 | */
13 |
14 | async create(data: NewRecipeInput): Promise {
15 | return {
16 | id: "sample",
17 | title: data.title,
18 | description: data.description,
19 | creationDate: new Date(),
20 | ingredients: data.ingredients
21 | } as Recipe;
22 | }
23 |
24 | async findOneById(id: string): Promise {
25 | return {} as any;
26 | }
27 |
28 | async findAll(recipesArgs: RecipesArgs): Promise {
29 | return [] as Recipe[];
30 | }
31 |
32 | async remove(id: string): Promise {
33 | return true;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/template.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agmoss/nestjs-graphql-azure-functions/5939b3791edb7ca0f73ac310405ce7e10d4cb891/template.zip
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "allowSyntheticDefaultImports": true,
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true
15 | },
16 | "exclude": ["node_modules", "dist"]
17 | }
18 |
--------------------------------------------------------------------------------