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.
The one source of reliable information on this topic is a blog post by trilon.io here. 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.
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!
I started this repo with the boilerplate from 23-type-graphql. This is a working repo with Typescript, GraphQL, and Nest but NOT nest/azure-func-http
$ nest add @nestjs/azure-func-http
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.
package.json
- "build": "nest build"
+ "build": "rimraf dist && tsc -p tsconfig.build.json"
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true
},
- "include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
These two steps create seperate /src
and /main
directories in /dist
.
/src
is for your source code/main
is the entry point for the function appAt 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.
main.ts
import { ValidationPipe } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
+ app.enableCors();
+ app.setGlobalPrefix("api");
await app.listen(3000);
}
bootstrap();
app.module.ts
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";
import { RecipesModule } from "./recipes/recipes.module";
@Module({
imports: [
RecipesModule,
GraphQLModule.forRoot({
installSubscriptionHandlers: true,
+ context: ({ req }) => ({ req }),
autoSchemaFile: "schema.gql",
+ useGlobalPrefix: true
})
]
})
export class AppModule {}
host.json
{
"version": "2.0",
+ "extensions": {
+ "http": {
+ "routePrefix": "api"
+ }
}
}
index.ts
import { Context, HttpRequest } from "@azure/functions";
import { AzureHttpAdapter } from "@nestjs/azure-func-http";
import { createApp } from "../src/main.azure";
export default function(context: Context, req: HttpRequest): void {
+ context.res = {
+ headers: {
+ "Content-Type": "application/json"
+ }
+ };
AzureHttpAdapter.handle(createApp, context, req);
}
$ npm run build && func host start
Add a sample body to the create method in recipies.service.ts
for testing.
recipies.service.ts
async create(data: NewRecipeInput): Promise<Recipe> {
+ return {
+ id: "sample",
+ title: data.title,
+ description: data.description,
+ creationDate: new Date(),
+ ingredients: data.ingredients
+ } as Recipe;
- return {} as any;
}
fire up http://localhost:7071/api/graphql and run a mutation
mutation($newRecipeData: NewRecipeInput!) {
addRecipe(newRecipeData: $newRecipeData) {
creationDate
}
}
query variables
{
"newRecipeData": {
"title": "Salad",
"description": "Im trying to be healthy and im disappointed in my self",
"ingredients": ["leaves", "sadness"]
}
}
you should get back something like....
{
"data": {
"addRecipe": {
"creationDate": 1582340836192
}
}
}
The battle has been won but the war has just begun
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.
Basics
Hosting
Upon completing the Basics and Hosting, press Review + create
to deploy the function app.
Once created, head back to your editor and deploy your Nest Graphql App to this newly created Function App.
$ npm run build && func azure functionapp publish <APP_NAME>
This will create an output similar to the following:
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
Wait a few minutes for the application to propagate then head to the deployed graphql endpoint: https://<APP_NAME>.azurewebsites.net/api/graphql
You should then be treated to the graphql playground.
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
Documentation on how to changes these Azure related setting can be found here
If you are using vscode there is some helpful extensions for deploying function apps. Your can read about that here
Included in this repo is a template.zip
. This contains the templated deployment parameters from the Deploying to Azure Functions
section of this article.