Bind a Service to Your App
Page last updated:
Page last updated:
The service marketplace has a large number of data stores, from Redis and MongoDB, to MariaDB (fork of MySQL) and RabbitMQ. You can run cf marketplace
to get an overview. In this step you will add a small MongoDB database to your app.
Create the database:
$ cf create-service mongodb-2 small my-mongodb Creating service instance my-mongodb in org MyOrg / space MySpace as user@mydomain.com... OK Create in progress. Use 'cf services' or 'cf service my-mongodb' to check operation status.
This creates a small MongoDB database for you which we now have to bind to our application. Binding means that the credentials and URL of the service will be written dynamically into the environment variables of the app as VCAP_SERVICES
and can hence be used directly from there.
Let’s bind the new service to our existing application:
$ cf bind-service my-nodejs-app my-mongodb Binding service my-mongodb to app my-nodejs-app in org MyOrg / space MySpace as user@mydomain.com... OK TIP: Use 'cf restage my-nodejs-app' to ensure your env variable changes take effect
Note: If you are getting Server error, status code: 409
, please try again after a couple of minutes. It takes a while to spin up that MongoDB for you.
After that we restage the application as suggested so that it includes the new credentials in its environment variables:
$ cf restage my-nodejs-app Restaging app my-app in org MyOrg / space MySpace as user@mydomain.com... -----> Downloaded app package (8.0K) -------> Buildpack version 1.5.8 -----> Creating runtime environment ...
Now we want to consume our new MongoDB from within our application. Use npm to add the mongoose
npm module to your dependencies:
$ npm install --save mongoose cf-sample-app-nodejs@0.0.0 /.../cf-sample-app-nodejs └─┬ mongoose@4.4.10 ├── async@1.5.2 ├── bson@0.4.21 ├── hooks-fixed@1.1.0 ...
Now edit your app.js
file to use this module to connect to the database specified in your VCAP_SERVICES
environment variable. We do this by requiring the mongoose
module and adding a route for the /kittens
endpoint which returns the kittens
collection of our MongoDB:
'use strict';
// require dependencies
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const routes = require('./routes/index');
// bootstrap our app
const app = express();
let mongoUrl = '';
// check if the app is running in the cloud and set the MongoDB settings accordingly
if (process.env.VCAP_SERVICES) {
const vcapServices = JSON.parse(process.env.VCAP_SERVICES);
mongoUrl = vcapServices.mongodb[0].credentials.uri;
} else {
mongoUrl = 'mongodb://localhost/db';
}
// connect to our MongoDB
mongoose.connect(mongoUrl);
// create a Mongoose schema for kittens
const kittenSchema = mongoose.Schema({
name: 'string'
});
// create a Mongoose model out of our kitten schema
const Kitten = mongoose.model('Kitten', kittenSchema);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// our basic route to serve the index page
app.use('/', routes);
// the route where you can retrieve all the kittens in our MongoDB
app.get('/kittens', (req, res, next) => {
Kitten.find((err, kittens) => {
if (err) return next(err);
res.json(kittens);
})
})
// the route where you can create a new kitten in our MongoDB
app.post('/kittens', (req, res, next) => {
const kitten = new Kitten({
name: req.query.name
});
kitten.save((err, newKitten) => {
if (err) return next(err);
res.send('Kitten ' + newKitten.name + ' saved');
})
})
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
This ensures that when you access your app using the /kittens
route, it will return all the kittens stored in your database. Currently there are no kittens so it will return an empty array. Sad…
But you can create new kittens by POST-ing to the /kittens
endpoint with the kitten’s name as a URL query parameter. You can do so using curl or any similar tool:
$ curl -X POST "http://localhost:3000/kittens?name=garfield"
and then retrieve your new kitten at the GET /kittens
endpoint.
The line
if (process.env.VCAP_SERVICES) {
checks if the app is running in the cloud. If not, it falls back to the default local MongoDB url. This allows you to run your app locally as well as in the cloud without having to configure anything differently. So let’s push it to the cloud using
$ cf push my-nodejs-app
You can access other services like Redis or MariaDB in a similar matter, simply by binding them to your app and accessing them through the environment variables.
View the source for this page in GitHub