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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment