Full Stack Development

Server-less



by Tomas Trescak t.trescak@westernsydney.edu.au

Serverless

/

Introduction

  • Scaling Deployments
  • Serverless: No server hassle, auto-scaling magic
  • Serverless, Lambda, Edge ...

Serverless

/

What is it?

  • Serveless ≠ No Servers
  • Functions run on demand, scale automatically
  • Pay only for what you use
https://medium.com/theburningmonk-com/even-simple-serverless-applications-have-complex-architecture-diagrams-so-what-8dc618fd4df6

Serverless

/

AWS Lambda

    import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { DynamoDBClient, ScanCommand } from '@aws-sdk/client-dynamodb';
import { unmarshall } from '@aws-sdk/util-dynamodb';

const client = new DynamoDBClient({ region: 'us-east-1' });

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
  const tag = event.queryStringParameters?.tag || '';
  const params = {
    TableName: 'BlogPosts',
    FilterExpression: tag ? 'contains(tags, :tag)' : undefined,
    ExpressionAttributeValues: tag ? { ':tag': { S: tag } } : undefined,
  };

  try {
    const result = await client.send(new ScanCommand(params));
    const posts = result.Items?.map(item => unmarshall(item)) || [];
    return {
      statusCode: 200,
      body: JSON.stringify(posts),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Failed to fetch posts' }),
    };
  }
};
  
  • AWS Lambda: Small, event-driven functions
  • Scales big, integrates with AWS
  • Watch out: Price, Cold starts, complex setup

Serverless

/

Edge Functions

    import type { NextRequest, NextResponse } from 'next/server';

export const config = {
  runtime: 'edge',
};

export default async function handler(req: NextRequest): Promise<NextResponse> {
  const url = new URL(req.url);
  const tag = url.pathname.slice(1); // e.g., "tech" from "/tech"
  const country = req.headers.get('x-vercel-ip-country') || 'US';

  if (tag) {
    url.pathname = '/api/posts';
    url.searchParams.set('tag', tag);
    url.searchParams.set('country', country);
    const response = await fetch(url);
    return new NextResponse(response.body, {
      status: response.status,
      headers: { 'x-country': country },
    });
  }
  return new NextResponse('Invalid tag', { status: 400 });
}
  
  • Run at CDN edge, super-fast
  • Lightweight, for routing or personalisation
  • Configure with runtime: 'edge'

Serverless

/

❌ Failing Edge Functions

    import type { NextRequest, NextResponse } from 'next/server';

export const config = {
  runtime: 'edge',
};

export default async function handler(req: NextRequest): Promise<NextResponse> {
  const url = new URL(req.url);
  const tag = url.pathname.slice(1);

  if (tag) {
    // Bad: Slow API and heavy loop
    const posts = await fetch('https://slow-api.com/posts').then(res => res.json());
    const filteredPosts = posts
      .filter((post: any) => post.tags.includes(tag))
      .map((post: any) => heavyComputation(post));
    return new NextResponse(JSON.stringify(filteredPosts), { status: 200 });
  }
  return new NextResponse('Invalid tag', { status: 400 });
}

function heavyComputation(post: any): string {
  let result = '';
  for (let i = 0; i < 1000000; i++) {
    result += post.title;
  }
  return result;
}
  
  • Limited resources, strict timeouts
  • Don’t overload with complex logic
  • Example: Heavy computation fails

Serverless

/

How to Deploy?

  • Vercel = Automatic
  • AWS = Configuration Necessary
  • Azure ...

Serverless

/

Key Takeaways

  • Serverless scales, saves hassle
  • Lambda: Full backend, but cold starts
  • Edge: Fast, but keep it light
  • Next.js: Full app, needs setup on AWS
  • Pitfall: Monitor costs, test well