Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleaned up memcached example and added tests. #26

Merged
merged 1 commit into from
Nov 13, 2015
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ cache:

services:
- redis-server
- memcached
- docker

env:
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and applications on [Google App Engine](http://cloud.google.com/nodejs).
### Frameworks

- Express.js - [Source code][express_1] | [App Engine Tutorial][express_2] | [Live demo][express_3] | [Documentation][express_4]
- Express.js + Memcached Sessions - [Source code][express_5] | [Documentation][express_6]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit that you don't have to fix now: it would be better to have more described anchors here. express_6 is kinda of funny.

- Geddy.js - [Source code][geddy_1] | [App Engine Tutorial][geddy_2] | [Live demo][geddy_3] | [Documentation][geddy_4]
- Hapi.js - [Source code][hapi_1] | [App Engine Tutorial][hapi_2] | [Live demo][hapi_3] | [Documentation][hapi_4]
- Loopback.js - [Source code][loopback_1] | [App Engine Tutorial][loopback_2] | [Live demo][loopback_3] | [Documentation][loopback_4]
Expand Down Expand Up @@ -68,6 +69,8 @@ See [LICENSE](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/ma
[express_2]: https://cloud.google.com/nodejs/resources/frameworks/express
[express_3]: http://express-dot-nodejs-docs-samples.appspot.com
[express_4]: http://expressjs.com/
[express_5]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/master/appengine/express-memcached-session
[express_6]: https://github.com/balor/connect-memcached

[geddy_1]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/master/appengine/geddy
[geddy_2]: https://cloud.google.com/nodejs/resources/frameworks/geddy
Expand Down
132 changes: 80 additions & 52 deletions appengine/express-memcached-session/README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,93 @@
# Express + Memcached Sessions -> Google App Engine
## Express.js + Memcached Sessions on Google App Engine

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this readme is too heavy. What's the point of the sample code if you put everything in the readme? :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the readme more lightweight, it now mirrors more closely the format used by the tutorials for the other framework examples.


This is a simple guide to using memcached for session state while running [expressjs](http://expressjs.com/) on Google App Engine. Each Google App Engine application comes with a memcached service instance, which can be reached with a standard memcached driver at `memcache:11211`.
This is a simple guide to using memcached for session state while running
[Express.js](http://expressjs.com/) on Google App Engine. Each Google App Engine
application comes with a memcached service instance, which can be reached with a
standard memcached driver at `memcache:11211`. This sample uses the
[connect-memcached](https://github.com/balor/connect-memcached) module to store
session data in memcached.

1. [Create a new Express app](http://expressjs.com/starter/generator.html)
## Clone the Express.js + Memcached Sessions app

2. Create an `app.yaml` in the root of your application with the following contents:
If you haven't already, copy the repository to your local machine by entering
the following command in your terminal window:

```yaml
runtime: nodejs
vm: true
env_variables:
PORT: 8080
MEMCACHE_URL: memcache:11211
```
```
$ git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
$ cd nodejs-docs-samples/appengine/express-memcached-session
```

Alternatively, you can [download the sample][download] as a zip and extract it.

## Run the app on your local computer

1. Install dependencies. Enter the following command:

Notice the MEMCACHE_URL environment variable - this is where you can reach your standard memcached cluster across instances.
3. Use the [connect-memcached](https://github.com/balor/connect-memcached) module. Run `npm install --save connect-memcached`, and add the following to your server.js or app.js:

```js
var MemcachedStore = require('connect-memcached')(session);
...
app.use(session({
secret: 'appengineFTW',
key: 'test',
proxy: 'true',
store: new MemcachedStore({
hosts: [process.env.MEMCACHE_URL || '127.0.0.1:11211']
})
}));
```
4. In your express route handlers, you can now safely use `req.session.*` across multiple nodejs instances:

```js
app.get('/', function(req, res){
publicIp.v4(function (err, ip) {
res.write("<div>" + ip + "</div>");
if(req.session.views) {
++req.session.views;
} else {
req.session.views = 1;
}
res.end('Viewed <strong>' + req.session.views + '</strong> times.');
});
});
$ npm install
```

5. To test the sample locally, you can install memcached.
- OSX + [Brew](http://brew.sh/): `brew install memcached`
- Windows + [Chocolatey](https://chocolatey.org/packages/memcached): `choco install memcached`
2. Run the start script.

Run memcached on localhost:11211 by running `memcached`

````
$ npm start
```
6. Deploy your app. For convenience, you can use an npm script to run the command. Modify your `package.json` to include:
3. In your web browser, enter the following address:
```js
"scripts": {
"start": "node server.js",
"deploy": "gcloud preview app deploy app.yaml --set-default --project [project id]"
}
```
$ http://localhost:8080
```
You can see the sample app displayed in the page. This page was delivered by the
Express.js web server running on your computer.
In your terminal window, press Ctrl+C to exit the web server.
## Deploy the app to Google Cloud Platform
In your terminal window, enter the following command to deploy the sample:
```
$ gcloud preview app deploy app.yaml --promote
```
### See the app run in the cloud
In your web browser, enter the following address:
```
https://<your-project-id>.appspot.com
```
For convenience, you can use an npm script to run the gcloud command. Add these lines to your package.json file:
```
"scripts": {
"start": "node server.js",
"deploy": "gcloud preview app deploy app.yaml --promote --project <your-project-id>"
}
```
At the terminal you can now run the following command to deploy your application:
```
$ npm run deploy
```
## Configuration
Every Managed VMs application requires an app.yaml file to describe its deployment configuration.
```yaml
runtime: nodejs
vm: true
env_variables:
PORT: 8080
MEMCACHE_URL: memcache:11211
```
Notice the `MEMCACHE_URL` environment variable–this is where you can reach your
standard memcached cluster across instances.
At the terminal you can now run `npm run deploy` to deploy your application.
[download]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples/archive/master.zip
1 change: 0 additions & 1 deletion appengine/express-memcached-session/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# limitations under the License.

runtime: nodejs
api_version: 1
vm: true
env_variables:
PORT: 8080
Expand Down
16 changes: 11 additions & 5 deletions appengine/express-memcached-session/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
{
"name": "express-memcached-session-demo",
"version": "1.0.0",
"name": "appengine-express-memcached-session",
"description": "An example of using memcached for sessions in Express.js on Google App Engine.",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"engines": {
"node": "~0.12.7"
},
"scripts": {
"deploy": "gcloud preview app deploy app.yaml --set-default --project express-memcached-demo"
"start": "node server.js",
"deploy": "gcloud preview app deploy app.yaml"
},
"dependencies": {
"connect-memcached": "^0.1.0",
"cookie-parser": "~1.3.5",
"express": "~4.12.4",
"cookie-parser": "^1.3.5",
"express": "^4.12.4",
"express-session": "^1.11.3",
"public-ip": "^1.1.0"
}
Expand Down
45 changes: 24 additions & 21 deletions appengine/express-memcached-session/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,40 @@
var express = require('express');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var http = require('http');
var MemcachedStore = require('connect-memcached')(session);
var publicIp = require('public-ip');

var app = express();

app.use(cookieParser());
app.use(session({
secret: 'appengineFTW',
key: 'test',
proxy: 'true',
store: new MemcachedStore({
hosts: [process.env.MEMCACHE_URL || '127.0.0.1:11211']
})
secret: 'your-secret-here',
key: 'view:count',
proxy: 'true',
store: new MemcachedStore({
hosts: [process.env.MEMCACHE_URL || '127.0.0.1:11211']

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you slimmed down the readme, a little comment here about how MEMCACHE_URL is set by GAE would be helpful. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's set by GAE? So it doesn't need to be set in the app.yaml file?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, just saw the full thing. A comment in the readme about the memcache host then. :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is one at the bottom.

})
}));

app.get('/', function(req, res){
publicIp.v4(function (err, ip) {

// This shows the IP for each
res.write('<div>' + ip + '</div>');
if(req.session.views) {
++req.session.views;
} else {
req.session.views = 1;
}
res.end('Viewed <strong>' + req.session.views + '</strong> times.');
});
publicIp.v4(function (err, ip) {

// This shows the IP for each
res.write('<div>' + ip + '</div>');

if(req.session.views) {
++req.session.views;
} else {
req.session.views = 1;
}
res.end('Viewed <strong>' + req.session.views + '</strong> times.');
});
});

http.createServer(app).listen(process.env.PORT || 8080, function() {
if (module === require.main) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed here? Is it for tests? I don't often see this in other samples.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for tests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha.

app.listen(process.env.PORT || 8080, function() {
console.log('Listening on %d', this.address().port);
});
});
}

module.exports = app;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"cover": "istanbul cover --hook-run-in-context node_modules/mocha/bin/_mocha -- --timeout 10000 --recursive",
"coveralls": "cat ./coverage/lcov.info | node_modules/.bin/coveralls",
"pretest_express": "cd appengine/express && npm install && cd ../..",
"pretest_memcached": "cd appengine/express-memcached-session && npm install && cd ../..",
"pretest_geddy": "cd appengine/geddy && npm install geddy; GEDDY_SECRET=config/secrets.json; [[ -f $GEDDY_SECRET ]] || echo '{}' > $GEDDY_SECRET && node node_modules/.bin/geddy gen secret; cd ../..;",
"pretest": "npm run pretest_express && npm run pretest_geddy",
"test": "npm run jshint && npm run cover"
Expand Down
23 changes: 15 additions & 8 deletions test/appengine/all.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ var sampleTests = [
msg: 'Hello World! Express.js on Google App Engine.',
TRAVIS_NODE_VERSION: '0.10'
},
{
dir: 'express-memcached-session',
cmd: 'node',
args: ['server.js'],
msg: 'Viewed',
TRAVIS_NODE_VERSION: '0.10'
},
{
dir: 'geddy',
deploy: true,
Expand Down Expand Up @@ -82,12 +89,12 @@ var sampleTests = [
args: ['app.js'],
msg: 'Express.js + Mailgun on Google App Engine.'
},
// {
// dir: 'redis',
// cmd: 'node',
// args: ['server.js'],
// msg: '127.0.0.1'
// },
{
dir: 'redis',
cmd: 'node',
args: ['server.js'],
msg: '127.0.0.1'
},
{
dir: 'restify',
deploy: true,
Expand All @@ -112,7 +119,7 @@ if (process.env.TRAVIS_NODE_VERSION === '0.10') {
});
}

//if (process.env.TRAVIS_NODE_VERSION === 'stable') {
if (process.env.TRAVIS_NODE_VERSION === 'stable') {
// For some reason the "npm install" step for the Sails sample doesn't work on
// Travis when using Node.js stable. It works locally, however.
sampleTests.push({
Expand All @@ -123,7 +130,7 @@ if (process.env.TRAVIS_NODE_VERSION === '0.10') {
msg: 'Hello World! Koa.js on Google App Engine.',
TRAVIS_NODE_VERSION: 'stable'
});
//}
}

// Send a request to the given url and test that the response body has the
// expected value
Expand Down
26 changes: 26 additions & 0 deletions test/appengine/express-memcached-session.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2015, Google, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

var request = require('supertest');
var app = require('../../appengine/express-memcached-session/server.js');

describe('express-memcached-session', function () {
it('should return 200', function (done) {
request(app)
.get('/')
.expect(200)
.end(done);
});
});