FaaS
THE
S
erverless
AND THE
Simon MacDonald
@macdonst
How I saved myself some 💰 by converting from
to
What do the Fast & the Furious movies have to do with Serverless?
Planning
Punching
Mechanic
Comic Relief
Characters
Functions
Fetch
Process
Transform
UI
Characters
- Have their own agency
- Internally consistent
- Do one thing well
Functions
- Stateless
- Pure
- One task per function
How does this relate to FaaS?
The Paul Walker Pattern(s)
Chaining
Fan in/out
Accomplishments
- Brought down drug lords (multiple times)
- Apprehend rouge secret agents (twice)
- Prevent nuclear war
function queryLegacyAPI(query) {
return fetch(api.getData(query))
.then(response => response.text())
.then(xmlString => parseXML(xmlString))
.then(xml => { return xml; })
);
}
function formatResponse(data) {
const template = await loadTemplate();
const html = json2html.transform(data, t);
return html;
}
function transform2Json(data) {
const xslt = await loadXslt();
const parser = new DOMParser();
const doc = parser.parseFromString(data, "application/xml");
const json = doc.transformNode(xslt)
return json;
}
Composing Functions
function queryLegacyAndFormat() {
return compose(
formatResponse,
transform2Json,
queryLegacyAPI
)(query);
}
Plot Points
- Car Thefts
- Blackmail
- Jailbreaks
- Kidnappings
- Murders!
Events
- Git commit
- Database action
- Cron job
- Blob storage
- Analytics trigger
- etc, etc, etc
What about Serverless?
On Premises
IaaS
PaaS
SaaS
Faas
It's about the Cars…
…and the Roads.
Furious…
- Characters
- Cars
- Roads
 - Infinite possiblities
Serverless
- Code (Faas)
- Containers (SaaS/PaaS)
- Other folks Infrastructure (IaaS)
- Unbounded Scaling
Why?
🤔
Translation
Query
Static Assets
const Hapi = require('hapi');
const Joi = require('joi');
const fetch = require('node-fetch');
const credentials = require('./credentials');
const server = Hapi.server({ host: '0.0.0.0', port: 8000 });
async function start() {
try {
await server.register(require('inert'));
server.route({
method: 'POST',
path: '/wolfram',
config: {…}
});
server.route({
method: 'POST',
path: '/translate',
config: {…}
});
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: 'public'
}
}
});
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
}
start();
Translation
Query
Static Assets
const Hapi = require('hapi');
const Joi = require('joi');
const fetch = require('node-fetch');
const credentials = require('./credentials');
const server = Hapi.server({ host: '0.0.0.0', port: 8000 });
async function start() {
try {
await server.register(require('inert'));
server.route({
method: 'POST',
path: '/wolfram',
config: {…}
});
server.route({
method: 'POST',
path: '/translate',
config: {…}
});
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: 'public'
}
}
});
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
}
start();
const Hapi = require('hapi');
const Joi = require('joi');
const fetch = require('node-fetch');
const credentials = require('./credentials');
const server = Hapi.server({ host: '0.0.0.0', port: 8000 });
async function start() {
try {
await server.register(require('inert'));
server.route({
method: 'POST',
path: '/wolfram',
config: {…}
});
server.route({
method: 'POST',
path: '/translate',
config: {…}
});
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
}
start();
Translation
Query
Static Assets
FaaS
server.route({
method: 'POST',
path: '/wolfram',
config: {
handler: function(req) {
console.log('asking wolfram alpha');
let url = `http://api.wolframalpha.com/v2/query?input=${
req.payload.searchTerm
}&appid=${credentials.wolfram}`;
return fetch(url)
.then(res => res.text())
.then(text => {
return text;
});
}
}
});
server.route({
method: 'POST',
path: '/translate',
config: {
handler: function(req) {
console.log('POST translate');
let url = `https://www.googleapis.com/language/translate/v2?key=${
credentials.google
}&source=en&target=fr&q=${req.payload.searchTerm}`;
return fetch(url)
.then(res => res.json())
.then(json => {
return json;
});
}
}
});
const fetch = require('node-fetch');
const credentials = require('./credentials');
module.exports = async function(context, req) {
context.log('Wolfram HTTP trigger function processed a request.');
if (req.query.searchTerm || (req.body && req.body.searchTerm)) {
const searchTerm = req.query.searchTerm || req.body.searchTerm;
context.log('search term = ' + searchTerm);
let url = `http://api.wolframalpha.com/v2/query?input=${searchTerm}&appid=${
credentials.wolfram
}`;
await fetch(url)
.then(res => res.text())
.then(text => {
context.res = {
status: 200,
headers: { 'Content-Type': 'text/plain' },
body: text
};
context.done();
});
} else {
context.res = {
status: 400,
body: 'Please pass a name on the query string or in the request body'
};
}
context.done();
};
Wolfram Function
const fetch = require('node-fetch');
const credentials = require('./credentials');
module.exports = async function(context, req) {
context.log('Translate HTTP trigger function processed a request.');
if (req.query.searchTerm || (req.body && req.body.searchTerm)) {
const searchTerm = req.query.searchTerm || req.body.searchTerm;
let url = `https://www.googleapis.com/language/translate/v2?key=${
credentials.google
}&source=en&target=fr&q=${searchTerm}`;
context.log('search term = ' + searchTerm);
await fetch(url)
.then(res => res.json())
.then(json => {
context.res = {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: json
};
context.done();
});
} else {
context.res = {
status: 400,
body: 'Please pass a name on the query string or in the request body'
};
}
context.done();
};
Translate Function
const Hapi = require('hapi');
const Joi = require('joi');
const fetch = require('node-fetch');
const credentials = require('./credentials');
const server = Hapi.server({ host: '0.0.0.0', port: 8000 });
async function start() {
try {
await server.register(require('inert'));
server.route({
method: 'POST',
path: '/wolfram',
config: {…}
});
server.route({
method: 'POST',
path: '/translate',
config: {…}
});
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
}
start();
Hapi Server
const Hapi = require('hapi');
const Joi = require('joi');
const fetch = require('node-fetch');
const credentials = require('./credentials');
const server = Hapi.server({ host: '0.0.0.0', port: 8000 });
async function start() {
try {
await server.register(require('inert'));
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
}
start();
🔥
Translation
Query
Static Assets
FaaS
💰
No more hosting fees!
💸
Until you add
CosmosDB
Thanks Linda Nichols (@lynnaloo)!
Check out her great talk on Serverless called
Summary
- Functions should only do one thing
- Everything a function needs should be passed in as input
- The same input should produce the same output every time
- Compose multiple small functions to make more complicated actions
- Use events to fire off sequences of functions
- Take advantage of other people's infrastructure so you can focus on the code
The FaaS and the Serverless
By Simon MacDonald
The FaaS and the Serverless
Connect.Tech Atlanta
- 2,229