Home » #Technology » Real-World Use Case: Serverless Web Service with MongoDB Backend

Real-World Use Case: Serverless Web Service with MongoDB Backend

Cloud services have revolutionized development by offering highly configurable tools with minimal coding required directly on the cloud. In my 18+ years of building enterprise solutions, when I was first introduced to this technology, we quickly adapted and created numerous use cases to accelerate our product MVP and launches. In this tech post, we tackle a real-world problem to demonstrate how to build and deploy a serverless web service using AWS Lambda and MongoDB as the backend. The service will be an API for managing a product catalog where users can perform CRUD (Create, Read, Update, Delete) operations on product data.

We use, MongoDB to serve as the database to store product details, and AWS Lambda to handle the web service logic. API Gateway will be used to expose the Lambda function as an HTTP API.

Tech Stack:

  • AWS Lambda: For serverless web service logic.
  • MongoDB (MongoDB Atlas): Cloud-based MongoDB for storing product data.
  • API Gateway: To create HTTP endpoints for interacting with the Lambda functions.
  • Mongoose: For MongoDB object modeling in Node.js.

Step 1: Set Up MongoDB on MongoDB Atlas

  1. Create a MongoDB Atlas account at mongodb.com.
  2. Set up a new MongoDB cluster.
  3. Create a database named productDB and a collection called products.
  4. Whitelist your IP and get the MongoDB connection string (with credentials).

Example MongoDB connection string:

mongodb+srv://<username>:<password>@cluster0.mongodb.net/productDB?retryWrites=true&w=majority

Step 2: Write the AWS Lambda Function

We’ll use Node.js as the runtime for our Lambda function and Mongoose to interact with MongoDB. The function will handle basic CRUD operations.

Lambda Function Code: productService.js
const mongoose = require('mongoose');
const { Schema } = mongoose;

// Define Product Schema
const productSchema = new Schema({
  name: String,
  description: String,
  price: Number,
  category: String,
});

const Product = mongoose.model('Product', productSchema);

let conn = null;

// Lambda function entry point
exports.handler = async (event) => {
  const { httpMethod, body, pathParameters } = event;

  // MongoDB connection string (replace with your actual connection string)
  const MONGODB_URI = 'mongodb+srv://<username>:<password>@cluster0.mongodb.net/productDB?retryWrites=true&w=majority';

  // Connect to MongoDB (reuse connection if exists)
  if (conn == null) {
    conn = await mongoose.connect(MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
  }

  // Handle CRUD operations based on the HTTP method
  switch (httpMethod) {
    case 'GET':
      return await getProduct(pathParameters.id);

    case 'POST':
      return await createProduct(JSON.parse(body));

    case 'PUT':
      return await updateProduct(pathParameters.id, JSON.parse(body));

    case 'DELETE':
      return await deleteProduct(pathParameters.id);

    default:
      return {
        statusCode: 405,
        body: JSON.stringify({ error: 'Method Not Allowed' }),
      };
  }
};

// CRUD Operation Functions

async function getProduct(id) {
  try {
    const product = await Product.findById(id);
    if (!product) {
      return {
        statusCode: 404,
        body: JSON.stringify({ error: 'Product not found' }),
      };
    }
    return {
      statusCode: 200,
      body: JSON.stringify(product),
    };
  } catch (error) {
    return handleError(error);
  }
}

async function createProduct(data) {
  try {
    const newProduct = new Product(data);
    const savedProduct = await newProduct.save();
    return {
      statusCode: 201,
      body: JSON.stringify(savedProduct),
    };
  } catch (error) {
    return handleError(error);
  }
}

async function updateProduct(id, data) {
  try {
    const updatedProduct = await Product.findByIdAndUpdate(id, data, { new: true });
    return {
      statusCode: 200,
      body: JSON.stringify(updatedProduct),
    };
  } catch (error) {
    return handleError(error);
  }
}

async function deleteProduct(id) {
  try {
    await Product.findByIdAndDelete(id);
    return {
      statusCode: 204,
      body: '',
    };
  } catch (error) {
    return handleError(error);
  }
}

// Error Handling
function handleError(error) {
  console.error(error);
  return {
    statusCode: 500,
    body: JSON.stringify({ error: 'Internal Server Error' }),
  };
}

Step 3: Set Up API Gateway

  1. Navigate to API Gateway in the AWS Console.
  2. Create a new HTTP API.
  3. Set up routes for CRUD operations:
  • GET /products/{id}
  • POST /products
  • PUT /products/{id}
  • DELETE /products/{id}
  1. Link each route to the Lambda function (make sure to enable Lambda Proxy Integration).
  2. Deploy the API and note down the API endpoint.

Step 4: Test the API

You can test your API using Postman, cURL, or even a web browser (for GET requests).

Example API Requests:

  • Create a Product:
curl -X POST https://<api-url>/products \
    -H "Content-Type: application/json" \
    -d '{"name": "Laptop", "description": "A gaming laptop", "price": 1500, "category": "Electronics"}'
  • Get a Product:
curl -X GET https://<api-url>/products/{product_id}
  • Update a Product:
curl -X PUT https://<api-url>/products/{product_id} \
    -H "Content-Type: application/json" \
    -d '{"name": "Updated Laptop", "price": 1200}'
  • Delete a Product:
curl -X DELETE https://<api-url>/products/{product_id}

Step 5: Monitor and Scale

With AWS Lambda, scaling happens automatically. AWS Lambda will scale your function to handle as many requests as needed, based on traffic, so there’s no need for manual scaling.

To monitor your Lambda function’s performance:

  • Use AWS CloudWatch for logging and metrics.
  • Set up alerts for error rates, latency, or execution time limits.

My TechAdvice: While these technologies accelerate development, they also introduce significant dependencies on these tech, which can be detrimental when building enterprise-grade applications. Leverage them to speed up MVP development or demonstrate your design’s capabilities, but ensure you select a robust framework and manage your own databases and servers, making them easily migratable across different cloud platforms.

#AskDushyant
#TechConcept #TechTool #AWS #LamdaFunction
Note: This example pseudo code is for illustration only. You must modify and experiment with the concept to meet your specific needs.

Leave a Reply

Your email address will not be published. Required fields are marked *