Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Node Express-based REST API (CRUD) using Firebase cloud Functions and FireStore cloud database + Babel config (The current Node version running in Cloud Functions is 6.10)
{
"presets": [
["env", {
"targets": {
"node": "6.10"
}
}]
]
}
const functions = require('firebase-functions');
const express = require('express')
const compression = require('compression')
const bodyParser = require('body-parser');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const {jwtSecret} = require('./config');
const {ctrlLogin, ctrlRead, ctrlCreate, ctrlUpdate, ctrlDelete} = require('./controller');
const app = express();
app.use(bodyParser.json());
app.use(compression())
app.use(cors());
app.use(function(req, res, next) {
const token = req.query.t;
if(req.path == '/login') {
next();
} else if ( ! token ) {
res.status(403).send({
success: false,
message: 'No token provided.'
});
} else {
jwt.verify(token, jwtSecret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
}
});
app.post('/login',
ctrlLogin,
(req, res) => res.json(res.data)
);
app.get('/api/:doc',
ctrlRead,
(req, res) => res.json(res.data)
);
app.get('/api/:doc',
ctrlRead,
(req, res) => res.json(res.data)
);
app.post('/api/:doc',
ctrlCreate,
(req, res) => res.json(res.data)
);
app.put('/api/:doc/:id',
ctrlUpdate,
(req, res) => res.json(res.data)
);
app.delete('/api/:doc:/id',
ctrlDelete,
(req, res) => res.json(res.data)
);
app.use( (err, req, res, next) => {
console.log(err)
res.status(500).send('An error occurred')
});
exports.app = functions.https.onRequest(app);
exports.jwtSecret = '';
exports.collectionName = '';
const jwt = require('jsonwebtoken');
const {checkLogin, getDocNames, getDoc, createDoc, updateDoc, deleteDoc} = require('./model');
const {jwtSecret} = require('./config');
exports.ctrlLogin = (req, res, next) => {
login(req)
.then( logged => {
if( ! logged ) {
res.data = {
success: false,
message: 'Authentication failed'
};
} else {
res.data = {
success: true,
token: jwt.sign({logged: true}, jwtSecret)
};
}
next();
})
.catch( err => {
next(new Error(err));
});
};
exports.ctrlRead = (req, res, next) => {
getData(req)
.then( data => {
res.data = data;
next();
})
.catch( err => {
next(new Error(err));
});
};
exports.ctrlCreate = (req, res, next) => {
addData(req)
.then( data => {
res.data = data;
next();
})
.catch( err => {
next(new Error(err));
});
};
exports.ctrlUpdate = (req, res, next) => {
updateData(req)
.then( data => {
res.data = data;
next();
})
.catch( err => {
next(new Error(err));
});
};
exports.ctrlDelete = (req, res, next) => {
deleteData(req)
.then( data => {
res.data = data;
next();
})
.catch( err => {
next(new Error(err));
});
};
async function deleteData(req) {
const id = req.params.id;
var doc = await getData(req);
return await deleteDoc(doc, id);
}
async function updateData(req) {
const updatedItem = req.body;
const id = req.params.id;
var doc = await getData(req);
return await updateDoc(doc, updatedItem, id);
}
async function addData(req) {
const newItem = req.body;
var doc = await getData(req);
return await createDoc(doc, newItem);
}
async function getData(req) {
try {
let docName = req.params.doc;
await isValidDocName(docName);
return await getDoc(docName);
} catch(err) {
throw err;
}
}
async function login(req) {
try {
let pwd = req.body.pwd;
return await checkLogin(pwd);
} catch(err) {
throw err;
}
}
async function isValidDocName(docName) {
try {
const docNames = await getDocNames();
if( ! docNames.includes(docName) ) {
throw `${docName} is not a valid document name`;
}
return docName;
} catch(err) {
throw err;
}
}
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
const {collectionName} = require('./config');
const addDoc = {
tasks: (doc, newItem) => {
newItem.id = ++doc.data.index;
doc.data.list.push(newItem);
return setFirebaseDoc(doc);
},
days: (doc, newItem) => {
doc.data = Object.assign({}, doc.data, newItem)
return setFirebaseDoc(doc);
},
users: (doc, newItem) => {
if( ! doc.data.list ) {
doc.data.list = [];
}
doc.data.list.push(newItem);
return setFirebaseDoc(doc);
}
}
const updateDoc = {
tasks: (doc, updatedItem, id) => {
delete updatedItem.id;
doc.data.list = doc.data.list
.map( task => ( task.id == id ) ? Object.assign({}, task, updatedItem) : task )
return setFirebaseDoc(doc);
},
days: (doc, updatedItem, id) => {
doc.data[id] = updatedItem;
return setFirebaseDoc(doc);
}
}
const deleteDoc = {
tasks: (doc, id) => {
doc.data.list = doc.data.list
.filter( task => task.id != id )
return setFirebaseDoc(doc);
},
days: (doc, id) => {
delete doc.data[id];
return setFirebaseDoc(doc);
}
}
exports.checkLogin = pwd =>
getFirebaseDoc('users')
.then( data => {
return data.data.list.some( doc => doc.pwd == pwd )
});
exports.getDocNames = () => getAllFirebaseDocsIds();
exports.getDoc = docName => getFirebaseDoc(docName)
exports.createDoc = (doc, newItem) =>
addDoc[doc.id](doc, newItem)
exports.updateDoc = (doc, updatedItem, id) =>
updateDoc[doc.id](doc, updatedItem, id)
exports.deleteDoc = (doc, id) =>
deleteDoc[doc.id](doc, id)
async function getAllFirebaseDocsIds() {
let ids = [];
let snapshot = await db.collection(collectionName).get();
snapshot.forEach( doc => ids.push(doc.id) );
return ids;
}
async function getFirebaseDoc(docName) {
try {
let res = {
id: docName
};
let snapshot = await db.collection(collectionName).get();
snapshot.forEach( doc => {
if(doc.id == docName) {
res.data = doc.data();
}
});
return res;
} catch(err) {
throw err;
}
};
async function setFirebaseDoc(updatedDoc) {
try {
let docRef = db.collection(collectionName).doc(updatedDoc.id);
await docRef.set(updatedDoc.data);
return updatedDoc;
} catch(err) {
throw err;
}
}
@sergio-27
Copy link

sergio-27 commented Nov 14, 2018

Where is your index js file?

@Franklin-Rodrigo
Copy link

Franklin-Rodrigo commented Apr 1, 2019

index.js file??

@gkkirilov
Copy link

gkkirilov commented Apr 3, 2019

app.js === index.js

@Edwary
Copy link

Edwary commented May 16, 2019

HI, I have a question, what i should to put here "exports.jwtSecret = '';"

@aloism
Copy link

aloism commented Jul 15, 2020

Thank you so much for these great notes, they really helped especially how your express is handling url calls, and separation of functions into other files, however i do have one question, please share an example of how the url are going to be called or consumed by other platforms.

@talesmgodois
Copy link

talesmgodois commented Aug 16, 2020

Cool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment