DEV Community

Tuncay
Tuncay

Posted on

From Zero to 🚀: Building a Serverless Task Manager with React, Node.js, and AWS

From Zero to 🚀: Building a Serverless Task Manager with React, Node.js, and AWS

TL;DR:
You’ll spin up a serverless to‑do app using React (frontend), AWS API Gateway + Lambda (backend), and DynamoDB (storage). Along the way, you’ll cover API design, simple auth, CI/CD with GitHub Actions, and infrastructure-as-code with the Serverless Framework. By the end, you’ll have a live demo, a polished README, and deploy badges for your GitHub profile.


Why You Should Try This

  • Full‑stack spectrum: Frontend, backend, database, CI/CD and infra‑as‑code all in one project.
  • Serverless is everywhere: From scrappy startups to big enterprise, “no‑ops” architectures are in demand.
  • Resume‑worthy outcome: A live app you can demo, plus build/test/deploy badges for your README.

What You’ll Need

  • Node.js & npm (v16+ recommended)
  • AWS account with rights to deploy Lambda, API Gateway, DynamoDB
  • Serverless Framework (or AWS SAM)—we’ll use Serverless here
  • GitHub repo to host code + Actions workflows
  • Basic React and JS skills

Table of Contents

  1. Project Kickoff & Architecture
  2. Scaffolding the React Frontend
  3. Creating the Serverless API
  4. Defining the Data Layer (DynamoDB)
  5. Adding Simple Auth
  6. CI/CD with GitHub Actions
  7. Infra as Code: serverless.yml
  8. Polishing Your README
  9. Next Steps & Ideas

1. Project Kickoff & Architecture

Before you write a single line of code, sketch out:

React App
   ↓ HTTPS
API Gateway
   ↓ Event JSON
Lambda (Node.js)
   ↓ AWS SDK
DynamoDB Table
Enter fullscreen mode Exit fullscreen mode
  1. Create a new GitHub repo called serverless-task-manager.
  2. In your local folder, run:
   git init
   npx create-react-app frontend
   mkdir backend
   cd backend
   npx serverless create --template aws-nodejs --path .
   cd ..
   git add .
   git commit -m "Scaffold frontend + backend"
Enter fullscreen mode Exit fullscreen mode
  1. Push to GitHub:
   git remote add origin [email protected]:YOUR_USERNAME/serverless-task-manager.git
   git push -u origin main
Enter fullscreen mode Exit fullscreen mode

2. Scaffolding the React Frontend

Inside the frontend folder:

  1. Install dependencies:
   cd frontend
   npm install axios tailwindcss@latest postcss@latest autoprefixer@latest
   npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode
  1. Configure Tailwind: in tailwind.config.js:
   module.exports = {
     content: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
     theme: { extend: {} },
     plugins: [],
   };
Enter fullscreen mode Exit fullscreen mode

Add to src/index.css:

   @tailwind base;
   @tailwind components;
   @tailwind utilities;
Enter fullscreen mode Exit fullscreen mode
  1. Build components:
  • TaskList.js: fetches GET /tasks
  • TaskForm.js: POST /tasks
  • TaskItem.js: toggle complete → PUT /tasks/{id}

Example TaskForm.js:

   import { useState } from 'react';
   import axios from 'axios';

   function TaskForm({ onAdded }) {
     const [text, setText] = useState('');
     const submit = async e => {
       e.preventDefault();
       if (!text) return;
       await axios.post(process.env.REACT_APP_API_URL + '/tasks', { text });
       setText('');
       onAdded();
     };
     return (
       <form onSubmit={submit} className="flex gap-2">
         <input
           value={text}
           onChange={e => setText(e.target.value)}
           className="border p-2 flex-grow"
           placeholder="New task..."
         />
         <button type="submit" className="bg-blue-500 text-white px-4 rounded">
           Add
         </button>
       </form>
     );
   }

   export default TaskForm;
Enter fullscreen mode Exit fullscreen mode
  1. Environment variable: add .env in frontend:
   REACT_APP_API_URL=https://YOUR_API_GATEWAY.execute-api.us-east-1.amazonaws.com/dev
Enter fullscreen mode Exit fullscreen mode

3. Creating the Serverless API

In the backend folder:

  1. Install helpers:
   cd backend
   npm install aws-sdk uuid
Enter fullscreen mode Exit fullscreen mode
  1. Define handlers in handler.js:
   const AWS = require('aws-sdk');
   const { v4: uuid } = require('uuid');
   const db = new AWS.DynamoDB.DocumentClient();
   const TABLE = process.env.TASKS_TABLE;

   module.exports.list = async () => {
     const { Items } = await db.scan({ TableName: TABLE }).promise();
     return { statusCode: 200, body: JSON.stringify(Items) };
   };

   module.exports.create = async event => {
     const { text } = JSON.parse(event.body);
     const item = { id: uuid(), text, done: false };
     await db.put({ TableName: TABLE, Item: item }).promise();
     return { statusCode: 201, body: JSON.stringify(item) };
   };

   module.exports.toggle = async event => {
     const { id } = event.pathParameters;
     const { done } = JSON.parse(event.body);
     const updated = await db.update({
       TableName: TABLE,
       Key: { id },
       UpdateExpression: 'set done = :d',
       ExpressionAttributeValues: { ':d': done },
       ReturnValues: 'ALL_NEW',
     }).promise();
     return { statusCode: 200, body: JSON.stringify(updated.Attributes) };
   };
Enter fullscreen mode Exit fullscreen mode
  1. serverless.yml (we’ll expand infra‑as‑code later). For now, register three functions (list, create, toggle) under the /tasks path.

4. Defining the Data Layer (DynamoDB)

  1. In serverless.yml, add:
   provider:
     name: aws
     runtime: nodejs16.x
     environment:
       TASKS_TABLE: ${self:service}-tasks-${sls:stage}
   resources:
     Resources:
       TasksTable:
         Type: AWS::DynamoDB::Table
         Properties:
           TableName: ${self:service}-tasks-${sls:stage}
           AttributeDefinitions:
             - AttributeName: id
               AttributeType: S
           KeySchema:
             - AttributeName: id
               KeyType: HASH
           BillingMode: PAY_PER_REQUEST
Enter fullscreen mode Exit fullscreen mode
  1. This creates a DynamoDB table keyed by id and lets your Lambdas read/write without provisioning throughput.

5. Adding Simple Auth

You have two main paths:

  • JWT: issue tokens on a /login endpoint (in-memory user map or Cognito).
  • Cognito: offload user pool + federated logins.

For brevity, you can stub a single “demo” user in handler.js:

module.exports.login = async event => {
  const { username, password } = JSON.parse(event.body);
  if (username === 'demo' && password === 'demo') {
    const token = Buffer.from('demo:demo').toString('base64');
    return { statusCode: 200, body: JSON.stringify({ token }) };
  }
  return { statusCode: 401 };
};
Enter fullscreen mode Exit fullscreen mode

Then in your React app, store that token in localStorage and attach Authorization: Bearer ${token} to your Axios calls. In serverless.yml, add a simple API key or Lambda authorizer to enforce it.


6. CI/CD with GitHub Actions

Create .github/workflows/ci.yml:

name: CI/CD

on:
  push:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install & Test Frontend
        working-directory: frontend
        run: |
          npm ci
          npm test -- --watchAll=false
      - name: Install & Test Backend
        working-directory: backend
        run: |
          npm ci
          npm test # if you write tests
  deploy:
    needs: build-and-test
    runs-on: ubuntu-latest
    environment:
      name: production
    steps:
      - uses: actions/checkout@v3
      - name: Deploy Serverless
        working-directory: backend
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: npx serverless deploy --stage prod
Enter fullscreen mode Exit fullscreen mode

After you add this, your README can show:

[![Build & Test](https://github.com/YOUR_USERNAME/serverless-task-manager/actions/workflows/ci.yml/badge.svg)](https://github.com/YOUR_USERNAME/serverless-task-manager/actions)
[![Deploy](https://github.com/YOUR_USERNAME/serverless-task-manager/actions/workflows/ci.yml/badge.svg?event=deployment)](https://github.com/YOUR_USERNAME/serverless-task-manager/actions)
Enter fullscreen mode Exit fullscreen mode

7. Infra as Code: serverless.yml

Here’s a more complete serverless.yml to drop into your backend:

service: serverless-task-manager

provider:
  name: aws
  runtime: nodejs16.x
  region: us-east-1
  environment:
    TASKS_TABLE: ${self:service}-tasks-${sls:stage}

functions:
  list:
    handler: handler.list
    events:
      - http:
          path: tasks
          method: get
          cors: true
  create:
    handler: handler.create
    events:
      - http:
          path: tasks
          method: post
          cors: true
          authorizer: NONE 
  toggle:
    handler: handler.toggle
    events:
      - http:
          path: tasks/{id}
          method: put
          cors: true

resources:
  Resources:
    TasksTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:service}-tasks-${sls:stage}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        BillingMode: PAY_PER_REQUEST
Enter fullscreen mode Exit fullscreen mode

8. Polishing Your README

  1. Project title & description at top.
  2. Badges (build, test, deploy).
  3. Quick‑start:
   git clone https://github.com/YOUR_USERNAME/serverless-task-manager.git
   cd serverless-task-manager
   cd frontend && npm ci && npm start
   cd ../backend && npm ci && npx serverless deploy
Enter fullscreen mode Exit fullscreen mode
  1. Screenshots or GIFs of your app in action.
  2. Live demo link (e.g. https://yourdomain.com).
  3. Next steps: invite contributions, list TODOs.

9. Next Steps & Ideas

  • Add offline support (Service Workers + IndexedDB syncing).
  • Swap DynamoDB for PostgreSQL on RDS (to show SQL chops).
  • Integrate WebSockets via API Gateway for live updates.
  • Build a mobile companion with React Native.

Top comments (0)