<?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>DEV Community: Pedro Oliveira</title>
    <description>The latest articles on DEV Community by Pedro Oliveira (@pedrosoaresll).</description>
    <link>https://dev.to/pedrosoaresll</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%2F170305%2Fc38a5fef-afeb-42cf-9543-87ffaebb40db.jpg</url>
      <title>DEV Community: Pedro Oliveira</title>
      <link>https://dev.to/pedrosoaresll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pedrosoaresll"/>
    <language>en</language>
    <item>
      <title>A 100% Serverless WhatsApp Agent: Go + AWS Lambda + EventBridge</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Thu, 11 Dec 2025 02:57:41 +0000</pubDate>
      <link>https://dev.to/pedrosoaresll/a-100-serverless-whatsapp-agent-go-aws-lambda-eventbridge-3ohf</link>
      <guid>https://dev.to/pedrosoaresll/a-100-serverless-whatsapp-agent-go-aws-lambda-eventbridge-3ohf</guid>
      <description>&lt;h2&gt;
  
  
  Building a WhatsApp Cloud API Agent on AWS Lambda Using Go
&lt;/h2&gt;

&lt;p&gt;In this post I want to share how we designed a WhatsApp Cloud API agent that is &lt;strong&gt;100% serverless&lt;/strong&gt;, built with &lt;strong&gt;Go&lt;/strong&gt; and &lt;strong&gt;AWS&lt;/strong&gt;. The goal was to keep costs extremely low while still being ready for &lt;strong&gt;large-scale message traffic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project powers an automated commercial agent for Barx, talking to customers over WhatsApp, powered by OpenAI Agents behind the scenes.&lt;/p&gt;




&lt;h3&gt;
  
  
  High-level architecture
&lt;/h3&gt;

&lt;p&gt;Everything runs serverlessly on AWS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway HTTP API (v2)&lt;/strong&gt;: single entry point for the WhatsApp webhook and auxiliary endpoints (verification + Swagger).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda functions (Go, &lt;code&gt;provided.al2023&lt;/code&gt;)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wpp-verification&lt;/code&gt;: handles the &lt;strong&gt;Webhook verification&lt;/strong&gt; challenge from WhatsApp Cloud API (GET &lt;code&gt;/webhook/messages&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;messages-webhook&lt;/code&gt;: receives &lt;strong&gt;incoming messages&lt;/strong&gt; (POST &lt;code&gt;/webhook/messages&lt;/code&gt;), validates &amp;amp; normalizes payloads, and schedules answer processing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;answer-handler&lt;/code&gt;: processes messages in batch, calls the &lt;strong&gt;OpenAI Agent&lt;/strong&gt; workflow, and sends responses back to WhatsApp.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;swagger-ui&lt;/code&gt; / &lt;code&gt;get-swagger&lt;/code&gt;: serve API docs for internal use.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Amazon EventBridge Scheduler&lt;/strong&gt;: used as a &lt;strong&gt;buffer + delay mechanism&lt;/strong&gt; to decouple message ingestion from answer generation.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;DynamoDB&lt;/strong&gt;: single table (referenced by name from CDK) storing messages, conversation memory, and related entities.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;: stores WhatsApp Cloud API credentials and related secrets (token, phone number ID, API version, etc.).&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The CDK stack (&lt;code&gt;cdk/lib/serverless-go-app-stack.ts&lt;/code&gt;) wires all of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates the &lt;strong&gt;HTTP API&lt;/strong&gt; and routes to each Lambda via &lt;code&gt;HttpLambdaIntegration&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Injects common environment variables into each Lambda (stage, region, table name, secret name, OpenAI API key).&lt;/li&gt;
&lt;li&gt;Grants &lt;strong&gt;fine-grained IAM permissions&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;secretsmanager:GetSecretValue&lt;/code&gt; only for the configured secret.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dynamodb:PutItem&lt;/code&gt;, &lt;code&gt;GetItem&lt;/code&gt;, &lt;code&gt;UpdateItem&lt;/code&gt;, etc. only on the WhatsApp agent table.&lt;/li&gt;
&lt;li&gt;EventBridge Scheduler permissions for the webhook Lambda to create/update/delete schedules and &lt;code&gt;iam:PassRole&lt;/code&gt; for the scheduler role.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Reuses &lt;strong&gt;log groups&lt;/strong&gt; with a short retention (3 days) to control CloudWatch costs.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Code architecture (clean-ish layers)
&lt;/h3&gt;

&lt;p&gt;The Go service is structured in layers under &lt;code&gt;services/agent&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;internal/application&lt;/code&gt;: &lt;strong&gt;use cases&lt;/strong&gt; (orchestrators) for each business flow.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal/domain&lt;/code&gt;: &lt;strong&gt;entities&lt;/strong&gt; and &lt;strong&gt;repository interfaces&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal/infra&lt;/code&gt;: concrete implementations (WhatsApp client, OpenAI agent, DynamoDB repositories, scheduler, config, logging).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal/interface/http&lt;/code&gt;: HTTP/Lambda controllers that adapt API Gateway events to use cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Incoming messages flow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;WhatsApp Cloud API sends a POST to &lt;code&gt;/webhook/messages&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;API Gateway forwards it to the &lt;code&gt;messages-webhook&lt;/code&gt; Lambda.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;receive_message&lt;/code&gt; controller (&lt;code&gt;internal/interface/http/receive_message/controller.go&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;Logs the raw payload.&lt;/li&gt;
&lt;li&gt;Unmarshals into a &lt;code&gt;WhatsAppWebhook&lt;/code&gt; model.&lt;/li&gt;
&lt;li&gt;Maps it into one or more &lt;code&gt;ReceiveMessageCommand&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;For each command, calls &lt;code&gt;ReceiveMessageUseCase.Execute&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ReceiveMessageUseCase&lt;/code&gt; (&lt;code&gt;internal/application/receive_message/use_case.go&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;Persists the message in a &lt;code&gt;MessageRepository&lt;/code&gt; (backed by DynamoDB).&lt;/li&gt;
&lt;li&gt;Schedules an answer using a &lt;code&gt;SchedulerAnswerRepository&lt;/code&gt; that wraps &lt;strong&gt;EventBridge Scheduler&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If a schedule already exists, it handles &lt;code&gt;ConflictException&lt;/code&gt; and &lt;strong&gt;updates&lt;/strong&gt; the schedule instead of failing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key idea: &lt;strong&gt;every message creates (or updates) a schedule&lt;/strong&gt; that will later trigger &lt;code&gt;answer-handler&lt;/code&gt; with the serialized &lt;code&gt;Message&lt;/code&gt; as input. This naturally batches multiple messages from the same user within a short window and avoids hammering OpenAI for each keystroke.&lt;/p&gt;

&lt;h4&gt;
  
  
  Answer generation flow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;EventBridge Scheduler invokes the &lt;code&gt;answer-handler&lt;/code&gt; Lambda with the serialized &lt;code&gt;Message&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;answerUseCase&lt;/code&gt; (&lt;code&gt;internal/application/answer/use_case.go&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;Sends a &lt;strong&gt;typing indicator&lt;/strong&gt; via WhatsApp Cloud API to improve UX.&lt;/li&gt;
&lt;li&gt;Loads conversation memory from a &lt;code&gt;MemoryRepository&lt;/code&gt; (DynamoDB).&lt;/li&gt;
&lt;li&gt;Lists pending user messages for that user ID.&lt;/li&gt;
&lt;li&gt;Concatenates the messages into a single prompt and calls the &lt;strong&gt;Agent client&lt;/strong&gt; (&lt;code&gt;AgentClient&lt;/code&gt;) wrapping &lt;code&gt;openai-agents-go&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Saves back the new &lt;code&gt;LastResponseID&lt;/code&gt; to memory to keep the conversation context alive.&lt;/li&gt;
&lt;li&gt;Sends one or more WhatsApp text messages with the agent’s answer.&lt;/li&gt;
&lt;li&gt;Deletes processed user messages in batch.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  WhatsApp Cloud API client
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;internal/infra/whatsapp/client.go&lt;/code&gt; implements &lt;code&gt;CloudAPIRepository&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads WhatsApp Cloud API settings from &lt;strong&gt;Secrets Manager&lt;/strong&gt; (version, phone number ID, access token).&lt;/li&gt;
&lt;li&gt;Sends &lt;strong&gt;text messages&lt;/strong&gt; and &lt;strong&gt;typing indicators&lt;/strong&gt; by POSTing JSON payloads to the Graph API.&lt;/li&gt;
&lt;li&gt;Logs both error states and successful responses, but returns simple Go errors to the application layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  OpenAI Agent integration
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;internal/infra/agent/client.go&lt;/code&gt; integrates with &lt;code&gt;openai-agents-go&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines a typed &lt;code&gt;SchemaOutput&lt;/code&gt; describing the structured response (&lt;code&gt;messages[] { content }&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Configures the Agent with:

&lt;ul&gt;
&lt;li&gt;Name and &lt;strong&gt;instructions&lt;/strong&gt; (&lt;code&gt;AgentInstructions&lt;/code&gt;) tailored to the Barx commercial assistant.&lt;/li&gt;
&lt;li&gt;Model (&lt;code&gt;gpt-5&lt;/code&gt; in this codebase), reasoning settings, and metadata.&lt;/li&gt;
&lt;li&gt;Tools: file search (vector store) and web search constrained to &lt;code&gt;barx.com.br&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Exposes a &lt;code&gt;RunWorkflow&lt;/code&gt; method that returns a generic &lt;code&gt;AgentOutput[SchemaOutput]&lt;/code&gt; with both the response and &lt;code&gt;LastResponseID&lt;/code&gt; to keep the conversation thread.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why 100% serverless?
&lt;/h3&gt;

&lt;p&gt;This architecture was intentionally designed to be &lt;strong&gt;fully serverless&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No containers, no servers&lt;/strong&gt; to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale-to-zero&lt;/strong&gt; behavior: when there is no WhatsApp traffic, there are essentially no compute costs.&lt;/li&gt;
&lt;li&gt;Cloud-native services (API Gateway, Lambda, EventBridge Scheduler, DynamoDB, Secrets Manager) all scale &lt;strong&gt;horizontally&lt;/strong&gt; with traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, we can support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spiky traffic&lt;/strong&gt; (promotions, campaigns, peak hours) without pre-provisioning instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-tail usage&lt;/strong&gt; across many establishments, each generating low but unpredictable traffic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pros of this architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost-efficient&lt;/strong&gt;: pay-per-invocation Lambdas + DynamoDB on-demand + short log retention keep monthly costs under control, especially for early-stage products.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naturally scalable&lt;/strong&gt;: API Gateway + Lambda + EventBridge Scheduler handle thousands of concurrent conversations without manual tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilient conversation pipeline&lt;/strong&gt;: storing messages and memory in DynamoDB plus scheduled triggers means transient failures usually don’t lose user messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean separation of concerns&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;interface/http&lt;/code&gt; for Lambda controllers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;application&lt;/code&gt; for orchestration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;domain&lt;/code&gt; for entities and interfaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;infra&lt;/code&gt; for AWS + external integrations (WhatsApp, OpenAI, Secrets Manager, DynamoDB, Scheduler)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Vendor-optimized integrations&lt;/strong&gt;: using EventBridge Scheduler for delayed processing instead of hand-rolled CRON, and Secrets Manager for credentials, keeps security and operations simpler.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Cons and trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cold starts&lt;/strong&gt;: Go on Lambda is generally fast, but with multiple functions (webhook, answer handler, Swagger) you can still feel cold start latency for very low-traffic tenants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed debugging&lt;/strong&gt;: tracing a single user interaction across API Gateway, Scheduler, multiple Lambdas, and DynamoDB can be harder than in a monolithic app unless you invest in end-to-end observability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational complexity at the cloud layer&lt;/strong&gt;: IAM, Scheduler permissions (&lt;code&gt;iam:PassRole&lt;/code&gt;), secrets, and environment variables must all be configured correctly; CDK helps, but misconfigurations can be subtle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited local emulation&lt;/strong&gt;: fully reproducing API Gateway + Scheduler + WhatsApp Webhooks locally is non-trivial; most realistic testing still happens in the cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling to AWS&lt;/strong&gt;: the design uses AWS-native services heavily (Scheduler, Secrets Manager, DynamoDB, Lambda), so moving to another cloud would require a serious rewrite.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  What would you improve?
&lt;/h3&gt;

&lt;p&gt;This architecture has worked well for us so far: low cost, simple scaling, and a clean Go codebase with clear boundaries between HTTP, application, domain, and infrastructure.&lt;/p&gt;

&lt;p&gt;If you’ve built something similar (WhatsApp bots, agentic backends, or serverless messaging pipelines), I’d love to hear from you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How would you &lt;strong&gt;improve&lt;/strong&gt; this design?&lt;/li&gt;
&lt;li&gt;What would you change to make it &lt;strong&gt;more resilient&lt;/strong&gt; or &lt;strong&gt;easier to operate&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Any patterns you recommend for &lt;strong&gt;observability&lt;/strong&gt; or &lt;strong&gt;testing&lt;/strong&gt; in this kind of serverless setup?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Share your thoughts, ideas, or questions in the comments — I’m happy to iterate on this architecture with the community’s feedback.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>go</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
    <item>
      <title>How I Designed a Zero-Waste, Event-Driven Analytics Pipeline Using DynamoDB TTL and AWS Lambda</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Thu, 11 Dec 2025 02:14:52 +0000</pubDate>
      <link>https://dev.to/pedrosoaresll/how-i-designed-a-zero-waste-event-driven-analytics-pipeline-using-dynamodb-ttl-and-aws-lambda-l29</link>
      <guid>https://dev.to/pedrosoaresll/how-i-designed-a-zero-waste-event-driven-analytics-pipeline-using-dynamodb-ttl-and-aws-lambda-l29</guid>
      <description>&lt;p&gt;It is time to get the data Barx is generating and bring it to the barbers. My first option was to create a cron job that runs every Monday at 3 am UTC. When my lambda triggers, I search for all barbershops in DynamoDB and start the analytics calculation for each one.&lt;/p&gt;

&lt;p&gt;However, this approach presents some resource and cost challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scanning the entire DynamoDB table is resource-intensive and can be costly.&lt;/li&gt;
&lt;li&gt;A single lambda function handles a heavy workload, which may impact performance and scalability.&lt;/li&gt;
&lt;li&gt;Every Monday, the process attempts to retrieve all barbershops, regardless of their activity status.&lt;/li&gt;
&lt;li&gt;Analytics calculations are performed for all barbershops, including those without any activity in the previous week, leading to unnecessary resource consumption.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Probably there are more problems, but I won't ask for AI to make it more dramatic...&lt;/p&gt;

&lt;p&gt;The point is, I would like to be able to spend resources only with those barbershops that had activities from last week and recalculate their analysis regarding the last 7, 30, 60, and 90 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture proposal
&lt;/h2&gt;

&lt;p&gt;Thinking on that, I built this architecture:&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%2Fd755kkmfhcbewdudunag.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%2Fd755kkmfhcbewdudunag.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks simple, but it helps me to schedule a job only for those barbershops that had some activity, so I create a record in DynamoDB to expire next Monday at 3 am UTC.&lt;/p&gt;

&lt;p&gt;When the record is deleted because TTL rule, it triggers another lambda that shares the data of that barbershop that was scheduled to start in a dedicated lambda for its analytic calculation.&lt;/p&gt;

&lt;p&gt;This approach helps me to spend resources only with users who are producing data, not those who are not producing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits I thought:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Analytics calculations are performed only for barbershops with recent activity, optimizing resource usage.&lt;/li&gt;
&lt;li&gt;Scheduled records in DynamoDB are automatically cleaned up via TTL, reducing manual maintenance.&lt;/li&gt;
&lt;li&gt;The architecture scales efficiently, supporting any number of users without additional complexity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Preocupations I have:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;If many records expire at the same time, a large number of lambdas may be triggered simultaneously, leading to a spike in concurrent executions. (But I can handle this, configuring better DynamoDB Stream batch size and parallelization factor).&lt;/li&gt;
&lt;li&gt;There may be a delay between the scheduled expiration time and the actual triggering of the lambda, depending on the load on the DynamoDB Streams service. (This is acceptable for my use case since analytics don't need to be real-time).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My intention in posting this article is to share my idea and get feedback from the community. If you have any suggestions for improvement or alternative approaches, please let me know in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>aws</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Minimal changes when migrating react-scripts to vite</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Tue, 27 Aug 2024 23:07:13 +0000</pubDate>
      <link>https://dev.to/pedrosoaresll/minimal-changes-when-migrating-react-scripts-to-vite-224i</link>
      <guid>https://dev.to/pedrosoaresll/minimal-changes-when-migrating-react-scripts-to-vite-224i</guid>
      <description>&lt;p&gt;🇧🇷 Pra quem quiser ver as alterações necessárias da migração do sistema de build react-scripts para vite em um projeto reactjs pequeno, segue o commit, o commit a seguir é de um pequeno projeto público:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pedroSoaresll/make-your-list/commit/e4bcf8e6d19637e0f49f491a13a43a4756f0dbc4" rel="noopener noreferrer"&gt;https://github.com/pedroSoaresll/make-your-list/commit/e4bcf8e6d19637e0f49f491a13a43a4756f0dbc4&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;🇺🇸 If you wish to see the needed changes when migrating react-scripts to vite system build, take a look at this git commit, it is related to a small project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pedroSoaresll/make-your-list/commit/e4bcf8e6d19637e0f49f491a13a43a4756f0dbc4" rel="noopener noreferrer"&gt;https://github.com/pedroSoaresll/make-your-list/commit/e4bcf8e6d19637e0f49f491a13a43a4756f0dbc4&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>vitejs</category>
      <category>reactscripts</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Invoking Web Worker as a module</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Tue, 30 Apr 2024 15:52:48 +0000</pubDate>
      <link>https://dev.to/pedrosoaresll/invoking-web-worker-as-a-module-4d1o</link>
      <guid>https://dev.to/pedrosoaresll/invoking-web-worker-as-a-module-4d1o</guid>
      <description>&lt;p&gt;Uau, I didn't know about that but all indicates that it is possible to load web worker directly as a module.&lt;/p&gt;

&lt;p&gt;Before, I was loading the script via HTTP passing the public path, note that I am using a React application with Vite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Old example:
&lt;/h2&gt;

&lt;p&gt;I didn't like this way of loading web workers because I was forced to write it as a javascript file, and I couldn't import other modules inside that. Take a look:&lt;/p&gt;

&lt;p&gt;Web Worker script: &lt;code&gt;public/get-merchant-ids-from-csv.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMessageFromMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * @type {File}
   */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onloadend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;merchantIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;merchantIds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invoking the web worker from the public directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// NOTE: In my case I needed to specify the base URL, but in most of cases you can write the URL starting with "/"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GET_MERCHANT_IDS_FROM_FILE_WORKER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/get-merchant-ids-from-csv.js`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GET_MERCHANT_IDS_FROM_FILE_WORKER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prefered way example:
&lt;/h2&gt;

&lt;p&gt;Web worker script: &lt;code&gt;src/modules/shared/workers/get-merchant-ids-from-csv.ts&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that it is a typescript now&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMessageFromMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MessageEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onloadend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;merchantIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;merchantIds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new way of importing the web worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../shared/workers/get-merchant-ids-from-csv.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, it is possible to import other modules, and independent of the place where the script is located, we can inform the path and import it. Note that it is important to pass as a second parameter the base URL &lt;code&gt;import.meta.url&lt;/code&gt; (exposed by vite build system)&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
