Skip to content

Commit

Permalink
Split Cloud SQL sample into two samples.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmdobry committed Sep 13, 2017
1 parent 868a585 commit d6270a9
Show file tree
Hide file tree
Showing 15 changed files with 616 additions and 297 deletions.
100 changes: 5 additions & 95 deletions appengine/cloudsql/README.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,11 @@
# Node.js Cloud SQL sample on Google App Engine
# Cloud SQL for MySQL Node.js sample on App Engine flexible environment

This sample demonstrates how to use [Google Cloud SQL][sql] (or any other SQL
server) on [Google App Engine Flexible][flexible].
This sample demonstrates how to use [Google Cloud SQL][sql] for
[MySQL][mysql] on [Google App Engine Flexible][flexible].

This sample has instructions for both [MySQL][mysql] and [Postgres][postgres].

## Setup

### General steps
Before you can run or deploy the sample, you will need to do the following:

1. In order for some of the commands below to work, you need to enable the
[Cloud SQL Admin API](https://console.cloud.google.com/apis/api/sqladmin-json.googleapis.com/overview).
1. Create a [Second Generation Cloud SQL][gen] instance. You can do this from
the [Cloud Console][console] or via the [Cloud SDK][sdk]. To create it via the
SDK use the following command:

gcloud sql instances create [YOUR_INSTANCE_NAME] \
--activation-policy=ALWAYS \
--tier=db-n1-standard-1

where `[YOUR_INSTANCE_NAME]` is a name of your choice.

1. Set the root password on your Cloud SQL instance:

gcloud sql instances set-root-password [YOUR_INSTANCE_NAME] --password [YOUR_INSTANCE_ROOT_PASSWORD]

where `[YOUR_INSTANCE_NAME]` is the name you chose in step 1 and
`[YOUR_INSTANCE_ROOT_PASSWORD]` is a password of your choice.

1. Using the [Cloud SQL console][sql_console], select your Cloud SQL instance.
Then, create a [user][user] (using the button in the *Access Control* > *Users* tab) and a
[database][database] (using the button in the *Databases* tab).

1. Create and download a [Service Account][service] for your project. You will
use this service account to connect to your Cloud SQL instance locally.

1. Download and install the [Cloud SQL Proxy][proxy].

1. [Start the proxy][start] to allow connecting to your instance from your local
machine:

./cloud_sql_proxy \
-instances=[YOUR_INSTANCE_CONNECTION_NAME]=tcp:[PORT] \
-credential_file=PATH_TO_YOUR_SERVICE_ACCOUNT_JSON_FILE

where `[YOUR_INSTANCE_CONNECTION_NAME]` is the connection name of your
instance on its Overview page in the Google Cloud Platform Console, or use
`[YOUR_PROJECT_ID]:[YOUR_REGION]:[YOUR_INSTANCE_NAME]`. If you're using
MySQL, `[PORT]` will be `3306`; for Postgres, it will be `5432`.

1. In a separate terminal, set the `SQL_USER`, `SQL_PASSWORD`, and `SQL_DATABASE` environment
variables to their respective values. This allows your local app to connect to your Cloud SQL
instance through the proxy.

export SQL_USER="..."
export SQL_PASSWORD="..."
export SQL_DATABASE="..."

### Choosing a SQL client
Choose which database connector to use via the `SQL_CLIENT` environment variable.

To use MySQL, set it to `mysql`:

export SQL_CLIENT="mysql"

To use Postgres, set it to `pg`:

export SQL_CLIENT="pg"

### Final setup steps
1. Update the values in `app.yaml` with your instance configuration.

1. Finally, run `createTables.js` to ensure that the database is properly
configured and to create the tables needed for the sample.

### Running locally

Refer to the [top-level README](../README.md) for instructions on running and deploying.

It's recommended to follow the instructions above to run the Cloud SQL proxy.
You will need to set the appropriate environment variables (as shown above) and
run the following commands via your shell to run the sample:

npm install
npm start
To run the sample, see [the tutorial][tutorial].

[sql]: https://cloud.google.com/sql/
[flexible]: https://cloud.google.com/appengine
[gen]: https://cloud.google.com/sql/docs/create-instance
[console]: https://console.developers.google.com
[sql_console]: https://console.developers.google.com/sql/instances/
[sdk]: https://cloud.google.com/sdk
[service]: https://cloud.google.com/sql/docs/external#createServiceAccount
[proxy]: https://cloud.google.com/sql/docs/external#install
[start]: https://cloud.google.com/sql/docs/external#6_start_the_proxy
[user]: https://cloud.google.com/sql/docs/create-user
[database]: https://cloud.google.com/sql/docs/create-database
[mysql]: https://www.mysql.com/downloads/
[postgres]: https://www.postgresql.org/download/
[tutorial]: https://cloud.google.com/appengine/docs/flexible/nodejs/using-cloud-sql
7 changes: 3 additions & 4 deletions appengine/cloudsql/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ env: flex

# [START env]
env_variables:
SQL_USER: YOUR_USER
SQL_PASSWORD: YOUR_PASSWORD
SQL_DATABASE: YOUR_DATABASE
SQL_USER: YOUR_SQL_USER
SQL_PASSWORD: YOUR_SQL_PASSWORD
SQL_DATABASE: YOUR_SQL_DATABASE
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
INSTANCE_CONNECTION_NAME: YOUR_INSTANCE_CONNECTION_NAME
SQL_CLIENT: YOUR_SQL_CLIENT # either 'pg' or 'mysql' (all lowercase)
# [END env]

# [START cloudsql_settings]
Expand Down
87 changes: 21 additions & 66 deletions appengine/cloudsql/createTables.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,84 +15,39 @@

'use strict';

// Require process, so we can mock environment variables
const process = require('process');

// [START createTables]
// [START setup]
const Knex = require('knex');
const prompt = require('prompt');
// [END setup]

// [START createTable]
/**
* Create the "visits" table.
*
* @param {object} knex A Knex client object.
*/
function createTable (knex) {
return knex.schema.createTable('visits', (table) => {
const FIELDS = ['user', 'password', 'database'];

prompt.start();

// Prompt the user for connection details
prompt.get(FIELDS, (err, config) => {
if (err) {
console.error(err);
return;
}

// Connect to the database
const knex = Knex({ client: 'mysql', connection: config });

// Create the "visits" table
knex.schema.createTable('visits', (table) => {
table.increments();
table.timestamp('timestamp');
table.string('userIp');
})
.then(() => {
console.log(`Successfully created 'visits' table.`);
return knex;
return knex.destroy();
})
.catch((err) => {
console.error(`Failed to create 'visits' table:`, err);
return knex;
if (knex) {
knex.destroy();
}
});
}
// [END createTable]

// [START getConnection]
/**
* Ask the user for connection configuration and create a new connection.
*/
function getConnection () {
const FIELDS = ['user', 'password', 'database'];
return new Promise((resolve, reject) => {
prompt.start();
prompt.get(FIELDS, (err, config) => {
if (err) {
return reject(err);
}

// Connect to the database
return resolve(Knex({
client: process.env.SQL_CLIENT,
connection: config
}));
});
});
}
// [END getConnection]

exports.main = function () {
// [START main]
getConnection()
.then((knex) => {
return createTable(knex);
})
.then((knex) => {
return knex.destroy();
})
.catch((err, knex) => {
console.error(`Failed to create database connection:`, err);
if (knex) {
knex.destroy();
}
});
// [END main]
};
});
// [END createTables]

// Get type of SQL client to use
const sqlClient = process.env.SQL_CLIENT;
if (sqlClient === 'pg' || sqlClient === 'mysql') {
exports.main();
} else {
throw new Error(`The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`);
}
10 changes: 3 additions & 7 deletions appengine/cloudsql/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "appengine-cloudsql",
"description": "Sample for Google Cloud SQL on Google App Engine Flexible Environment.",
"name": "appengine-cloudsql-mysql",
"description": "Node.js MySQL sample for Cloud SQL on App Engine flexible environment.",
"version": "0.0.1",
"private": true,
"license": "Apache-2.0",
Expand All @@ -14,7 +14,6 @@
},
"scripts": {
"deploy": "gcloud app deploy",
"start": "node server.js",
"lint": "samples lint",
"pretest": "npm run lint",
"unit-test": "ava --verbose test/*.test.js",
Expand All @@ -30,7 +29,6 @@
"express": "4.15.4",
"knex": "0.13.0",
"mysql": "2.14.1",
"pg": "7.2.0",
"prompt": "1.0.0"
},
"devDependencies": {
Expand All @@ -43,22 +41,20 @@
"test": {
"app": {
"requiredEnvVars": [
"SQL_CLIENT",
"SQL_USER",
"SQL_PASSWORD",
"SQL_DATABASE",
"SQL_PORT",
"INSTANCE_CONNECTION_NAME"
],
"msg": "Last 10 visits:",
"substitutions": "YOUR_SQL_CLIENT=$SQL_CLIENT,YOUR_USER=$SQL_USER,YOUR_PASSWORD=$SQL_PASSWORD,YOUR_DATABASE=$SQL_DATABASE,YOUR_INSTANCE_CONNECTION_NAME=$INSTANCE_CONNECTION_NAME",
"substitutions": "YOUR_SQL_USER=$SQL_USER,YOUR_SQL_PASSWORD=$SQL_PASSWORD,YOUR_SQL_DATABASE=$SQL_DATABASE,YOUR_INSTANCE_CONNECTION_NAME=$INSTANCE_CONNECTION_NAME",
"args": [
"server.js"
]
},
"build": {
"requiredEnvVars": [
"SQL_CLIENT",
"SQL_USER",
"SQL_PASSWORD",
"SQL_DATABASE",
Expand Down
44 changes: 14 additions & 30 deletions appengine/cloudsql/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@
const process = require('process');

// [START app]
// [START setup]
const express = require('express');
const Knex = require('knex');
const crypto = require('crypto');

const app = express();
app.enable('trust proxy');
// [END setup]
let knex;

const knex = connect();

function connect () {
// [START connect]
Expand All @@ -38,39 +37,37 @@ function connect () {
};

if (process.env.INSTANCE_CONNECTION_NAME && process.env.NODE_ENV === 'production') {
if (process.env.SQL_CLIENT === 'mysql') {
config.socketPath = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`;
} else if (process.env.SQL_CLIENT === 'pg') {
config.host = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`;
}
config.socketPath = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`;
}

// Connect to the database
const knex = Knex({
client: process.env.SQL_CLIENT,
client: 'mysql',
connection: config
});
// [END connect]

return knex;
}

// [START insertVisit]
/**
* Insert a visit record into the database.
*
* @param {object} knex The Knex connection object.
* @param {object} visit The visit record to insert.
* @returns {Promise}
*/
function insertVisit (visit) {
function insertVisit (knex, visit) {
return knex('visits').insert(visit);
}
// [END insertVisit]

// [START getVisits]
/**
* Retrieve the latest 10 visit records from the database.
*
* @param {object} knex The Knex connection object.
* @returns {Promise}
*/
function getVisits () {
function getVisits (knex) {
return knex.select('timestamp', 'userIp')
.from('visits')
.orderBy('timestamp', 'desc')
Expand All @@ -79,7 +76,6 @@ function getVisits () {
return results.map((visit) => `Time: ${visit.timestamp}, AddrHash: ${visit.userIp}`);
});
}
// [END getVisits]

app.get('/', (req, res, next) => {
// Create a visit record to be stored in the database
Expand All @@ -89,11 +85,9 @@ app.get('/', (req, res, next) => {
userIp: crypto.createHash('sha256').update(req.ip).digest('hex').substr(0, 7)
};

insertVisit(visit)
.then(() => {
// Query the last 10 visits from the database.
return getVisits();
})
insertVisit(knex, visit)
// Query the last 10 visits from the database.
.then(() => getVisits(knex))
.then((visits) => {
res
.status(200)
Expand All @@ -106,21 +100,11 @@ app.get('/', (req, res, next) => {
});
});

// Get type of SQL client to use
const sqlClient = process.env.SQL_CLIENT;
if (sqlClient === 'pg' || sqlClient === 'mysql') {
knex = connect();
} else {
throw new Error(`The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`);
}

// [START listen]
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
// [END listen]
// [END app]

module.exports = app;
Loading

0 comments on commit d6270a9

Please sign in to comment.