Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,50 @@ npm install @as-integrations/google-cloud-functions @google-cloud/functions/fram
```

## Usage
In the root of your project, create an Apollo Server instance and pass it to `startServerAndCreateGoogleCloudFunctionsHandler`, imported from `@as-integrations/google-cloud-functions`:
In the root of your project, create an Apollo Server instance and pass it to `startServer`, imported from `@as-integrations/google-cloud-functions`. This will start a single instance of the Apollo Server once per container.

Google Cloud Functions requires you to name the function entry point. In this example, we name it `apollo-graphql`. Take note the the name you give to the function is the name you will use when deploying to Google Cloud Functions:
Afterwards, define and export a named `async` function that will be your Cloud Function handler, and use `requestProxy` and `responseProxy` to compose your function.

```ts
import { ApolloServer } from "@apollo/server";
import { startServerAndCreateGoogleCloudFunctionsHandler } from "@as-integrations/google-cloud-functions";
```typescript
import { ApolloServer } from '@apollo/server';
import { requestProxy, responseProxy, startServer } from '@as-integrations/google-cloud-functions';
import { gql } from 'graphql-tag';

import type { Request, Response } from '@google-cloud/functions-framework';

const resolvers = {
Query: {
hello: () => 'world',
},
};


const typeDefs = gql`
type Query {
hello: String
}
`;

const server = new ApolloServer({
resolvers,
typeDefs,
const apolloServer = new ApolloServer({
schema,
});

startServerAndCreateGoogleCloudFunctionsHandler(server, { functionTarget: "apollo-graphql" });
const server = startServer(apolloServer, {
context: async (req, res) => {
return { req, res }
}
});

export async function handler(req: Request, res: Response) {
const graphQLReponse = await requestProxy({ req, res, server });
await responseProxy(res, graphQLReponse);
}
```

## Example project
To develop, test and deploy your function, you will need to setup proper tooling to bundle your function and its dependencies.
To develop, test and deploy your function, you will need to setup proper tooling to compile your function and its dependencies.

We highly recommend taking a look at the [the project example](/example), which gives you an good starting point and sane defaults on how you can correctly bundle your function using `esbuild` and setup scripts for common development tasks.
We highly recommend taking a look at the [the project example](/example), which gives you an good starting point and sane defaults on how you can correctly bundle your function and setup scripts for common development tasks.

>**Note**
> ### Why do I need to bundle my function?
> You're probably writing your function in TypeScript, and you're probably using modern syntax from ES Modules like `import` and `export`. Google Cloud Functions Framework for Node.js does not support TypeScript, and it does not understand ES Modules.
>
> Futhermore, Google Cloud Functions works by having an entry point signature supplied to the function handler. This means that the final bundle of code that gets uploaded to Google Cloud Functions needs to visibly have the function entry point, otherwise it will fail with the error: `Function <function-name> is not defined in the provided module...`.


3 changes: 0 additions & 3 deletions example/.env.example

This file was deleted.

1 change: 0 additions & 1 deletion example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ dist
# Environment variables
.env

package-lock.json
38 changes: 9 additions & 29 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Boilerplate for Apollo Server projects using `@as-integrations/google-cloud-functions` for Google Cloud Function deployments
# Example project for Apollo Server using `@as-integrations/google-cloud-functions` for Google Cloud Function deployments

This example project comes pre-configured with sane defaults for tooling that you can use to deploy Apollo Server on Google Cloud Functions.

Expand All @@ -10,31 +10,19 @@ Install dependencies with the package manager of your choice:
npm install
```

Copy the `.env.example` file and set the environment variables:
Build your project:

```bash
# Copy the example .env file to your local .env
cp .env.example .env
npm build
```

Edit the `FUNCTION_TARGET` on the `.env` file to match the name of your function. This is used to name both your function entry point on the bundled output, and the deployment.

Start and test the development server:

On a terminal instance, start the bundler on watch mode for your project:
```bash
npm run watch
```

On another terminal instance, start the Google Cloud Functions Framework CLI tool:
Test your project on the Google Cloud Framework development server:
```bash
npm run start
```

> **Warning**
> While the bundler can watch for file changes on your project and rebuild accordingly, Google Cloud Functions Framework unfortunately doesn't support hot reloading. You will need to restart the CLI tool every time you make a change to the project.

The start script will build the project into a bundle usable by Google Cloud Functions and start a local development server using the `@google-cloud/functions-framework` package provided for Node.js runtimes. The server will be available at `http://localhost:8080`, and will run with an Apollo Server Playground where you can test your queries.
This will build the project into a bundle usable by Google Cloud Functions and start a local development server using the `@google-cloud/functions-framework` package provided for Node.js runtimes. The server will be available at `http://localhost:8080`, and will run with an Apollo Server Playground where you can test your queries.

## Deployment

Expand All @@ -49,9 +37,7 @@ The project is configured to be deployed to Google Cloud Functions trough the Go
npm run deploy
```

To use the provided deploy script, you need to have the Google Cloud CLI Tool installed and configured on your machine. You can find more information about the CLI Tool [here](https://cloud.google.com/sdk/gcloud).

The script will use the bundled output from the `/dist` directory, and name your function using the provided string on the `FUNCTION_TARGET` environment variable.
To use the provided deploy script, you need to have the Google Cloud CLI Tool installed and configured on your machine. You can find more information about the CLI Tool [here](https://cloud.google.com/sdk/gcloud). The script will use the bundled output from the `/dist` directory.

> **Note**
> The deploy script currently uses POSIX compliant commands, and **will not work on Windows** (I encourage you to file an issue and/or open a pull request if you can help me solve this). If you're using Windows, you can use the `gcloud` CLI tool directly to deploy your function, and change the correct flags accordingly.
Expand All @@ -62,20 +48,14 @@ The script will use the bundled output from the `/dist` directory, and name your
> gcloud functions deploy <function-name> --runtime nodejs16 --trigger-http --allow-unauthenticated --entry-point=<function-name> --source=./dist
> ```
>
> Beware that the `--entry-point` flag should match the name provided in the `FUNCTION_TARGET` environment variable.
> Beware that the `--entry-point` flag should match the name of the function handler exported on the main file of the server application.

## FAQ

### I got an error `Expected value for define "process.env.FUNCTION_TARGET" to be a string, got undefined instead` when trying to build my function:

This error is caused by the absence of the `FUNCTION_TARGET` environment variable. Make sure you have the `FUNCTION_TARGET` environment variable set on your `.env`, or otherwise, supplied to your running Node process.

### I got an error `Provided code is not a loadable module` when trying to run the `start` command:

You'll get this error when there is no bundled output on the `/dist` directory. Make sure to run the `build` or `watch` command before running the `start` command.
You'll get this error when there is no bundled output on the `/dist` directory. Make sure to run the `build` command before running the `start` command.

### I got an error `Function <function-name> is not defined in the provided module...` when trying to run/deploy my function:

This will usually happen when the bundled code does not include `@as-integrations/google-cloud-functions` source code on the final bundle. **It is important to have the source-code for the integration bundled *within* your function** because of how Google Cloud Functions reads the function entry point.

In `scripts/buildConfig.mjs`, make sure that the `external` array **does not** include `@as-integrations/google-cloud-functions`. Every other direct dependency that can be normally resolved by package.json should be included on the `external` array.
The value of `--target` and `--entry-point` flags, on the `start` and `deploy` scripts, respective, should match the name of the function you export from the main file of your app. By default it is named `handler`.
20 changes: 9 additions & 11 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@
"with-env": "dotenv -e .env --"
},
"dependencies": {
"@apollo/server": "^4.3.0",
"@apollo/utils.withrequired": "^2.0.0",
"@as-integrations/google-cloud-functions": "file:..",
"@google-cloud/functions-framework": "^3.1.3",
"graphql": "^16.6.0"
"@apollo/server": "^4.7.1",
"@as-integrations/google-cloud-functions": "file:../",
"@pothos/core": "^3.30.0",
"graphql": "^16.6.0",
"graphql-scalars": "^1.21.3"
},
"devDependencies": {
"@types/node": "18.11.18",
"dotenv-cli": "6.0.0",
"esbuild": "0.17.5",
"rimraf": "3.0.2",
"ts-node": "10.9.1",
"typescript": "4.9.4"
"@google-cloud/functions-framework": "^3.2.0",
"@types/node": "20.1.4",
"rimraf": "5.0.0",
"typescript": "5.0.4"
}
}
Loading