<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Onayade Mogboluwaga Tolulope</title>
    <description>The latest articles on Forem by Onayade Mogboluwaga Tolulope (@musliu2kul).</description>
    <link>https://forem.com/musliu2kul</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1441488%2F5d4ea654-d33a-4d8e-9b1e-a46d2228e8cf.jpeg</url>
      <title>Forem: Onayade Mogboluwaga Tolulope</title>
      <link>https://forem.com/musliu2kul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/musliu2kul"/>
    <language>en</language>
    <item>
      <title>TicketCraft: Dynamic Ticket Generator</title>
      <dc:creator>Onayade Mogboluwaga Tolulope</dc:creator>
      <pubDate>Sun, 06 Apr 2025 00:10:31 +0000</pubDate>
      <link>https://forem.com/musliu2kul/ticketcraft-dynamic-ticket-generator-3kge</link>
      <guid>https://forem.com/musliu2kul/ticketcraft-dynamic-ticket-generator-3kge</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/pulumi"&gt;Pulumi Deploy and Document Challenge&lt;/a&gt;: Fast Static Website Deployment&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;While working as a backend developer for AIESEC in Nigeria, I encountered a challenge during conference registrations: creating personalized tickets for attendees. Traditional methods, like writing extensive MJML templates, felt cumbersome and time-consuming. So, I decided to treat the tickets as images and used Python libraries: Pillow (PIL) for image manipulation and FastAPI to create a web server to host the images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Demo Link
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://c8ffygl2f3.execute-api.us-east-1.amazonaws.com/docs" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/MogboPython/conference_tickets_server" rel="noopener noreferrer"&gt;Project repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;Instead of continuing with template-heavy solutions, I reimagined tickets as dynamic images that could be generated on-demand. I developed a FastAPI service that leverages Python's Pillow library to create personalized conference tickets through these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Architecture Design&lt;/strong&gt;: I designed a modular system where conference configurations could be easily defined and managed separately from the core ticket generation logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Modeling&lt;/strong&gt;: Using Pydantic, I created structured schemas (&lt;code&gt;TicketConfig&lt;/code&gt; and &lt;code&gt;ServiceConfig&lt;/code&gt;) to ensure data validation and maintain consistent ticket generation across different events.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Text Rendering&lt;/strong&gt;: One of my key innovations was developing an algorithm that automatically adjusts font size and text placement to accommodate attendee names of varying lengths.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One particularly challenging issue I faced was that attendee names vary a lot in length, and I needed to ensure the names were displayed properly without the text overflowing or looking disproportionate. So I implemented an adaptive text sizing algorithm that starts with the maximum font size, measures the text width with Pillow's multiline_textbbox function and progressively reduces font size until the text fits within the specified area. It also splits text across multiple lines when necessary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5qusj28jshue8sgizak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5qusj28jshue8sgizak.png" alt="Algorithm for managing varying name length" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Working on this project was my first time working with Pillow and it deepened my understanding of programmatic image manipulation. And later on when I wanted to deploy to AWS for the first time it was the easiest project I code use for a test run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Pulumi
&lt;/h2&gt;

&lt;p&gt;This was my first time deploying to AWS, and I found myself struggling with both the AWS console interface and Terraform’s syntax for infrastructure as code. Then a friend introduced me to Pulumi, which lets you define your cloud infrastructure using Python—a language I was already comfortable with. This significantly reduced the learning curve of mastering Terraform’s HCL. By following this &lt;a href="https://www.pulumi.com/blog/serverless-api/" rel="noopener noreferrer"&gt;article&lt;/a&gt;, I was able to understand key AWS resources like Lambda functions, API Gateway, and IAM roles—what they do and how they work together—all in a language I already knew.&lt;/p&gt;

&lt;p&gt;In my implementation, I used Pulumi to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create an API Gateway to expose my FastAPI endpoints&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9srs3wjf04d87u1f2ant.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9srs3wjf04d87u1f2ant.png" alt="Create an API Gateway" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up proper IAM roles and permissions&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77ps35vwi8c7llyetydd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77ps35vwi8c7llyetydd.png" alt="Create IAM role for Lambda" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure a Lambda function using my containerized application and establish a Docker image pipeline with ECR (Elastic Container Registry&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi63aboxhom9v7dbeufv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi63aboxhom9v7dbeufv.png" alt="Configure Lambda function" width="800" height="611"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then create and connect an API Gateway stage to Lambda and finally export the API Gateway URL&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbqdi9xwihfyk8vgnckj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbqdi9xwihfyk8vgnckj.png" alt="Create API Gateway stage and export the API Gateway URL" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One challenge I faced was that Pulumi kept failing to build my Docker image when I ran pulumi up. This was because my infrastructure code was inside a subfolder within my main project directory, and the default build context didn’t include everything it needed. Later, I discovered that you can explicitly define the Docker build context in your Pulumi code—which solved the issue and, honestly, I found that really cool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhisq37qm99m4ebsvsz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhisq37qm99m4ebsvsz7.png" alt="Context issue fix" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>pulumichallenge</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
