mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
add hello-world remote-schema boilerplates for major serverless providers (#1694)
A hello world schema is provided with local dev and deployment instructions for AWS Lambda, Google Cloud Functions and Azure Functions. Older boilerplates are cleaned up.
This commit is contained in:
parent
98405fdc0c
commit
d6a3cf337f
@ -1 +0,0 @@
|
||||
Moved to [zeit-now](../zeit-now)
|
@ -1,3 +1,3 @@
|
||||
# (MOVED) GraphQL server using NodeJS and Apollo
|
||||
|
||||
**This folder has been moved to [remote-schemas/zeit-now/nodejs/apollo-sequelize](../../remote-schemas/zeit-now/nodejs/apollo-sequelize)**
|
||||
**This folder has been moved to [remote-schemas/aws-lambda/nodejs](../../remote-schemas/aws-lambda/nodejs)**
|
||||
|
@ -1,3 +1,3 @@
|
||||
# (MOVED) GraphQL server using NodeJS-Express
|
||||
|
||||
**This folder has been moved to [remote-schemas/zeit-now/nodejs/express-graphqljs-sequelize](../../remote-schemas/zeit-now/nodejs/express-graphqljs-sequelize)**
|
||||
**This folder has been moved to [remote-schemas/aws-lambda/nodejs](../../remote-schemas/aws-lambda/nodejs)**
|
||||
|
3
community/boilerplates/remote-schemas/aws-lambda/nodejs/.gitignore
vendored
Normal file
3
community/boilerplates/remote-schemas/aws-lambda/nodejs/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
*.zip
|
||||
package-lock.json
|
@ -0,0 +1,76 @@
|
||||
# AWS Lambda + NodeJS + Apollo
|
||||
|
||||
This is a GraphQL backend boilerplate in nodejs that can be deployed on AWS Lambda.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
AWS Lambda
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Apollo Server (GraphQL framework)
|
||||
|
||||
## Schema
|
||||
|
||||
|
||||
```
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `index.js`.
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/aws-lambda/nodejs
|
||||
```
|
||||
|
||||
Start a local development server (you may need to install dependencies from npm):
|
||||
|
||||
```bash
|
||||
$ npm i --no-save apollo-server express
|
||||
$ node localDev.js
|
||||
|
||||
Output:
|
||||
|
||||
Server ready at http://localhost:4000/
|
||||
```
|
||||
|
||||
This will start a local server on `localhost:4000`. You can hit the graphql service at `localhost:4000`. This opens a graphql playground where you can query your schema.
|
||||
|
||||
## Deployment
|
||||
|
||||
Now that you have run the graphql service locally and made any required changes, it's time to deploy your service to AWS Lambda and get an endpoint. The easiest way to do this is through the AWS console.
|
||||
|
||||
1) Create a Lambda function by clicking on Create Function on your Lambda console. Choose the `NodeJS 8.10` runtime and `lambda_basic_execution` role.
|
||||
|
||||
![create-lambda](../../_assets/create-lambda.png)
|
||||
|
||||
2) In the next page (or Lambda instance page), select API Gateway as the trigger.
|
||||
|
||||
![create-api-gateway](../../_assets/create-api-gateway.png)
|
||||
|
||||
3) Configure the API Gateway as you wish. The simplest configuration is shown below.
|
||||
|
||||
![configure-api-gateway](../../_assets/configure-api-gateway.png)
|
||||
|
||||
Save your changes. You will receive a HTTPS endpoint for your lambda.
|
||||
|
||||
![output-api-gateway](../../_assets/output-api-gateway.png)
|
||||
|
||||
If you go to the endpoint, you will receive a "Hello from Lambda!" message. This is because we haven't uploaded any code yet!
|
||||
|
||||
4) Zip and upload code. Make sure the handler is set as `lambdaCtx.handler`:
|
||||
|
||||
```bash
|
||||
$ zip -r graphql.zip *
|
||||
```
|
||||
|
||||
And that's it. Hit save and visit the endpoint again. You will see the graphql playground again.
|
||||
|
||||
**IMPORTANT NOTE:** You may have to edit the GraphQL URL in the Playground to reflect the right endpoint ( same as the URL created by the API Gateway ).
|
@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
build
|
||||
*.zip
|
@ -1,104 +0,0 @@
|
||||
# AWS Lambda + NodeJS + Apollo + Sequelize
|
||||
|
||||
This is a GraphQL backend boilerplate in nodejs that can be deployed on AWS Lambda.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
Postgres
|
||||
|
||||
AWS Lambda
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Apollo Server (GraphQL framework)
|
||||
|
||||
Sequelize (Postgres ORM)
|
||||
|
||||
## Schema
|
||||
|
||||
We consider a user schema where a user can be added only if a custom validation passes. The custom validation involves fetching a min amount from a table and checking if the user balance is greater than the min amount. This will be done in a transaction.
|
||||
|
||||
```
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
validateAndAddUser(name: String, balance: Int): User
|
||||
}
|
||||
|
||||
type User {
|
||||
id: Int
|
||||
name: String
|
||||
balance: Int
|
||||
}
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `index.js`. Clone the repo and go to `community/boilerplates/remote-schemas/aws-lambda/nodejs/apollo-sequelize` folder:
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/aws-lambda/nodejs/apollo-sequelize
|
||||
```
|
||||
|
||||
1) First, let's set the environment variable for connecting to the postgres instance. This can be a local postgres instance or some managed postgres instance like AWS RDS.
|
||||
|
||||
```bash
|
||||
$ export POSTGRES_CONNECTION_STRING='postgres://username:password@rds-database-endpoint.us-east-1.rds.amazonaws.com:5432/mydb'
|
||||
```
|
||||
|
||||
2) Next, lets create the tables required for our schema.
|
||||
|
||||
```bash
|
||||
psql $POSTGRES_CONNECTION_STRING -c "create table users(id serial primary key, name text, balance integer); create table min_amount(amount integer); insert into min_amount values (100)"
|
||||
```
|
||||
|
||||
3) Now, you can start a development environment by setting an environment variable and running the server:
|
||||
|
||||
```bash
|
||||
$ LAMBDA_LOCAL_DEVELOPMENT=1 node index.js
|
||||
|
||||
Output:
|
||||
|
||||
Server ready at http://localhost:4000/
|
||||
```
|
||||
|
||||
This will start a local server on `localhost:4000`. You can hit the graphql service at `localhost:4000`. This opens a graphql playground where you can query your schema.
|
||||
|
||||
Now, you can play with the schema and make any changes in the source code for additional functionalities as you desire.
|
||||
|
||||
## Deployment
|
||||
|
||||
Now that you have run the graphql service locally and made any required changes, it's time to deploy your service to AWS Lambda and get an endpoint. The easiest way to do this is through the AWS console.
|
||||
|
||||
1) Create a Lambda function by clicking on Create Function on your Lambda console. Choose the `NodeJS 8.10` runtime and `lambda_basic_execution` role.
|
||||
|
||||
![create-lambda](../../../_assets/create-lambda.png)
|
||||
|
||||
2) In the next page (or Lambda instance page), select API Gateway as the trigger.
|
||||
|
||||
![create-api-gateway](../../../_assets/create-api-gateway.png)
|
||||
|
||||
3) Configure the API Gateway as you wish. The simplest configuration is shown below.
|
||||
|
||||
![configure-api-gateway](../../../_assets/configure-api-gateway.png)
|
||||
|
||||
Save your changes. You will receive a HTTPS endpoint for your lambda.
|
||||
|
||||
![output-api-gateway](../../../_assets/output-api-gateway.png)
|
||||
|
||||
If you go to the endpoint, you will receive a "Hello from Lambda!" message. This is because we haven't uploaded any code yet!
|
||||
|
||||
4) Zip and upload code. Make sure to set the handler as `index.handler` and add the `POSTGRES_CONNECTION_STRING` environment variable:
|
||||
|
||||
```bash
|
||||
$ zip -r graphql.zip *
|
||||
```
|
||||
|
||||
And that's it. Hit save and visit the endpoint again. You will see the graphql playground again.
|
||||
|
||||
**IMPORTANT NOTE:** You may have to edit the GraphQL URL in the Playground to reflect the right endpoint ( same as the URL created by the API Gateway ).
|
@ -1,85 +0,0 @@
|
||||
const { ApolloServer, gql } = require('apollo-server');
|
||||
const ApolloServerLambda = require('apollo-server-lambda').ApolloServer;
|
||||
const Sequelize = require("sequelize");
|
||||
const {User, MinAmount, sequelize} = require('./models.js');
|
||||
|
||||
const typeDefs = gql`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
validateAndAddUser(name: String, balance: Int): User
|
||||
}
|
||||
|
||||
type User {
|
||||
id: Int
|
||||
name: String
|
||||
balance: Int
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
// We consider a user schema where a user can be added only if a custom validation passes.
|
||||
// The custom validation involves fetching a min amount from a table
|
||||
// and checking if the user balance is greater than the min amount.
|
||||
// This will be done in a transaction.
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
},
|
||||
Mutation: {
|
||||
validateAndAddUser: async (_, { name, balance }) => {
|
||||
//begin transaction
|
||||
return await sequelize.transaction(async (t) => {
|
||||
try {
|
||||
//fetch min amount
|
||||
const minAmount = await MinAmount.findOne({}, {transaction: t});
|
||||
//check balance
|
||||
if (balance >= minAmount.amount) {
|
||||
//create user if balance is greater
|
||||
const user = await User.create({
|
||||
name: name,
|
||||
balance: balance
|
||||
});
|
||||
return user;
|
||||
} else {
|
||||
throw new Error("balance too low, required atleast " + minAmount.amount);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const server = new ApolloServerLambda({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: ({ event, context }) => ({
|
||||
headers: event.headers,
|
||||
functionName: context.functionName,
|
||||
event,
|
||||
context,
|
||||
}),
|
||||
});
|
||||
|
||||
exports.handler = server.createHandler({
|
||||
cors: {
|
||||
origin: '*',
|
||||
credentials: true,
|
||||
allowedHeaders: 'Content-Type, Authorization'
|
||||
},
|
||||
});
|
||||
|
||||
// For local development
|
||||
if( process.env.LAMBDA_LOCAL_DEVELOPMENT == "1") {
|
||||
const serverLocal = new ApolloServer({ typeDefs, resolvers });
|
||||
|
||||
serverLocal.listen().then(({ url }) => {
|
||||
console.log(`Server ready at ${url}`);
|
||||
});
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
const Sequelize = require("sequelize");
|
||||
|
||||
const POSTGRES_CONNECTION_STRING = process.env.POSTGRES_CONNECTION_STRING || "postgres://postgres:password@localhost:6432/postgres";
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
POSTGRES_CONNECTION_STRING, {}
|
||||
);
|
||||
|
||||
const User = sequelize.define(
|
||||
'user',
|
||||
{
|
||||
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
name: Sequelize.TEXT,
|
||||
balance: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
const MinAmount = sequelize.define(
|
||||
'min_amount',
|
||||
{
|
||||
amount: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
MinAmount.removeAttribute('id');
|
||||
|
||||
exports.sequelize = sequelize;
|
||||
exports.User = User;
|
||||
exports.MinAmount = MinAmount;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
||||
const gql = require('graphql-tag');
|
||||
|
||||
const typeDefs = gql`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
},
|
||||
};
|
||||
|
||||
exports.typeDefs = typeDefs;
|
||||
exports.resolvers = resolvers;
|
@ -0,0 +1,22 @@
|
||||
const ApolloServerLambda = require('apollo-server-lambda').ApolloServer;
|
||||
const { typeDefs, resolvers } = require('./index');
|
||||
|
||||
const server = new ApolloServerLambda({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: ({ event, context }) => ({
|
||||
headers: event.headers,
|
||||
functionName: context.functionName,
|
||||
event,
|
||||
context,
|
||||
}),
|
||||
});
|
||||
|
||||
exports.handler = server.createHandler({
|
||||
cors: {
|
||||
origin: '*',
|
||||
credentials: true,
|
||||
allowedHeaders: 'Content-Type, Authorization'
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,11 @@
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
const { typeDefs, resolvers } = require('./index');
|
||||
|
||||
const helloSchema = new ApolloServer({ typeDefs, resolvers });
|
||||
|
||||
helloSchema.listen().then(({ url }) => {
|
||||
console.log(`schema ready at ${url}`);
|
||||
});
|
@ -2,7 +2,7 @@
|
||||
"name": "aws-lambda-nodejs",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "graphql.js",
|
||||
"main": "lambdaCtx.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
@ -10,11 +10,8 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"apollo-server": "^2.2.2",
|
||||
"apollo-server-lambda": "^2.1.0",
|
||||
"graphql": "^0.13.1",
|
||||
"pg": "^7.6.0",
|
||||
"pg-hstore": "^2.3.2",
|
||||
"sequelize": "^4.41.0"
|
||||
"graphql-tag": "^2.10.1"
|
||||
}
|
||||
}
|
3
community/boilerplates/remote-schemas/azure-functions/nodejs/.gitignore
vendored
Normal file
3
community/boilerplates/remote-schemas/azure-functions/nodejs/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
*.zip
|
||||
package-lock.json
|
@ -0,0 +1,83 @@
|
||||
# Azure Functions + NodeJS + Apollo
|
||||
|
||||
This is a GraphQL backend boilerplate in nodejs that can be deployed on Azure Functions.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
Azure Functions
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Apollo Server (GraphQL framework)
|
||||
|
||||
## Schema
|
||||
|
||||
|
||||
```
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `graphql` folder.
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/azure-functions/nodejs
|
||||
```
|
||||
|
||||
Start a local development server (you may need to install dependencies from npm):
|
||||
|
||||
```bash
|
||||
$ cd graphql
|
||||
$ npm i --no-save apollo-server express
|
||||
$ node localDev.js
|
||||
|
||||
Output:
|
||||
|
||||
Server ready at http://localhost:4000/
|
||||
```
|
||||
|
||||
This will start a local server on `localhost:4000`. You can hit the graphql service at `localhost:4000`. This opens a graphql playground where you can query your schema.
|
||||
|
||||
## Deployment
|
||||
|
||||
1. Install `az` cli.
|
||||
|
||||
2. Create a zip of all resources (from the current directory):
|
||||
|
||||
```bash
|
||||
$ zip -r graphql.zip *
|
||||
```
|
||||
|
||||
2. Run the following commands to deploy:
|
||||
|
||||
```bash
|
||||
$ az group create --name 'my-functions-group' --location southindia
|
||||
|
||||
$ az storage account create --name 'myfunctionsstorage' --location southindia --resource-group 'my-functions-group' --sku Standard_LRS
|
||||
|
||||
$ az functionapp create --name 'hello-graphql' --storage-account 'myfunctionsstorage' --resource-group 'my-functions-group' --consumption-plan-location southindia
|
||||
|
||||
$ az functionapp deployment source config-zip -g "my-functions-group" -n "hello-graphql" --src graphql.zip
|
||||
|
||||
```
|
||||
|
||||
4. Get the HTTP Url:
|
||||
|
||||
```bash
|
||||
$ az functionapp show -g "my-functions-group" -n "hello-graphql"
|
||||
```
|
||||
Output:
|
||||
```json
|
||||
"hostNames": [
|
||||
"hello-graphql.azurewebsites.net"
|
||||
],
|
||||
```
|
||||
|
||||
The url of the function is \<hostname\>/\<function\> e.g. in our case: `https://hello-graphql.azurewebsites.net/graphql`
|
||||
|
3
community/boilerplates/remote-schemas/azure-functions/nodejs/graphql/.gitignore
vendored
Normal file
3
community/boilerplates/remote-schemas/azure-functions/nodejs/graphql/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
*.zip
|
||||
package-lock.json
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"disabled": false,
|
||||
"bindings": [
|
||||
{
|
||||
"authLevel": "anonymous",
|
||||
"type": "httpTrigger",
|
||||
"direction": "in",
|
||||
"name": "req",
|
||||
"methods": [
|
||||
"get",
|
||||
"post"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "http",
|
||||
"direction": "out",
|
||||
"name": "$return"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
const { typeDefs, resolvers } = require('./server');
|
||||
|
||||
const helloSchema = new ApolloServer({ typeDefs, resolvers });
|
||||
|
||||
helloSchema.listen().then(({ url }) => {
|
||||
console.log(`schema ready at ${url}`);
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "azure-functions-nodejs",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "run.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"apollo-server-azure-functions": "^2.4.8",
|
||||
"graphql": "^0.13.1",
|
||||
"graphql-tag": "^2.10.1"
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
const { ApolloServer } = require("apollo-server-azure-functions");
|
||||
const { typeDefs, resolvers } = require('./server');
|
||||
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
});
|
||||
|
||||
module.exports = server.createHandler({
|
||||
cors: {
|
||||
origin: '*',
|
||||
credentials: true,
|
||||
allowedHeaders: 'Content-Type, Authorization'
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,16 @@
|
||||
const gql = require('graphql-tag');
|
||||
|
||||
const typeDefs = gql`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
},
|
||||
};
|
||||
|
||||
exports.typeDefs = typeDefs;
|
||||
exports.resolvers = resolvers;
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": "2.0",
|
||||
"functions": [ "graphql" ],
|
||||
"functionTimeout": "00:00:30",
|
||||
"extensions": {
|
||||
"http": {
|
||||
"routePrefix": ""
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"fileLoggingMode": "always",
|
||||
"logLevel": {
|
||||
"default": "Information"
|
||||
},
|
||||
"console": {
|
||||
"isEnabled": "false"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# This file specifies files that are *not* uploaded to Google Cloud Platform
|
||||
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||
# file at that point).
|
||||
#
|
||||
# For more information, run:
|
||||
# $ gcloud topic gcloudignore
|
||||
#
|
||||
.gcloudignore
|
||||
# If you would like to upload your .git directory, .gitignore file or files
|
||||
# from your .gitignore file, remove the corresponding line
|
||||
# below:
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
node_modules
|
||||
#!include:.gitignore
|
3
community/boilerplates/remote-schemas/google-cloud-functions/nodejs/.gitignore
vendored
Normal file
3
community/boilerplates/remote-schemas/google-cloud-functions/nodejs/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
*.zip
|
||||
package-lock.json
|
@ -0,0 +1,62 @@
|
||||
# Google Cloud Functions + NodeJS + Apollo
|
||||
|
||||
This is a GraphQL backend boilerplate in nodejs that can be deployed on Google Cloud Functions.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
Google Cloud Functions
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Apollo Server (GraphQL framework)
|
||||
|
||||
## Schema
|
||||
|
||||
|
||||
```
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `index.js`.
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/google-cloud-functions/nodejs
|
||||
```
|
||||
|
||||
Start a local development server (you may need to install dependencies from npm):
|
||||
|
||||
```bash
|
||||
$ npm i --no-save apollo-server express
|
||||
$ node localDev.js
|
||||
|
||||
Output:
|
||||
|
||||
Server ready at http://localhost:4000/
|
||||
```
|
||||
|
||||
This will start a local server on `localhost:4000`. You can hit the graphql service at `localhost:4000`. This opens a graphql playground where you can query your schema.
|
||||
|
||||
## Deployment
|
||||
|
||||
1. Install `gcloud` cli.
|
||||
|
||||
2. From the current directory, run the following command to deploy the function:
|
||||
|
||||
```bash
|
||||
$ gcloud functions deploy hello-graphql --entry-point handler --runtime nodejs8 --trigger-http
|
||||
```
|
||||
|
||||
3. Get the trigger URL from the above output:
|
||||
|
||||
```yaml
|
||||
httpsTrigger:
|
||||
url: https://us-central1-hasura-test.cloudfunctions.net/hello-graphql
|
||||
```
|
||||
|
@ -0,0 +1,23 @@
|
||||
const { ApolloServer } = require("apollo-server-cloud-functions");
|
||||
const { typeDefs, resolvers } = require('./index');
|
||||
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
playground: true,
|
||||
introspection: true,
|
||||
context: ({ req, res }) => ({
|
||||
headers: req.headers,
|
||||
req,
|
||||
res,
|
||||
}),
|
||||
});
|
||||
|
||||
exports.handler = server.createHandler({
|
||||
cors: {
|
||||
origin: '*',
|
||||
credentials: true,
|
||||
allowedHeaders: 'Content-Type, Authorization'
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,16 @@
|
||||
const gql = require('graphql-tag');
|
||||
|
||||
const typeDefs = gql`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
},
|
||||
};
|
||||
|
||||
exports.typeDefs = typeDefs;
|
||||
exports.resolvers = resolvers;
|
@ -0,0 +1,11 @@
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
const { typeDefs, resolvers } = require('./index');
|
||||
|
||||
const helloSchema = new ApolloServer({ typeDefs, resolvers });
|
||||
|
||||
helloSchema.listen().then(({ url }) => {
|
||||
console.log(`schema ready at ${url}`);
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "google-cloud-functions-nodejs",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "googleCtx.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"apollo-server-cloud-functions": "^2.4.8",
|
||||
"graphql": "^0.13.1",
|
||||
"graphql-tag": "^2.10.1"
|
||||
}
|
||||
}
|
@ -1,43 +1,27 @@
|
||||
# Zeit + NodeJS + Apollo + Sequelize GraphQL boilerplate
|
||||
# Zeit (1.0) + NodeJS + Apollo
|
||||
|
||||
A boilerplate using nodejs and [Apollo Server](https://www.apollographql.com/docs/apollo-server/) that can be deployed on Zeit.
|
||||
A boilerplate using Nodejs and [Apollo Server](https://www.apollographql.com/docs/apollo-server/) that can be deployed on Zeit.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
Postgres
|
||||
|
||||
Zeit Now
|
||||
Zeit 1.0
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Apollo Server (GraphQL framework)
|
||||
|
||||
Sequelize (Postgres ORM)
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `server.js`. Clone the repo and go to `community/boilerplates/remote-schemas/zeit-now/nodejs/apollo-sequelize` folder:
|
||||
The sample source code is present in `server.js`.
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/zeit-now/nodejs/apollo-sequelize
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/zeit-now/nodejs
|
||||
```
|
||||
|
||||
1) First, let's set the environment variable for connecting to the postgres instance. This can be a local postgres instance or some managed postgres instance like AWS RDS.
|
||||
|
||||
```bash
|
||||
$ export POSTGRES_CONNECTION_STRING='postgres://username:password@rds-database-endpoint.us-east-1.rds.amazonaws.com:5432/mydb'
|
||||
```
|
||||
|
||||
2) Next, lets create the tables required for our schema.
|
||||
|
||||
```bash
|
||||
psql $POSTGRES_CONNECTION_STRING -c "create table users(id serial primary key, name text, balance integer); create table min_amount(amount integer); insert into min_amount values (100)"
|
||||
```
|
||||
|
||||
3) Now, you can run the server locally:
|
||||
Run the server locally:
|
||||
|
||||
```bash
|
||||
npm install
|
@ -1,37 +0,0 @@
|
||||
const Sequelize = require("sequelize");
|
||||
|
||||
const POSTGRES_CONNECTION_STRING = process.env.POSTGRES_CONNECTION_STRING || "postgres://postgres:password@localhost:6432/postgres";
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
POSTGRES_CONNECTION_STRING, {}
|
||||
);
|
||||
|
||||
const User = sequelize.define(
|
||||
'user',
|
||||
{
|
||||
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
name: Sequelize.TEXT,
|
||||
balance: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
const MinAmount = sequelize.define(
|
||||
'min_amount',
|
||||
{
|
||||
amount: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
MinAmount.removeAttribute('id');
|
||||
|
||||
exports.sequelize = sequelize;
|
||||
exports.User = User;
|
||||
exports.MinAmount = MinAmount;
|
||||
|
@ -1,71 +0,0 @@
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const { makeExecutableSchema } = require('graphql-tools');
|
||||
const Sequelize = require("sequelize");
|
||||
const {User, MinAmount, sequelize} = require('./models.js');
|
||||
|
||||
const port = process.env.PORT || 4000;
|
||||
|
||||
const typeDefs = `
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
validateAndAddUser(name: String, balance: Int): User
|
||||
}
|
||||
|
||||
type User {
|
||||
id: Int
|
||||
name: String
|
||||
balance: Int
|
||||
}
|
||||
`;
|
||||
|
||||
// We consider a user schema where a user can be added only if a custom validation passes.
|
||||
// The custom validation involves fetching a min amount from a table
|
||||
// and checking if the user balance is greater than the min amount.
|
||||
// This will be done in a transaction.
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
},
|
||||
Mutation: {
|
||||
validateAndAddUser: async (_, { name, balance }) => {
|
||||
//begin transaction
|
||||
return await sequelize.transaction(async (t) => {
|
||||
try {
|
||||
//fetch min amount
|
||||
const minAmount = await MinAmount.findOne({}, {transaction: t});
|
||||
//check balance
|
||||
if (balance >= minAmount.amount) {
|
||||
//create user if balance is greater
|
||||
const user = await User.create({
|
||||
name: name,
|
||||
balance: balance
|
||||
});
|
||||
return user;
|
||||
} else {
|
||||
throw new Error("balance too low, required atleast " + minAmount.amount);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const schema = makeExecutableSchema({
|
||||
typeDefs,
|
||||
resolvers
|
||||
});
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema
|
||||
});
|
||||
|
||||
server.listen({ port }).then(({url}) => {
|
||||
console.log(`GraphQL server running at ${url}`);
|
||||
});
|
@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
package-lock.json
|
@ -1,11 +0,0 @@
|
||||
FROM node:8
|
||||
|
||||
WORKDIR /server
|
||||
|
||||
COPY ./package.json /server/
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . /server/
|
||||
|
||||
CMD ["npm", "start"]
|
@ -1,80 +0,0 @@
|
||||
# Zeit + NodeJS + Express + GraphQL.js + Sequelize GraphQL boilerplate
|
||||
|
||||
A boilerplate using nodejs, express and [GraphQL.js](https://graphql.github.io/graphql-js/running-an-express-graphql-server/) that can be deployed on Zeit.
|
||||
|
||||
## Stack
|
||||
|
||||
node 8.10
|
||||
|
||||
Postgres
|
||||
|
||||
Zeit Now
|
||||
|
||||
#### Frameworks/Libraries
|
||||
|
||||
Express (NodeJS framework)
|
||||
|
||||
GraphQL.js (GraphQL framework)
|
||||
|
||||
Sequelize (Postgres ORM)
|
||||
|
||||
## Local Development
|
||||
|
||||
The sample source code is present in `server.js`. Clone the repo and go to `community/boilerplates/remote-schemas/zeit-now/nodejs/express-graphqljs-sequelize` folder:
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:hasura/graphql-engine
|
||||
$ cd graphql-engine/community/boilerplates/remote-schemas/zeit-now/nodejs/express-graphqljs-sequelize
|
||||
```
|
||||
|
||||
1) First, let's set the environment variable for connecting to the postgres instance. This can be a local postgres instance or some managed postgres instance like AWS RDS.
|
||||
|
||||
```bash
|
||||
$ export POSTGRES_CONNECTION_STRING='postgres://username:password@rds-database-endpoint.us-east-1.rds.amazonaws.com:5432/mydb'
|
||||
```
|
||||
|
||||
2) Next, lets create the tables required for our schema.
|
||||
|
||||
```bash
|
||||
psql $POSTGRES_CONNECTION_STRING -c "create table users(id serial primary key, name text, balance integer); create table min_amount(amount integer); insert into min_amount values (100)"
|
||||
```
|
||||
|
||||
3) Now, you can run the server locally:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Running the server using Docker:
|
||||
|
||||
```bash
|
||||
docker build -t graphql .
|
||||
docker run -p 4000:4000 graphql
|
||||
```
|
||||
|
||||
This will start a local server on `localhost:4000`. You can hit the graphql service at `localhost:4000/graphql` which opens GraphiQL.
|
||||
|
||||
## Deployment
|
||||
|
||||
Install the [Zeit Now](https://zeit.co/now) CLI:
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
```
|
||||
|
||||
Deploy the server:
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
Get the URL and make a sample query:
|
||||
```bash
|
||||
curl https://app-name-something.now.sh/graphql \
|
||||
-H 'Content-Type:application/json' \
|
||||
-d'{"query":"{ hello }"}'
|
||||
|
||||
{"data":{"hello":"Hello World!"}}
|
||||
```
|
||||
|
||||
You can also visit the now url to open GraphiQL.
|
@ -1,37 +0,0 @@
|
||||
const Sequelize = require("sequelize");
|
||||
|
||||
const POSTGRES_CONNECTION_STRING = process.env.POSTGRES_CONNECTION_STRING || "postgres://postgres:password@localhost:6432/postgres";
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
POSTGRES_CONNECTION_STRING, {}
|
||||
);
|
||||
|
||||
const User = sequelize.define(
|
||||
'user',
|
||||
{
|
||||
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
name: Sequelize.TEXT,
|
||||
balance: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
const MinAmount = sequelize.define(
|
||||
'min_amount',
|
||||
{
|
||||
amount: Sequelize.INTEGER
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
MinAmount.removeAttribute('id');
|
||||
|
||||
exports.sequelize = sequelize;
|
||||
exports.User = User;
|
||||
exports.MinAmount = MinAmount;
|
||||
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"version": 1
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "nodejs-express-gql-server",
|
||||
"version": "1.0.0",
|
||||
"description": "A GraphQL server boilerplate for NodeJS-Express using graphql-js library",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node -r esm server.js"
|
||||
},
|
||||
"author": "Hasura",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.16.4",
|
||||
"express-graphql": "^0.7.1",
|
||||
"graphql": "^14.0.2",
|
||||
"pg": "^7.7.1",
|
||||
"sequelize": "^4.42.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esm": "^3.0.84"
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
const express = require('express');
|
||||
const graphqlHTTP = require('express-graphql');
|
||||
const { buildSchema } = require('graphql');
|
||||
const Sequelize = require("sequelize");
|
||||
const {User, MinAmount, sequelize} = require('./models.js');
|
||||
|
||||
const port = process.env.port || 4000;
|
||||
|
||||
// Construct a schema, using GraphQL schema language
|
||||
const schema = buildSchema(`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
validateAndAddUser(name: String, balance: Int): User
|
||||
}
|
||||
|
||||
type User {
|
||||
id: Int
|
||||
name: String
|
||||
balance: Int
|
||||
}
|
||||
`);
|
||||
|
||||
// We consider a user schema where a user can be added only if a custom validation passes.
|
||||
// The custom validation involves fetching a min amount from a table
|
||||
// and checking if the user balance is greater than the min amount.
|
||||
// This will be done in a transaction.
|
||||
|
||||
const root = {
|
||||
hello: () => {
|
||||
return 'Hello world!';
|
||||
},
|
||||
validateAndAddUser: async ({ name, balance }) => {
|
||||
//begin transaction
|
||||
return await sequelize.transaction(async (t) => {
|
||||
try {
|
||||
//fetch min amount
|
||||
const minAmount = await MinAmount.findOne({}, {transaction: t});
|
||||
//check balance
|
||||
if (balance >= minAmount.amount) {
|
||||
//create user if balance is greater
|
||||
const user = await User.create({
|
||||
name: name,
|
||||
balance: balance
|
||||
});
|
||||
return user;
|
||||
} else {
|
||||
throw new Error("balance too low, required atleast " + minAmount.amount);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var app = express();
|
||||
app.use('/graphql', graphqlHTTP({
|
||||
schema: schema,
|
||||
rootValue: root,
|
||||
graphiql: true,
|
||||
}));
|
||||
app.listen(port);
|
||||
console.log(`Running a GraphQL API server at localhost:${port}/graphql`);
|
@ -4,18 +4,13 @@
|
||||
"description": "A GraphQL server boilerplate written in NodeJS using and Apollo Server",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node -r esm server.js"
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "Hasura",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"apollo-server": "^2.2.1",
|
||||
"graphql": "^14.0.2",
|
||||
"graphql-tools": "^4.0.3",
|
||||
"pg": "^7.7.1",
|
||||
"sequelize": "^4.42.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esm": "^3.0.84"
|
||||
"graphql-tools": "^4.0.3"
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const { makeExecutableSchema } = require('graphql-tools');
|
||||
|
||||
const port = process.env.PORT || 4000;
|
||||
|
||||
const typeDefs = `
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => "world",
|
||||
}
|
||||
};
|
||||
|
||||
const schema = makeExecutableSchema({
|
||||
typeDefs,
|
||||
resolvers
|
||||
});
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema
|
||||
});
|
||||
|
||||
server.listen({ port }).then(({url}) => {
|
||||
console.log(`GraphQL server running at ${url}`);
|
||||
});
|
Loading…
Reference in New Issue
Block a user