DEV Community

Cover image for Writing Event-Driven Serverless Code to Build Scalable Applications
Chirag Aggarwal
Chirag Aggarwal

Posted on

113 7 5 8 8

Writing Event-Driven Serverless Code to Build Scalable Applications

Serverless isn't just trendy—it's rewriting how software scales.

Netflix streams billions of hours without servers. Coca-Cola automates workflows without infrastructure. Figma and T-Mobile ditch downtime. What do they know that you don't?

The secret? Event-driven serverless code. It's the backbone of apps that scale instantly, cut costs, and survive traffic spikes. No servers. No guesswork. Just code that reacts.

This isn't hype—it's a blueprint. Ready to build smarter? Let's break down how event-driven serverless turns scalability from a challenge into a reflex.


Brief Intro to Serverless

Spoiler alert: servers are still there.

There are multiple definitions for this term online, often filled with complex jargon. The best way I like to define it is:

A "fashion" of deploying your code where YOU don't have to think about the servers running your code.

Let's take an example:

Serverless Deployment Diagram

Take Bob. He built mytrashcode.com but panicked at "server setup." He's a developer, not a sysadmin. Instead, he uploaded his code to a cloud provider. They handled security, scaling, and traffic—his site went live. No late-night server meltdowns. No panic during traffic surges. Done.


Why Can't I Manage My Own Servers?

Managing your own servers usually takes one of two paths. You either run physical hardware—like turning an old laptop into a DIY server—or rent a Virtual Private Server (VPS) from providers like DigitalOcean Droplets, Azure VMs, or AWS Lightsail. These fall under IaaS (Infrastructure as a Service), where the cloud company provides the bare-metal infrastructure, but the rest—updates, scaling, security—is entirely up to you.

Does this mean self-managing servers is impossible?

Not at all. Plenty of teams still do it. But managing your own servers comes with a lot of... challenges, including:

  1. Knowing how to manage infrastructure/hardware.
  2. Setting up auto-scaling and downscaling.
  3. Periodically applying system patches and updates to avoid exposing vulnerabilities.
  4. Configuring proxies, SSL certificate generation, network settings, etc.

Dividing Your Code into Functions

Serverless code doesn't need to be monolithic, i.e., all code doesn't need to be in the same place. It can be a collection of bite-sized, event-triggered functions.

A Function is nothing but a set of code that performs a specific task. When writing your entire code serverless, you'll find that you can divide your code into various functions, each handling a specific part of your application. Let's understand this more deeply with an example:

Functions Diagram

When Bob first logs into mytrashcode.com as a new user, the system triggers a "send welcome email" function before redirecting him. Subsequent logins bypass this function entirely, routing him straight to the dashboard. This separation serves a critical purpose—while 99% of users interact solely with the dashboard, isolating secondary functions (like email triggers) enables independent scaling.

Though trivial in this example, the cost implications compound dramatically at scale. Each decoupled function operates on its own resource allocation curve—high-frequency features like dashboard access demand consistent infrastructure, while one-time actions (welcome emails) can scale down during inactive periods. This modular approach prevents overprovisioning for rarely triggered events, even before considering complex systems with hundreds of interdependent functions.


Where to Deploy???

Okay, so just a quick recap—we now know:

  1. Deploying serverless is great!
  2. Dividing your code into functions is modular and scalable.
  3. Functions can be triggered by events.

So, where do you deploy this architecture? Leading platforms like AWS Lambda, Azure Functions, and Google Cloud Functions support it, but we'll focus on Appwrite Functions.

Function Deployment Options

Appwrite, an open-source Backend-as-a-Service (BaaS), bundles authentication, databases, storage, and serverless functions into a single toolkit. This tight integration streamlines deployment—instead of managing fragmented cloud services, Appwrite centralizes backend logic, letting you deploy event-driven functions with minimal overhead. For developers prioritizing simplicity without sacrificing scalability, this unified approach reduces operational friction significantly.

So, let's deploy our function!


Deploying Your First Function

Before writing code, set up your backend on Appwrite:

  1. Go to appwrite.io and register or log in.
  2. Create an organization (if new).
  3. Create a new project.
  4. Copy your Project ID for later use.

Now, let's simulate a server-side project using the node-appwrite package:

  • Create a project directory:
mkdir my-project  
cd my-project  
Enter fullscreen mode Exit fullscreen mode
npm init -y  
appwrite init  
Enter fullscreen mode Exit fullscreen mode
  • Install dependencies:
npm install dotenv node-appwrite  
Enter fullscreen mode Exit fullscreen mode
  • Create your function using the Appwrite CLI:
appwrite init function  
Enter fullscreen mode Exit fullscreen mode

For the runtime, I selected Node 20, but you can choose any runtime.

  • Write your main function in src/main.js:
import dotenv from "dotenv";
import { Account, Client, Users } from "node-appwrite";

dotenv.config();

const client = new Client();
client.setEndpoint("https://cloud.appwrite.io/v1");
client.setProject(process.env.PROJECT_ID);

const users = new Users(client);
const account = new Account(client);

const main = async () => {
  await account.create(
    "test-user",
    "test@test.com",
    "test@123",
    "test",
  );
  const session = await account.createEmailPasswordSession(
    "test@test.com",
    "test@123",
  );
  console.log(session);
};

main();
Enter fullscreen mode Exit fullscreen mode
  • Add a start script in package.json to run node src/main.js.

  • Create a .env file with the required environment variables.

This function simulates a new user creation and login, logging the session details.

Note: Replace the email IDs with actual emails to receive the email.

Functions Architecture

Now, let's set up the function logic. Navigate to functions/your-function where your function resides.

For this demo, we'll use Resend to send emails:

  • Install the resend package:
npm install resend  
Enter fullscreen mode Exit fullscreen mode
  • Update src/main.js with this code:
import { Resend } from 'resend';

// https://appwrite.io/docs/advanced/platform/events
export default async ({ res, req, log }) => {
  const resend = new Resend(process.env.RESEND_API_KEY);

  await resend.emails.send({
    from: 'hello@yourdomain.com',
    to: req.body.email,
    subject: 'Hello!',
    text: 'Hi, its nice to meet you!',
  });

  log('Email sent successfully');

  return res.json({
    success: true,
    message: 'Email sent successfully',
  });
};
Enter fullscreen mode Exit fullscreen mode

You need to set up an account on Resend to get the API Key. Resend also requires you to connect to your own domain to send emails. You can read more about it on the Resend docs.

Now, let's push the created function to the console using:

appwrite push functions  
Enter fullscreen mode Exit fullscreen mode

The final step is to set up the event that connects the two pieces of code together using the users.*.create event:

  1. Go to the Appwrite console and navigate to your created project.
  2. Navigate to the Functions tab.
  3. You should see your newly created function there—click on it.
  4. Go to its settings and under the Events section.
  5. Add a new event to trigger this function: users.*.create.

Appwrite Console showing Events

And... voila! Your program is done. If everything is set up correctly, running your main script should send the newly created user an invitation email. Try it using:

npm run start  
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, serverless architecture is more than just a passing trend—it's a transformative approach to building and scaling modern applications.

Platforms like Appwrite further simplify the process, offering a unified backend solution that integrates seamlessly with serverless functions. Whether you're a solo developer like Bob or part of a larger team, adopting serverless can turn scalability from a daunting challenge into an effortless reflex.

Conclusion Image


Thanks for reading!

You can also connect with me here: https://www.chiragaggarwal.tech/

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (15)

Collapse
 
j_j_b77c6f60020810c325308 profile image
J J

Mmm let's pretend for the billionth time that we're all the same as Netflix, and break down a working monolith serving a small number of users into 1000 functions, which all have to be maintained and spun up independently and which communicate with each other via rest. Won't that be fun! No downsides!

Collapse
 
chiragagg5k profile image
Chirag Aggarwal

I absolutely agree! You never have to compare your projects to giants like Netflix's infrastructure. But case studying these practices help us in understanding why these extremely smart people working for these companies opt for these practices.

Ofcourse there are downsides , mainly complexity which your app might never need. But at their scale, it's a necessity.

Hope that helps!

Collapse
 
siy profile image
Sergiy Yevtushenko

But case studying these practices help us in understanding why these extremely smart people working for these companies opt for these practices.

"Extremely smart" is a stretch, in reality they are just like anyone else. And even if they are actually very smart, it does not mean that they don't make mistakes. Studying their solutions is definitely interesting, but not necessarily useful or applicable anywhere else.

Ofcourse there are downsides , mainly complexity which your app might never need. But at their scale, it's a necessity.

No application needs complexity. The ideal solution must be as simple as possible because complexity is what you pay for every single minute the application is in development or production. Huge companies can address complexity by having large teams, but this rarely a solution for middle or small enterprises (often for big ones too). Moreover, complex solutions mean that people who created it not as smart as you think they are.

Collapse
 
thomas_baseline profile image
Thomas Nixon

Serverless or event driven doesn't mean microservices. Using serverless doesn't have to be complicated, in fact it can be way more simple and cost effective than managing all the overhead needed with server or container based solutions. I used to be sceptical of what serverless could provide in real world application but after the success I have seen with Baseline as well as other startups/scaleups using serverless, it is hard to ignore the technology. If you bundle it up into a monorepo things become much simpler. Serverless has come a long way in recent years, I would suggest looking into it a little more before grouping it in with microservices like issues. Leveraging the cloud is best done by building cloud native solutions. Something like a startup that has limited resources this stuff can be a life saver, scales on demand, cost efficient, easy to hire for, less to maintain, less to secure.

Collapse
 
laztheripper_aa157ff8fbcc profile image
laztheripper

I'm sorry, cost efficient how?
Simple how?

Alright, here's an example of something I can do on a 10$ a month VPS, you tell me how you'd do it serverless and how much it would cost:

Fully dynamic content website, discoverability and SEO for all pages, websockets for live data, under 100ms load times, 100 lighthouse score, able to handle over 100k users on at the same time with no issue, cloudflare ddos protection in front and a reverse proxy that blocks any traffic that isn't forwarded from cloudflare (no bot scans), multiple login methods including oauth, email sending/receiving, postgres with zero latency (local) and redis cache (local).

All of this without cold starts and vendor lock in or using a crappy web UI to configure things.
100k users is enough for 99.9% of websites but if you wanted more, my setup already has nginx as reverse proxy and it could easily be split off to a dedicated vps to handle load balancing.

To be clear, I'm no tech wizard either. I just took a day or two to think about my architecture, and now I can change anything I want about the stack at a moment's notice and at no cost.
Unless I missed a major breakthrough in serverless, doing what I did would be worse in every possible way imaginable, but most importantly, worse for the end user experience.

Also Netflix as the measuring stick is hilarious, their app is notoriously buggy and slow.
Some of it is unavoidable by the nature of the service and how many people use it, but that doesn't excuse the remainder (in reference to the other guy's comment).

Thread Thread
 
thomas_baseline profile image
Thomas Nixon

Use the right tool for the right job, team size, skill sets, problem scope etc. If your needs are met by your solution keep using it, sounds like you have it under control for your case.

I suspect your cost significantly increase once you have more developers, heavier app requirements, multiple clients, integrations, staging and production environment. Don't forget about uptime, reliability and resilience, if your VPS goes down so does your app, AWS serverless will have the redundancy for all the data centers in the region (in mine there are 3 in different cities). So to compare the $10 VPS, you would be looking at least 1 running, 1 redundant, production(so 2), staging(likely 2 but probably 1 right?), so minimum $30 for a real world app and team. Ignoring the scaling, multiple regions, further provisioning and your database replication. There is also the security, backup, monitoring, operational, software/os side of things but you get the idea, there is more to the picture than just what you have mentioned.

I have hosted apps which have a marketing site, user portal, admin portal, integrations, staging & production, 100k users for less than $2 a month, obviously if you add more hungry services it will cost more 💸 I have around 20 example apps up and running that cost less than $10 in total since they only cost me when they are running and exceed the free tier, and they could all scale on demand if needed without any changes. Spiky workloads are perfect for serverless, heavy continuous load might be the time to re-evaluate. Since we are using express anyway it isn't too hard to re-platform.

Vendor lock in, sure, but how often do you change cloud provider really? I really hope that serverless does become less vendor specific in the future so that this becomes a non-issue.

Cold starts are really not much of an problem, worst case usually 700ms, then roughly 75ms including auth, database hit and return trip to user. Actual Lambda compute time is less than 5ms for simple CRUD endpoints. If you are server side rendering, then yes that is not a good idea, but for a SPA on a CDN or mobile app, it is not an issue.

I would love a better solution for server side rendering, you can technically provision/warm lambdas but it can blow out your cost as well. Just look at open nextJS for an example of issues. There are ways around it as well, but they are more complicated than I would like, so I agree on that. Caching could be cheaper for serverless, I haven't seen a great example.

When I started using serverless running locally was an absolute nightmare, Baseline has a nice solution that works well and has everything glued together, it is lighter than running a VM or docker.

Being IaC you can still have a new stack at a moments notice and with no cost, not sure what you mean here.

I used to think server/container based solutions were simple and made a lot more sense but since using serverless for real world applications it has easily become my preferred solution and has been more cost effective. There is just so much less to worry about and maintain, e.g. network, scaling, patching/updates, monitoring. However there are things that still do not make sense for serverless, and that is fine, it isn't the answer for everything.

Either way there is always tradeoffs no matter which solution you pick.

Worth a try if it is as easy I say right?

Collapse
 
siy profile image
Sergiy Yevtushenko

No progress in serverless makes it simpler to test, for example.

Thread Thread
 
thomas_baseline profile image
Thomas Nixon

What are you testing? It shouldn't be more difficult than for other architectures since unit tests are for the code not the architecture.

Thread Thread
 
siy profile image
Sergiy Yevtushenko

Do you do only unit testing? How about integration testing?

Thread Thread
 
thomas_baseline profile image
Thomas Nixon

It depends, I do what makes sense for an application, if I am prototyping an idea I am not going to write integration test, if I need to create something that absolutely cannot go down I would implement some pretty robust tests and of course build, lint, cfn-lint and something like synk for security. Just don't go too far to test for the sake of testing. If you are using mocks to do integration testing you are still just testing code. However you should be using a pre-prod environment to deploy to first regardless (built into Baseline already). You could run it locally in the CI/CD pipeline and run tests. Alternatively deploy to the cloud and run end to end tests, which would be much closer to a prod like environment. Especially good if you need to test performance. CloudFormation will automatically roll back if it fails during deploy as well, the Serverless framework will catch issues in your IaC before it deploys as well. If you need more than staging & prod environments than just spin up another one since your setup is repeatable with IaC. You could argue that running locally isn't the same as running it in the cloud, sure, I agree - especially around IAM permissions, however it is probably going to catch most of your issues, either way you can just deploy it to the cloud and test. Do you have a specific issue that you have faced with testing serverless? I would be interested to know what your experience was.

Thread Thread
 
siy profile image
Sergiy Yevtushenko

As you already described, this is getting too complicated overall. Testing of the regular apps is much simpler infrastructure-wise.
About mocks: any mock makes assumptions about the service it mocks. If assumptions don't match real behavior or real behavior changes, we're in trouble because tests are no longer relevant (and we don't know that).

Collapse
 
thomas_baseline profile image
Thomas Nixon

Great article! Pretty interesting setup. Baseline does something similar but it feels like working on a monolith but deploys out into grouped endpoint functions and event driven lambdas. Great technology to build apps on no matter which provider you go with. Something like Baseline just gets you to a working app a bit quicker.

Collapse
 
gauravsinghbisht profile image
Gaurav Singh Bisht

👍

Collapse
 
rohan_sharma profile image
Rohan Sharma

Liked it!!! Will try it soon

Collapse
 
chiragagg5k profile image
Chirag Aggarwal

Absolutely do!

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay