├── sample.secrets.json ├── .gitignore ├── readme.md ├── package.json ├── db.js ├── serverless.yml └── handler.js /sample.secrets.json: -------------------------------------------------------------------------------- 1 | { 2 | "NODE_ENV": "dev", 3 | "DB": "", 4 | "APP_ID": "" 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless 7 | 8 | # secrets 9 | secrets.json -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Building a serverless REST API with Node.js and MongoDB Stitch 2 | 3 | Sample repository for building a serverless service on AWS Lambda with MongoDB Stitch as a persistent data store. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "building-a-serverless-rest-api-with-nodejs-and-mongodb-stitch", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "handler.js", 6 | "scripts": { 7 | "offline": "sls offline start --skipCacheInvalidation" 8 | }, 9 | "keywords": [], 10 | "author": "Adnan Rahić", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "serverless-offline": "^3.16.0" 14 | }, 15 | "dependencies": { 16 | "mongodb-stitch": "^3.1.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | const { StitchClientFactory, BSON } = require('mongodb-stitch') 2 | const { ObjectId } = BSON 3 | const appId = process.env.APP_ID 4 | const database = process.env.DB 5 | const connection = {} 6 | 7 | module.exports = async () => { 8 | if (connection.isConnected) { 9 | console.log('[MongoDB Stitch] Using existing connection to Stitch') 10 | return connection 11 | } 12 | 13 | try { 14 | const client = await StitchClientFactory.create(appId) 15 | const db = client.service('mongodb', 'mongodb-atlas').db(database) 16 | await client.login() 17 | const ownerId = client.authedId() 18 | console.log('[MongoDB Stitch] Created connection to Stitch') 19 | 20 | connection.isConnected = true 21 | connection.db = db 22 | connection.ownerId = ownerId 23 | connection.ObjectId = ObjectId 24 | return connection 25 | } catch (err) { 26 | console.error(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: sls-api-stitch 2 | 3 | custom: 4 | secrets: ${file(secrets.json)} 5 | 6 | provider: 7 | name: aws 8 | runtime: nodejs8.10 9 | timeout: 10 10 | stage: ${self:custom.secrets.NODE_ENV} 11 | region: eu-central-1 12 | environment: 13 | NODE_ENV: ${self:custom.secrets.NODE_ENV} 14 | DB: ${self:custom.secrets.DB} 15 | APP_ID: ${self:custom.secrets.APP_ID} 16 | 17 | functions: 18 | create: 19 | handler: handler.create 20 | events: 21 | - http: 22 | path: notes 23 | method: post 24 | cors: true 25 | getOne: 26 | handler: handler.getOne 27 | events: 28 | - http: 29 | path: notes/{id} 30 | method: get 31 | cors: true 32 | getAll: 33 | handler: handler.getAll 34 | events: 35 | - http: 36 | path: notes 37 | method: get 38 | cors: true 39 | update: 40 | handler: handler.update 41 | events: 42 | - http: 43 | path: notes/{id} 44 | method: put 45 | cors: true 46 | delete: 47 | handler: handler.delete 48 | events: 49 | - http: 50 | path: notes/{id} 51 | method: delete 52 | cors: true 53 | 54 | plugins: 55 | - serverless-offline -------------------------------------------------------------------------------- /handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const connectToDatabase = require('./db'); 4 | 5 | module.exports.create = async (event) => { 6 | try { 7 | const { db } = await connectToDatabase() 8 | const { insertedId } = await db.collection('notes') 9 | .insertOne(JSON.parse(event.body)) 10 | 11 | const addedObject = await db.collection('notes') 12 | .findOne({ _id: insertedId }) 13 | 14 | return { 15 | statusCode: 200, 16 | body: JSON.stringify(addedObject) 17 | } 18 | } catch (err) { 19 | return { 20 | statusCode: err.statusCode || 500, 21 | headers: { 'Content-Type': 'text/plain' }, 22 | body: 'Could not create the object.' 23 | } 24 | } 25 | } 26 | 27 | module.exports.getOne = async (event) => { 28 | try { 29 | const { db, ObjectId } = await connectToDatabase() 30 | const foundNote = await db.collection('notes') 31 | .findOne({ _id: ObjectId(event.pathParameters.id) }) 32 | 33 | return { 34 | statusCode: 200, 35 | body: JSON.stringify(foundNote) 36 | } 37 | } catch (err) { 38 | console.log(err) 39 | return { 40 | statusCode: err.statusCode || 500, 41 | headers: { 'Content-Type': 'text/plain' }, 42 | body: 'Could not fetch the note.' 43 | } 44 | } 45 | } 46 | 47 | module.exports.getAll = async () => { 48 | try { 49 | const { db } = await connectToDatabase() 50 | const foundNotes = await db.collection('notes') 51 | .find({}) 52 | .execute() 53 | 54 | return { 55 | statusCode: 200, 56 | body: JSON.stringify(foundNotes) 57 | } 58 | } catch (err) { 59 | console.log(err) 60 | return { 61 | statusCode: err.statusCode || 500, 62 | headers: { 'Content-Type': 'text/plain' }, 63 | body: 'Could not fetch the notes.' 64 | } 65 | } 66 | } 67 | 68 | module.exports.update = async (event) => { 69 | try { 70 | const { db, ObjectId } = await connectToDatabase() 71 | await db.collection('notes') 72 | .updateOne({ _id: ObjectId(event.pathParameters.id) }, JSON.parse(event.body), { upsert: true }) 73 | 74 | const updatedNote = await db.collection('notes') 75 | .findOne({ _id: ObjectId(event.pathParameters.id) }) 76 | 77 | return { 78 | statusCode: 200, 79 | body: JSON.stringify(updatedNote) 80 | } 81 | } catch (err) { 82 | console.log(err) 83 | return { 84 | statusCode: err.statusCode || 500, 85 | headers: { 'Content-Type': 'text/plain' }, 86 | body: 'Could not update the note.' 87 | } 88 | } 89 | }; 90 | 91 | module.exports.delete = async (event) => { 92 | try { 93 | const { db, ObjectId } = await connectToDatabase() 94 | const deletedNote = await db.collection('notes') 95 | .deleteOne({ _id: ObjectId(event.pathParameters.id) }) 96 | 97 | return { 98 | statusCode: 200, 99 | body: JSON.stringify(deletedNote) 100 | } 101 | } catch (err) { 102 | console.log(err) 103 | return { 104 | statusCode: err.statusCode || 500, 105 | headers: { 'Content-Type': 'text/plain' }, 106 | body: 'Could not delete the note.' 107 | } 108 | } 109 | }; 110 | --------------------------------------------------------------------------------