<?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: Rk </title>
    <description>The latest articles on DEV Community by Rk  (@rsiv).</description>
    <link>https://dev.to/rsiv</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F831844%2F44918d0f-a0f3-409d-90ef-a4ea95600ae2.jpeg</url>
      <title>DEV Community: Rk </title>
      <link>https://dev.to/rsiv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rsiv"/>
    <language>en</language>
    <item>
      <title>Heroku Alternatives Worth Trying in 2026</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Tue, 18 Aug 2026 14:43:41 +0000</pubDate>
      <link>https://dev.to/rsiv/heroku-alternatives-worth-trying-in-2026-1f2d</link>
      <guid>https://dev.to/rsiv/heroku-alternatives-worth-trying-in-2026-1f2d</guid>
      <description>&lt;p&gt;Heroku defined a whole generation of application deployment. &lt;code&gt;git push heroku main&lt;/code&gt; set the bar for what a good deploy loop feels like, buildpacks made runtimes disappear into the background, and add-ons turned integrating a database into a two-minute chore rather than a two-day one. Even now, teams building brand new applications quietly copy the operational model Heroku popularised.&lt;/p&gt;

&lt;p&gt;What changed over the last few years is the price of that convenience and the surface area around it. Dyno costs climb quickly once traffic grows, the free tier that made Heroku famous for side projects is long gone, add-on pricing stacks on top of the dyno bill, and the ecosystem around Postgres, workers, and background jobs no longer feels as central as it once did. A generation of newer platforms has picked up the same developer experience and rebuilt it on top of containers, edge networks, and pricing models that match how modern applications actually consume resources.&lt;/p&gt;

&lt;p&gt;This post walks through nine platforms that we see teams choose when they move away from Heroku, why each one exists, what they are best at, and the trade-offs to know before you start migrating. Some are fully managed, some are self-hosted, and one of them is ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to look for when you leave Heroku
&lt;/h2&gt;

&lt;p&gt;Before running through the list, it helps to name the axes that usually decide the choice, because most of the time the "right" answer changes depending on which of these matters most for the workload in question.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;runtime model&lt;/strong&gt; is the first fork in the road, since some platforms run your code as always-on containers with steady memory footprints and others run it as short-lived function invocations that spin up on demand. Container platforms feel closer to Heroku dynos and suit long-running processes, background workers, and stateful protocols like WebSockets, while function platforms bill by request and shine for spiky, stateless traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing structure&lt;/strong&gt; matters more than the sticker number. A flat monthly workspace fee plus per-second compute metering looks very different on a bill from a purely usage-based model, especially once you factor in bandwidth, storage, and add-ons like managed Postgres. Look at how the platform charges for the traffic patterns you actually have, not the ones the pricing page is optimised to make look cheap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regional coverage&lt;/strong&gt; decides how close your services live to your users, and how close they live to any managed databases you already run on AWS, GCP, or Azure. If your Postgres is in &lt;code&gt;us-east-1&lt;/code&gt;, running your app anywhere else adds latency you cannot make up for with faster code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The edge tier&lt;/strong&gt; in front of your app changes what you have to build yourself. Platforms fronted by a real CDN and WAF cover DDoS protection, TLS termination, and bot mitigation without you thinking about it, while platforms that only expose raw TCP leave those layers as your problem or as another vendor line on the bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whose infrastructure your workload runs on&lt;/strong&gt; is a question that used to have one answer (the vendor's) but increasingly has two. Bring-your-own-cloud and bring-your-own-cluster options let you use a managed control plane while keeping your workloads on infrastructure you already own or are contractually committed to.&lt;/p&gt;

&lt;p&gt;With those in mind, here are the nine.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Suga
&lt;/h2&gt;

&lt;p&gt;Suga is the platform we build, so consider this section the founder's pitch, but the honest one: we started Suga because we wanted the Heroku-style loop back with the parts that scale properly. You design your application on a visual canvas, connect services to databases and networking, and deploy every change with a commit message and an audit trail. Deployments finish in seconds, environments can be forked whole for testing, and rolling back to any previous state is a single click.&lt;/p&gt;

&lt;p&gt;Under the hood, workloads run as containers on managed infrastructure fronted by Cloudflare's global edge, so DDoS protection, WAF, and TLS termination apply to every service by default, with no separate integration to configure. Pricing is per-seat with hosting credits bundled into every Pro seat, which suits small teams running real production workloads before they need enterprise governance features.&lt;/p&gt;

&lt;p&gt;The interesting move on the top tier is bring-your-own-cloud. On Enterprise, the same control plane can also drive infrastructure you already own on any cloud or on-premises, so you get a single deployment surface across the managed environment and workloads that need to run inside your existing cloud commitments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; teams that want the discipline of Infrastructure as Code with fast deploys, that value environment forking for testing, and that want the edge tier included by default. Enterprises with existing cloud footprints who want a modern deployment experience on the cluster of record they already have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you need very large per-instance memory ceilings today, you require published SOC 2 and ISO 27001 certifications (Suga is working toward these but has not published them yet), or you need a mature managed Postgres with point-in-time recovery as a first-party service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Free tier for evaluation, Pro is per-seat with $20 of hosting credits included per seat, Enterprise is custom with BYOC support.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Render
&lt;/h2&gt;

&lt;p&gt;Render is the platform that most closely mirrors the Heroku operating model on modern infrastructure. You push a repo, Render detects the runtime or reads a Dockerfile, and the service comes up on managed infrastructure with automatic TLS, health checks, and zero-downtime deploys.&lt;/p&gt;

&lt;p&gt;Where Render differentiates is on the range of instance sizes it offers, scaling up to 32 GB of RAM and 8 CPU per service on the top tier, and on its managed data services, which include a full Postgres product with point-in-time recovery, read replicas, and tiered compute. For teams whose main frustration with Heroku is dyno price-to-performance and add-on lock-in, Render often reads as the closest one-to-one replacement without the operational retraining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; teams who want the Heroku experience (push code, get a URL) with wider instance sizes, first-party managed Postgres, and published compliance certifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you need to run workloads on your own infrastructure, or your traffic pattern is spiky enough that flat workspace fees plus per-second compute metering does not fit as cleanly as pure usage-based billing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Free hobby tier with sleep, Pro from $25/month workspace fee plus compute metered by the second, Scale plan for larger teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Railway
&lt;/h2&gt;

&lt;p&gt;Railway leans into the visual side of application composition. You drop services on a canvas, wire databases, workers, and crons together, and Railway takes care of stitching networking and secrets between them. Every project can spin up databases as containerised services, and per-service resources are set by picking a compute plan rather than an instance size.&lt;/p&gt;

&lt;p&gt;For teams that liked Heroku's add-on marketplace but want the flexibility to run everything as containers, Railway hits a familiar note, and the developer loop of connecting to running containers over SSH is one of the more distinctive features on this list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; small teams and startups that want a single project canvas showing everything running in the application, plus straightforward per-minute billing on CPU and RAM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you need L7 DDoS protection and a WAF at the edge without adding a separate provider, or you need to run on your own infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Hobby plan from $5/month with compute metered per minute, team plans add collaboration and higher limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Fly.io
&lt;/h2&gt;

&lt;p&gt;Fly.io approaches the deployment problem from a very different angle. Fly runs your app on its own micro-VMs distributed across dozens of regions worldwide, so "run this container close to the user" is baked into how you design the deployment from the start, and the concepts you'd normally reach for on other platforms (a CDN in front of a single-region origin) are replaced by the workload actually living where the users are.&lt;/p&gt;

&lt;p&gt;That model shines for latency-sensitive applications, multi-region databases, and anything with a genuinely global user base. It also introduces its own set of concerns around data locality, region failover, and how you design your topology, so Fly rewards teams who are willing to think about placement explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; applications where round-trip latency to the user matters, multi-region topologies, and workloads that benefit from geographical distribution as a first-class primitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you have a single-region application backed by a single-region database, since much of Fly's distinctive value comes from the multi-region model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Usage-based across CPU, RAM, and bandwidth, with a small always-on hobby footprint free.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Vercel
&lt;/h2&gt;

&lt;p&gt;Vercel is not a container platform, but it belongs on any honest list of Heroku alternatives because Heroku's Node.js and frontend workloads are exactly the traffic Vercel is optimised for. If your application is a Next.js frontend calling out to an API, Vercel gives you a build-and-deploy experience tuned for that pattern, with edge-cached static assets, serverless functions for the API layer, and preview environments on every pull request.&lt;/p&gt;

&lt;p&gt;Where Vercel diverges from Heroku is in the runtime model. Instead of always-on dynos, Vercel runs your code as serverless functions that scale to zero between requests, which suits spiky HTTP workloads and hurts long-running background jobs, WebSocket connections, and anything that assumes a warm process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Next.js frontends, marketing sites, JAMstack applications, and teams whose "backend" is mostly a thin API layer in front of managed services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; your workload is a long-running process, holds open WebSocket connections, runs background workers, or does heavy in-process caching that scale-to-zero would evict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Hobby free with usage limits, Pro from $20/user/month plus usage-based function invocations and bandwidth.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Porter
&lt;/h2&gt;

&lt;p&gt;Porter takes the Heroku developer experience and puts it on top of Kubernetes. You get a push-to-deploy flow, sensible defaults for TLS and autoscaling, and the ability to graduate into raw Kubernetes primitives when you need them, all against a cluster Porter manages for you or one you connect from your own AWS, GCP, or Azure account.&lt;/p&gt;

&lt;p&gt;The pitch is aimed at teams who see Kubernetes in their future but do not want to learn it as a prerequisite to shipping today. Porter is meaningfully more opinionated than raw Kubernetes and meaningfully less opinionated than a fully managed PaaS, so it lives in an interesting middle ground.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; teams who expect to end up on Kubernetes eventually and want a gradient into it, or teams who already have cluster infrastructure they want to keep using with a nicer deploy loop on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you have no interest in Kubernetes concepts ever leaking into your day-to-day work, or you want a fully managed platform with no cluster-level decisions to make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Metered by compute usage on Porter Cloud, with self-hosted options for teams running on their own accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Dokploy
&lt;/h2&gt;

&lt;p&gt;Dokploy is a self-hosted PaaS that sits on top of Docker and Docker Swarm, giving you an application dashboard, database provisioning, and deploy flows on infrastructure you rent yourself. Because the platform itself is open source, you can run it on a single VPS and treat that VPS as your entire production footprint, which is a very different economic profile than any managed service.&lt;/p&gt;

&lt;p&gt;The obvious appeal is cost control: for small side projects and internal tools, running everything on a single $5-$20 VPS with a self-managed platform on top can be dramatically cheaper than the equivalent workload on a managed PaaS, especially once bandwidth and add-ons stack up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; developers who are comfortable managing a VPS, want maximum control over the underlying host, and have workloads whose economics benefit from a fixed monthly VM bill rather than usage-based metering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you would rather not manage host-level concerns like OS updates, backups, and VPS-level networking, or you want a platform team's support contract behind your production infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Open source and free to self-host, with a paid cloud option for teams that want the same platform without running the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Coolify
&lt;/h2&gt;

&lt;p&gt;Coolify occupies the same self-hosted PaaS space as Dokploy and has grown a substantial community over the last few years. The user experience is broadly similar: connect a Git repository or a Docker image, provision databases as containers on the same host, and get automatic TLS through Let's Encrypt on top of your own server.&lt;/p&gt;

&lt;p&gt;Coolify's differentiators are its plugin ecosystem, breadth of one-click databases and services, and a mature web UI that a lot of solo developers and small teams find pleasant to live in day to day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; solo developers and small teams who want a self-hosted PaaS with an active community, first-class support for a wide range of databases as containers, and no per-service pricing to worry about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; the workload really needs multi-region deployment, managed Postgres with point-in-time recovery, or a support contract with an SLA behind the platform itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Open source and free to self-host, with paid cloud tiers available.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Dokku
&lt;/h2&gt;

&lt;p&gt;Dokku is the original self-hosted "mini-Heroku" and still one of the leanest options on this list. Built around Docker and Heroku buildpacks, it gives you &lt;code&gt;git push&lt;/code&gt; deploys onto a single server with almost no ceremony, and its plugin ecosystem covers Postgres, Redis, and the rest of the classic Heroku add-on lineup.&lt;/p&gt;

&lt;p&gt;Where Dokku differs from the newer self-hosted PaaS options is in its philosophy: it is command-line first, opinionated in the Heroku direction (buildpacks, procfiles, twelve-factor conventions), and comfortable running on a single small server for years without much attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; developers who want the original Heroku-style deploy loop reproduced on a single VPS, and teams who appreciate a CLI-first, minimal-web-UI approach to platform administration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider elsewhere if:&lt;/strong&gt; you prefer a web UI as the primary interface, or you need built-in multi-host orchestration without stepping up to Kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Open source and free to self-host.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose
&lt;/h2&gt;

&lt;p&gt;The question that decides most of these choices is not "which platform is best" but "which platform matches the character of the workload and the operational appetite of the team".&lt;/p&gt;

&lt;p&gt;If the workload is a straightforward web application with a Postgres database, a managed platform like Suga, Render, or Railway will remove the largest amount of undifferentiated infrastructure work with the smallest amount of ceremony. Among those, Suga leads with an included edge tier and hosting credits per seat, Render leads on managed Postgres depth and published compliance, and Railway leads on the canvas-first project experience with SSH into running containers.&lt;/p&gt;

&lt;p&gt;If latency to end users is central to the product, Fly.io's multi-region model is genuinely differentiated and worth investing in.&lt;/p&gt;

&lt;p&gt;If the frontend is Next.js and the backend is mostly a thin API in front of managed services, Vercel is the natural fit even though the runtime model is different from Heroku's dynos.&lt;/p&gt;

&lt;p&gt;If you are already on the road to Kubernetes or need to run on your own cluster, Porter is the smoother on-ramp than raw kubectl, and Suga's Enterprise plan can also run against clusters you already own.&lt;/p&gt;

&lt;p&gt;If cost control is the primary motivation and you are comfortable managing a host, Dokploy, Coolify, and Dokku each solve the same problem with different flavours: Dokploy leans into a modern web UI on Swarm, Coolify offers the largest community and plugin ecosystem, and Dokku offers the closest reproduction of the original Heroku loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating away from Heroku
&lt;/h2&gt;

&lt;p&gt;Whichever destination you pick, the migration itself tends to follow the same rough pattern. Start by containerising the application, since every platform on this list can accept a Dockerfile and this is the deploy artefact most likely to survive any future platform change too. Move the database next by taking a fresh dump from Heroku Postgres and restoring it into the new managed Postgres or container-based database in your target platform, ideally cutting over during a low-traffic window with DNS pointed at both endpoints during the switch. Environment variables are usually the fiddliest part, since Heroku's config vars often accumulate over years, so treat this as an opportunity to audit and remove the ones nobody remembers adding.&lt;/p&gt;

&lt;p&gt;Two decisions make the difference between a clean migration and a painful one. First, decide the runtime model deliberately: if you are moving to a serverless platform like Vercel, budget time to rework anything that assumed a warm long-running process, and if you are staying on containers, take the opportunity to consolidate any background workers and web dynos into a single application topology. Second, mirror your Heroku environment layout into whatever the target platform calls its environments (workspaces, projects, environments, spaces), and use the migration as a chance to formalise dev, staging, and production with clean isolation between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Heroku itself still viable?&lt;/strong&gt; Yes, Heroku is still a working product and still fits some teams well, particularly those that have been on it for years and have deep integration with its add-on ecosystem. The reason teams look elsewhere is usually a combination of cost curves that no longer match the workload, a preference for containers over buildpacks, or a desire to run on infrastructure the team already owns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I combine platforms?&lt;/strong&gt; Absolutely, and this is more common than the vendor pages suggest. A typical modern stack might run the frontend on Vercel, the backend on Suga or Render, and background jobs on Fly.io close to the database, all glued together with the same managed Postgres. Pick per workload rather than trying to fit everything onto one platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about AWS, GCP, and Azure directly?&lt;/strong&gt; They are always an option, and for large enterprises with dedicated platform teams they are often the right answer. For everyone else, the PaaS platforms on this list exist because the raw cloud consoles carry too much undifferentiated infrastructure work for a small team to absorb, so the sensible move is usually a managed PaaS with a bring-your-own-cloud escape hatch for the day the workload outgrows the vendor's own infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How should I evaluate free tiers?&lt;/strong&gt; As evaluation environments, not as long-term hosting. Every free tier on this list has constraints (sleep after inactivity, low resource ceilings, or usage caps) that make sense for kicking the tyres and stop making sense once you have real traffic. Budget for the paid tier of whichever platform you pick, and use the free tier to make sure the deploy loop and operational model actually suit your team before you commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I want to change platforms again later?&lt;/strong&gt; Container-based platforms make this dramatically easier than the previous generation of buildpack-based PaaS, since the deploy artefact is a Docker image that any container platform can accept. If portability is a first-order concern, favour platforms that use Dockerfiles as the primary input and treat any platform-specific configuration as a thin wrapper around the container.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>deployment</category>
      <category>paas</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I handed Claude a docker-compose.yml and it deployed my app instantly onto Suga</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Thu, 13 Aug 2026 14:38:46 +0000</pubDate>
      <link>https://dev.to/rsiv/i-handed-claude-a-docker-composeyml-and-it-deployed-my-app-instantly-2cbg</link>
      <guid>https://dev.to/rsiv/i-handed-claude-a-docker-composeyml-and-it-deployed-my-app-instantly-2cbg</guid>
      <description>&lt;p&gt;It turns out you can hand a &lt;code&gt;docker-compose.yml&lt;/code&gt; to Claude, point it at a container platform's MCP server, and get a working managed deploy back. &lt;/p&gt;

&lt;p&gt;This is really cool, because moving a local compose stack onto a managed platform normally means one of three things: rewriting the deploy shape in whatever config language your target platform speaks, running a converter tool that only handles the parts compose and the platform both understand, or staying on a VM and running &lt;code&gt;docker compose up&lt;/code&gt; directly. &lt;/p&gt;

&lt;p&gt;Each of those requires an intervention, because compose describes a container topology while modern managed platforms need more than that to run your app well, including managed builds from a git source, auto-provisioned public URLs, cross-service secret references, and per-container scaling and resource specs.&lt;/p&gt;

&lt;p&gt;The MCP-and-agent path skips all three: &lt;strong&gt;expose a platform's configuration API through an MCP server, and let an AI coding agent do the translation on demand from your compose file, without your compose file ever having to become anything else.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP (the &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;) is an open standard for giving LLMs structured, permissioned access to external tools. If a platform ships MCP tools like &lt;code&gt;create_environment&lt;/code&gt;, &lt;code&gt;add_container&lt;/code&gt;, &lt;code&gt;add_volume&lt;/code&gt;, and &lt;code&gt;set_secret&lt;/code&gt;, an agent can read a compose file, translate the topology, generate the secrets it assumes are external, feed auto-allocated URLs back into env vars, flag missing durability settings, and stop short of actually deploying so a human still presses ship.&lt;/p&gt;

&lt;p&gt;To see how it plays out, I pointed Claude (via Claude Code) at &lt;a href="https://suga.app" rel="noopener noreferrer"&gt;Suga&lt;/a&gt;'s MCP server and asked it to stand up a real Laravel app's &lt;code&gt;compose.yaml&lt;/code&gt; in a fresh environment. &lt;/p&gt;

&lt;h2&gt;
  
  
  The compose file
&lt;/h2&gt;

&lt;p&gt;The example is taken from a Laravel app I run live as a side project to my main gig which is a hosting platform named Suga (&lt;a href="https://suga.app" rel="noopener noreferrer"&gt;https://suga.app&lt;/a&gt;), and since I work on Suga and I wanted to see if this would just work.&lt;/p&gt;

&lt;p&gt;Here's the &lt;code&gt;compose.yaml&lt;/code&gt; so you have the source material inline as we step through the translation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;x-app-env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;app-env&lt;/span&gt;
  &lt;span class="na"&gt;APP_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
  &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;APP_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${APP_KEY:?APP_KEY must be set (e.g. in .env)}&lt;/span&gt;
  &lt;span class="na"&gt;APP_DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
  &lt;span class="na"&gt;APP_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8080&lt;/span&gt;
  &lt;span class="na"&gt;LOG_CHANNEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stderr&lt;/span&gt;
  &lt;span class="na"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
  &lt;span class="na"&gt;DB_CONNECTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pgsql&lt;/span&gt;
  &lt;span class="na"&gt;DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432"&lt;/span&gt;
  &lt;span class="na"&gt;DB_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
  &lt;span class="na"&gt;DB_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
  &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
  &lt;span class="na"&gt;REDIS_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;REDIS_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379"&lt;/span&gt;
  &lt;span class="na"&gt;SESSION_DRIVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;CACHE_STORE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;QUEUE_CONNECTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;

&lt;span class="na"&gt;x-depends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;app-depends&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-env&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-depends&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;php"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artisan"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queue:work"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--tries=3"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-env&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-depends&lt;/span&gt;

  &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;php"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artisan"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;schedule:work"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-env&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app-depends&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three PHP services share an image built from a repo-root Dockerfile and pull in a large block of shared env vars via the &lt;code&gt;x-app-env&lt;/code&gt; YAML anchor, while postgres gets a named volume for its data directory so writes survive a container restart. That's a fairly standard Laravel-plus-Redis-plus-Postgres shape, and anyone who has run compose in dev will recognize the topology immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suga's model, briefly
&lt;/h2&gt;

&lt;p&gt;To follow along you need a quick sketch of how Suga models an environment, since it doesn't map one to one to compose services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;environment&lt;/strong&gt; contains a &lt;strong&gt;draft&lt;/strong&gt;, which is a canvas of &lt;strong&gt;containers&lt;/strong&gt; and &lt;strong&gt;volumes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each container has either an image reference or a build source (a connected GitHub repo), plus env vars, private networking, and optional public HTTPS.&lt;/li&gt;
&lt;li&gt;Containers can reference each other's values e.g. &lt;code&gt;{{&amp;lt;id&amp;gt;.variables.KEY}}&lt;/code&gt; where &lt;code&gt;&amp;lt;id&amp;gt;&lt;/code&gt; is a short nanoid that Suga assigns when the container is created.&lt;/li&gt;
&lt;li&gt;The MCP server exposes tools for shaping the draft, including &lt;code&gt;create_environment&lt;/code&gt;, &lt;code&gt;add_container&lt;/code&gt;, &lt;code&gt;add_volume&lt;/code&gt;, &lt;code&gt;set_env_variable&lt;/code&gt;, &lt;code&gt;set_secret&lt;/code&gt;, &lt;code&gt;connect_build_repository&lt;/code&gt;, and &lt;code&gt;update_build_repository&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The MCP has no &lt;code&gt;deploy&lt;/code&gt; tool by design, which is the safety rail: the agent crafts the draft and a human approves and deploys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wiring Claude Code up to Suga
&lt;/h2&gt;

&lt;p&gt;I registered Suga's MCP server with Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http suga https://dashboard.suga.app/api/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I ran &lt;code&gt;/mcp&lt;/code&gt; inside Claude Code to authenticate against my Suga account, and every Suga tool referenced in the rest of this post was callable directly from a Claude Code conversation. If you're using Cursor, Cline, or another MCP-capable coding agent instead of Claude Code, the &lt;a href="https://docs.suga.app/reference/mcp#connect-an-ai-agent-mcp" rel="noopener noreferrer"&gt;Suga MCP connection guide&lt;/a&gt; has the equivalent setup instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;p&gt;The prompt I gave Claude was essentially&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Deploy the compose.yaml at the repo root to Suga as a new environment on my existing project&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 with no accompanying instructions or hints, and that direction was enough for it to come back with a plan and a stream of MCP calls.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It did have access to the Laravel project&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Translation, decision by decision
&lt;/h2&gt;
&lt;h3&gt;
  
  
  services: becomes five add_container calls
&lt;/h3&gt;

&lt;p&gt;Each compose service becomes one &lt;code&gt;add_container&lt;/code&gt; call, where the service name doubles as both the container's &lt;code&gt;displayName&lt;/code&gt; and its &lt;code&gt;networking.private.hostname&lt;/code&gt;. Keeping the hostname aligned with the compose service name matters because the app's env vars refer to services by name (&lt;code&gt;DB_HOST=postgres&lt;/code&gt;, &lt;code&gt;REDIS_HOST=redis&lt;/code&gt;), and cross-container DNS on Suga's private network resolves off the container's &lt;code&gt;hostname&lt;/code&gt;, so the app reaches postgres and redis by the same names it uses locally.&lt;/p&gt;
&lt;h3&gt;
  
  
  image: myapp:latest becomes a GitHub build source
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;myapp:latest&lt;/code&gt; tag in compose is really shorthand for "the image you get when you run &lt;code&gt;docker build&lt;/code&gt; in this repo," so the natural translation on Suga is to give each PHP service a build source that actually produces that image. The agent did this by connecting app, worker, and scheduler to my GitHub repo on the main branch via &lt;code&gt;connect_build_repository&lt;/code&gt;, pointing all three at the same repo-root Dockerfile.&lt;/p&gt;

&lt;p&gt;There's a design choice worth calling out here: each Suga container has its own build source, which means you can point three containers at the same repo and Dockerfile for a monorepo setup, or at completely different repos and Dockerfiles for a multi-repo setup, depending on how your services are laid out. Compose's implicit "one image reused across services" pattern via the shared image field is the odd one out in that comparison, and if you want that same shared-image behaviour on Suga the pattern is to build once, push to a registry, and reference the tag from each container. &lt;/p&gt;

&lt;p&gt;The agent picked the same-repo path here because it was the most direct translation of what the compose file was already expressing (three services sharing one Dockerfile, one build, one image), and because it stayed inside Suga (three &lt;code&gt;connect_build_repository&lt;/code&gt; calls) rather than pulling in a separate registry push as a side quest.&lt;/p&gt;
&lt;h3&gt;
  
  
  ports: "8080:8080" becomes public HTTPS
&lt;/h3&gt;

&lt;p&gt;The app service's port publish became &lt;code&gt;networking.public.https[{port: 8080}]&lt;/code&gt; in the draft, at which point Suga auto-allocated a hostname on the cluster and returned a &lt;code&gt;publicUrl&lt;/code&gt; immediately, before the deploy even happened. The response payload looked something like this: https://--..suga.run&lt;/p&gt;

&lt;p&gt;Because that URL was available in the same MCP call that created the container, the agent could feed it back into the app's &lt;code&gt;APP_URL&lt;/code&gt; env var in a follow-up call, wiring it up in the same pass rather than needing a second step to fill in the value later.&lt;/p&gt;
&lt;h3&gt;
  
  
  ${APP_KEY:?...} becomes a generated secret
&lt;/h3&gt;

&lt;p&gt;Compose's &lt;code&gt;${APP_KEY:?APP_KEY must be set (e.g. in .env)}&lt;/code&gt; syntax means "this variable must come from somewhere, fail loudly if unset." The agent recognized it as a Laravel APP_KEY, generated one with &lt;code&gt;openssl rand -base64 32&lt;/code&gt;, and stored it as a sensitive variable in the draft so the value is write-only from that point on.&lt;/p&gt;
&lt;h3&gt;
  
  
  DB_PASSWORD: myapp becomes a sensitive cross-container reference
&lt;/h3&gt;

&lt;p&gt;The compose file hardcodes the DB password on both the postgres service and the app services, which is convenient for local dev but worth improving on for a real deploy. The agent had a choice between hardcoding the literal password on both sides in Suga too or referencing postgres's secret from the app, and it picked the reference form: DB_PASSWORD = {{.variables.POSTGRES_PASSWORD}}&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;postgres-id&amp;gt;&lt;/code&gt; placeholder in that expression is where the postgres container's resource id goes, a nanoid that Suga assigned when the container was created (something in the shape of &lt;code&gt;k3n8vpqm0xrs&lt;/code&gt;), and this reference form has no compose analogue at all, which is what makes it interesting: rotating the postgres password now updates every consuming container automatically on the next deploy, without the agent having to remember to keep two literal values in sync.&lt;/p&gt;
&lt;h3&gt;
  
  
  depends_on: {condition: service_healthy} gets dropped
&lt;/h3&gt;

&lt;p&gt;Modern services are generally expected to retry their dependencies at startup rather than crash on the first failed connection, so the agent handled the compose &lt;code&gt;depends_on&lt;/code&gt; gate by trusting that behaviour and letting the app come up naturally. That keeps the draft clean of compose-specific boot orchestration, and the retry logic built into most modern frameworks (Laravel included) does the rest at runtime.&lt;/p&gt;
&lt;h3&gt;
  
  
  Volumes
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;postgres-data:/var/lib/postgresql/data&lt;/code&gt; became a 1GB Suga volume mounted at the same path on the postgres container, which is essentially the same shape as the compose declaration with a size added.&lt;/p&gt;
&lt;h3&gt;
  
  
  What the agent noticed, that wasn't in the compose
&lt;/h3&gt;

&lt;p&gt;The compose file doesn't say anything about Redis durability, which is a subtle gap worth flagging: Redis in production usually runs with &lt;code&gt;--appendonly yes&lt;/code&gt; and a mounted volume so that a container restart doesn't erase the cache, session store, and queue state Laravel is putting there. The agent didn't quietly add either to the draft, staying faithful to the compose file as instructed, but it did flag the gap in its summary and noted that a real deploy of this shape would probably want durability configured before it ships. That's the kind of observation you get from an agent that has both the compose file in hand and general knowledge of how services like Redis are typically run in production, since a rules-based converter can only see what compose has spelled out.&lt;/p&gt;
&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;A couple of minutes and a few MCP calls later, the draft was populated and ready to review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;create_environment&lt;/code&gt; call created the new environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;add_container&lt;/code&gt; calls added app, worker, scheduler, postgres, and redis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;add_volume&lt;/code&gt; call added &lt;code&gt;postgres-data&lt;/code&gt; mounted on postgres&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set_env_variable&lt;/code&gt; call fed the auto-allocated public URL back into &lt;code&gt;APP_URL&lt;/code&gt; on the app container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;connect_build_repository&lt;/code&gt; calls wired app, worker, and scheduler to the GitHub repo on the main branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I opened the Suga link, logged in, reviewed the canvas, and clicked Deploy.&lt;/p&gt;

&lt;p&gt;About ninety seconds later the deploy came back failed, with a clean and specific error surfaced right in the deployment record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build failed for service "worker": failed to solve: failed to read
dockerfile: open Dockerfile: no such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That same message showed up once for each of the three source-built services, and it was the branch guess catching up with me. My &lt;code&gt;main&lt;/code&gt; branch on this repo happens to be nearly empty, since all of the real code (including the &lt;code&gt;Dockerfile&lt;/code&gt;) lives on &lt;code&gt;develop&lt;/code&gt;, but the agent had no way to know that and had picked &lt;code&gt;main&lt;/code&gt; as a reasonable default.&lt;/p&gt;

&lt;p&gt;I told the agent the correct branch name in one sentence, and it made three &lt;code&gt;update_build_repository&lt;/code&gt; calls (one per source-built container) to patch each build config in place, leaving the rest of the environment untouched.&lt;/p&gt;

&lt;p&gt;I clicked Deploy on the same review link, and the second attempt went through end to end. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm70qa4kata1lew6v60yg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm70qa4kata1lew6v60yg.png" alt="The blog-demo environment in the Suga dashboard after the successful deploy: five containers on the canvas with green health indicators and their private-network links, and the app container's config panel on the right showing the connected build source and the generated public URL." width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app came up and started responding to requests on the same public URL Suga had allocated back during &lt;code&gt;add_container&lt;/code&gt;, which meant the URL I'd already fed into &lt;code&gt;APP_URL&lt;/code&gt; on the app container was still the correct one, exactly as the pre-allocation flow implied it would be.&lt;/p&gt;

&lt;p&gt;One thing worth noting from the logs: Suga starts all five containers in parallel, which is the fastest way to get a deploy up. During the first thirty seconds the scheduler's Laravel bootstrap tried to talk to &lt;code&gt;postgres:5432&lt;/code&gt; twice while postgres was still finishing &lt;code&gt;initdb&lt;/code&gt;, both attempts were logged as expected connection errors, and the scheduler was healthy on subsequent connection attempts once postgres was ready (whether by Laravel retrying internally or by the container restarting and coming back up cleanly). Either path is a normal way for a modern app to handle a downstream service that takes a moment to warm up. For an app that wouldn't recover on its own (a one-shot init container or a strict migration job, for example), adding a container-level retry loop is a small tune-up worth doing in the second pass, once the first deploy is live and you can see everything talking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is really about
&lt;/h2&gt;

&lt;p&gt;For app devs, the reason this pattern matters is iteration speed. &lt;code&gt;docker compose up&lt;/code&gt; gets your stack running locally in seconds, keeps every dependency described in one file, and lets you rebuild your mental model of the system quickly whenever you need to, which is why compose is still the default local dev setup for most multi-service apps in 2026. The friction hits the moment you want that same stack running on a managed platform rather than a raw VM, because your &lt;code&gt;compose.yaml&lt;/code&gt; only describes half of what the platform needs to know, and you either translate the missing half by hand into whatever config language your platform expects or spend real build cycles pushing to CI just to iterate on the deploy shape.&lt;/p&gt;

&lt;p&gt;An MCP-driven flow removes almost all of that friction. Your compose file stays the source of truth for how the app runs locally, and every time you change it, the same one-sentence prompt gets you a fresh production draft with the secrets generated, the build source wired up, the public URL allocated, and the cross-service references in place. The loop between "changed the compose file locally" and "reviewed a production draft" collapses to about the time it takes to type the prompt, which starts to feel a lot closer to the speed of local dev than the speed of a normal deploy pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you want to point Claude at your own &lt;code&gt;compose.yaml&lt;/code&gt;, the setup is short and free to try end to end. You'll need a Suga account first, which you can create at &lt;a href="https://suga.app" rel="noopener noreferrer"&gt;suga.app&lt;/a&gt; on the free tier, and once you're signed in you can follow the &lt;a href="https://docs.suga.app/reference/mcp#connect-an-ai-agent-mcp" rel="noopener noreferrer"&gt;MCP connection guide&lt;/a&gt; to wire the Suga MCP server into Claude Code (or whichever MCP-capable agent you prefer). With the server connected, handing your compose file to the agent is a one-sentence prompt away, and you'll step through the same translation flow this post walked through, right up until you're the human clicking Deploy.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>laravel</category>
      <category>ai</category>
      <category>containers</category>
    </item>
    <item>
      <title>Fixing observability across async events (like queues)</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 20 Apr 2026 18:14:04 +0000</pubDate>
      <link>https://dev.to/rsiv/three-days-debugging-a-missing-trace-7p8</link>
      <guid>https://dev.to/rsiv/three-days-debugging-a-missing-trace-7p8</guid>
      <description>&lt;p&gt;Last week we had a deploy on &lt;a href="https://suga.app" rel="noopener noreferrer"&gt;Suga&lt;/a&gt; that was taking longer than usual. It was hard to reproduce, so the only obvious thing to do was try and trace it end to end. We immediately ran into roadblocks because the trace only covered the first 140 milliseconds.&lt;/p&gt;

&lt;p&gt;For context: the bulk of our deployment code runs in a background worker and is triggered via a queue, and the worker's trace had no connection back to the request that kicked it off. &lt;/p&gt;

&lt;p&gt;Without a complete trace, we were forced to look at everything related to our deployments and burnt time combing through the worker looking for oddities. At this point we were stuck trying to build a trace manually by correlating by timestamps in logs - not a lot of fun. &lt;/p&gt;

&lt;p&gt;It turns out, a lot of tracing docs spend very little time on the edge cases like handling asynchronous workloads. Instead, they're more foundational, with a focus on synchronous HTTP requests between services which are a little more straight forward. &lt;/p&gt;

&lt;p&gt;When one service calls another the headers carry the trace, and you get a trace log that flows like a waterfall which is the exact behavior we wanted for queued workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Queues don't have headers
&lt;/h2&gt;

&lt;p&gt;HTTP tracing works because requests have headers, a sanctioned place to attach arbitrary metadata that both sides know how to read. Queues generally don't, or at least not in the same obvious way. &lt;/p&gt;

&lt;p&gt;They do have payloads, which is the data the job works and the first instinct when you hit this problem might be to cram trace context in there. This might work briefly, but its not a good solution because  mixing makes it hard to debug and maintain in the future.&lt;/p&gt;

&lt;p&gt;The trick is to find whatever metadata channel the queue already gives you and use that and thankfully, almost every mature queue has one (probably because of this scenario). SQS has message attributes, &lt;a href="https://temporal.io" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt; has context propagators built into the SDK, and &lt;a href="https://hatchet.run" rel="noopener noreferrer"&gt;Hatchet&lt;/a&gt; (which we use to run our workflows) has a metadata field called &lt;code&gt;additionalMetadata&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Hatchet gave us
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;additionalMetadata&lt;/code&gt; is a &lt;code&gt;Record&amp;lt;string, string&amp;gt;&lt;/code&gt;, which turns out to be exactly the right shape for our trace data. It is structured enough to be useful and unstructured enough to carry whatever you want without Hatchet needing to understand it, but more importantly - no database or schema changes. &lt;/p&gt;

&lt;p&gt;This is a big deal, because we were retroactively fitting traceability into our workflows, our requirements were to minimize the impact on all of our callers/workers.&lt;/p&gt;

&lt;p&gt;Attaching the headers alongside the existing metadata looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runNoWait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;additionalMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;injectTraceContext&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;h2&gt;
  
  
  Anatomy of a trace header
&lt;/h2&gt;

&lt;p&gt;The pattern we used leans on a standard called &lt;em&gt;propagation&lt;/em&gt;: the way tracing SDKs serialize the current trace into a small set of string headers on one side of a boundary (like an enqueue event) and rehydrate it on the other (dequeue). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We use Sentry to handle the propagation, but, the pattern will work with any tracing SDK that supports an inject/continue pair.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The header names &lt;code&gt;sentry-trace&lt;/code&gt; and &lt;code&gt;baggage&lt;/code&gt; follow the W3C Trace Context spec, allowing any compliant SDK to work with each other.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;sentry-trace&lt;/code&gt; header is three dash-separated fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sentry-trace: 771a43a4192642f0b136d5159a501700-7d3efb1b173fecfa-1
              └──────── trace id ────────────┘ └── span id ───┘ └ sampled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The trace id is the identifier every span in this trace shares, which is how the worker-side spans end up stitched to the web request in the UI. &lt;/li&gt;
&lt;li&gt;The span id is the parent the next span should hang off, so the worker's first span slots in as a child of whatever span called &lt;code&gt;runNoWait&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;The sampled flag tells the receiver whether this trace is being recorded, so both sides agree on whether to keep the data. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;OTel calls the equivalent header &lt;code&gt;traceparent&lt;/code&gt; and structures it almost identically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;baggage&lt;/code&gt; field is a comma-separated list of key=value pairs carrying trace-level metadata like environment and release.&lt;/p&gt;

&lt;h2&gt;
  
  
  The handoff
&lt;/h2&gt;

&lt;p&gt;In Sentry the two sides of propagation are &lt;code&gt;getTraceData()&lt;/code&gt; on the way in and &lt;code&gt;Sentry.continueTrace(...)&lt;/code&gt; on the way out. We've implemented the two helpers below, &lt;code&gt;injectTraceContext&lt;/code&gt; and &lt;code&gt;extractTraceContext&lt;/code&gt;, as thin adapters around that pair: one takes the SDK's output and hands it to the queue, the other takes the queue's input and hands it to the SDK.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;injectTraceContext&lt;/code&gt; grabs the current trace's propagation headers, returning an empty object when there is no active trace data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;injectTraceContext&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TraceContext&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTraceData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sentry-trace&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="p"&gt;{};&lt;/span&gt; &lt;span class="c1"&gt;// no active trace on this request, nothing to propagate&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TraceContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sentry-trace&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sentry-trace&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="k"&gt;if &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="nx"&gt;baggage&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;baggage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baggage&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;result&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;&lt;code&gt;extractTraceContext&lt;/code&gt; takes the metadata coming in from the queue, and hands it to &lt;code&gt;Sentry.continueTrace&lt;/code&gt; which wraps the callback so any spans created inside it become children of the original request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractTraceContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&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;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&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;sentryTrace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sentry-trace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sentryTrace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// upstream didn't propagate, run the task in its own trace&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;continueTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sentryTrace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baggage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;baggage&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;callback&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;Once &lt;code&gt;continueTrace&lt;/code&gt; has fired, anything the task does after that including database queries or outbound HTTP calls hangs off the web request's trace automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it invisible to the task author
&lt;/h2&gt;

&lt;p&gt;From personal experience, developers aren't good at remembering things like tracing when writing new workers, so instead of exposing &lt;code&gt;extractTraceContext&lt;/code&gt; directly, we put it alongside our structured logger inside the &lt;code&gt;withTaskLogging&lt;/code&gt; wrapper that every task already uses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;withTaskLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deploy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;event&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;deploymentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deploymentId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deployEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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;Behind the scenes, &lt;code&gt;withTaskLogging&lt;/code&gt; handles the extraction before the task body runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;extractTraceContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;additionalMetadata&lt;/span&gt;&lt;span class="p"&gt;(),&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;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`task:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;workflowName&lt;/span&gt;&lt;span class="p"&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;taskName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hatchet.task&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async &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="c1"&gt;// task body runs here, as a child of the original web trace&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;h2&gt;
  
  
  The payoff
&lt;/h2&gt;

&lt;p&gt;We now have a complete trace across synchronous and asynchronous events and the 30.2 seconds the job spent queued shows up as a visible gap rather than something you'd have to infer from timestamps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/deploy                          60ms  web
 ├─ db.query: select env                  12ms  web
 └─ workflow.runNoWait: deploy            18ms  web
                                                        30.2s gap (queued)
 task:deployment/deploy                  8.4s   worker
 ├─ db.query: fetch deployment            45ms  worker
 ├─ k8s.apply: deployment                3.2s   worker
 └─ k8s.wait: service loadbalancer       4.8s   worker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, that gap was the entire mystery of the deploy we opened with: the deploy wasn't actually slow; it was waiting in queue to execute and that gave us the information to know where the issue was. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Can we trust AI to deploy as fast as it generates code?</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 30 Mar 2026 18:20:10 +0000</pubDate>
      <link>https://dev.to/rsiv/ai-sped-up-development-not-shipping-5g1</link>
      <guid>https://dev.to/rsiv/ai-sped-up-development-not-shipping-5g1</guid>
      <description>&lt;p&gt;AI can generate Terraform, but generating it was never the hard part — trusting and deploying it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI made me faster at writing code, not shipping it
&lt;/h2&gt;

&lt;p&gt;The way I build software has changed more in the last year than in the decade before it. Take a decision like whether to process a new order with a background job or a webhook. Before AI tooling, the workflow looked something like this: identify the problem, spend time researching the tradeoffs, check how others had solved it in similar systems, form a plan, write the code, test it locally, then do everything needed to actually ship it. The whole thing, from identifying the problem to having it running in production, might take a couple of days or weeks depending on the feature.&lt;/p&gt;

&lt;p&gt;Now I give an agent the context it needs, the codebase, the outcome, the constraints, and each option comes back as working code (usually) in minutes. The research, the planning, the implementation are compressed. We’re now looking at hours not days, and this is including the time it takes to review the result and wrestle with the AI to get to the end state that I had in mind.&lt;/p&gt;

&lt;p&gt;That's not unique to me, I see it both in my organization and amongst my dev friends, AI is fundamentally changing the first half of the journey from problem to production. What hasn't changed much, is everything that happens after the code is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every application has a shadow project
&lt;/h2&gt;

&lt;p&gt;Behind every application in production there's a second project that nobody outside the immediate work ever sees. It's the infrastructure-as-code that provisions your databases and caches, the CI/CD pipeline config that builds and ships the containers, the Dockerfile, and sets the environment variables across staging and prod.&lt;/p&gt;

&lt;p&gt;The two projects depend on each other but move independently. Add a service and the deployment project needs matching infrastructure. Add a queue and the pipeline config needs updating. Even something as trivial as changing a port can require an update to the infrastructure configuration.&lt;/p&gt;

&lt;p&gt;During my review of the background job I found the performance wasn't good enough, fed that back to the agent, and it came back with a revised approach using a Redis cache. Once I had Redis running locally via Docker it worked perfectly, but from where the agent sits, the application code is the whole picture. It has no idea that Redis now needs to be provisioned, that the pipeline config needs an environment variable, or that staging and prod need to stay in sync.&lt;/p&gt;

&lt;p&gt;Keeping the two projects in sync was already one of the harder parts of backend development. Teams hired platform engineers to manage it, but it remained and it was still a common source of production incidents. AI didn't create the problem, it just made it untenable. The problem now is that an agent can introduce new infrastructure requirements every time I prompt it, and it has no idea the shadow project exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just generate the Terraform at the same time
&lt;/h2&gt;

&lt;p&gt;The obvious response is to have the agent generate the deployment project alongside the application code. When the agent adds Redis, have it also write the Terraform module, the pipeline config, and the environment variables at the same time so both projects stay in sync.&lt;/p&gt;

&lt;p&gt;The thing is, generating Terraform isn't the hard part. It's really not that different from leveraging an old project or selecting from a library of templates. The hard part is trusting it.&lt;/p&gt;

&lt;p&gt;When an agent writes application code, there's a wealth of open source references, community patterns, and established conventions to validate against. IaC is different. It's privately held, idiosyncratic, and often tailored specifically for the application it's coupled to. There's no easy way to know whether what you're looking at reflects best practice or just how the last person happened to solve it. A misconfigured IAM policy or an incorrectly sized database doesn't throw an exception or print a stack trace. It either silently costs money or takes down production in a way that's genuinely difficult to debug.&lt;/p&gt;

&lt;p&gt;Whether you write it yourself or generate it, someone has to review it before it touches production. That means verifying IAM policies aren't overly permissive, checking security groups are scoped correctly, confirming instance sizing makes sense for the workload, and running static analysis on the output before applying through staging and production. The work shifts from writing and reviewing Terraform to reviewing AI-generated Terraform, but you didn't write it, so you have to check everything with a fine-toothed comb.&lt;br&gt;
Terraform wasn't built for this&lt;/p&gt;

&lt;p&gt;I want to be clear that this isn't unique to Terraform. The same arguments apply to pretty much all IaC tools. The workflow Terraform enforces exists for good reason: plan, review, approve, apply, verify, repeat across environments. Infrastructure mistakes are expensive and sometimes irreversible, and when you're exposing your entire cloud provider's API surface including every IAM policy, every network rule, and every storage configuration, that level of scrutiny is genuinely warranted. The problem is that workflow was designed for infrastructure that changes infrequently and deliberately. It assumes the person writing the Terraform understands the full context of what they're configuring.&lt;/p&gt;

&lt;p&gt;AI-assisted development breaks both of those assumptions at once. Infrastructure changes aren't just new resources, they're configuration updates, scaling adjustments, environment variables, and things being removed. During early development and every time a feature is added or removed, the rate of change spikes. Those are exactly the moments when you're moving fastest and leaning on an agent the most. &lt;/p&gt;

&lt;p&gt;Think about what just happened with Redis, with just one performance issue during an iteration with your AI and the codebase had a new infrastructure dependency. That wasn't a deliberate decision made in a planning meeting, it was a side effect of a five minute conversation. The person reviewing is no longer reading decisions they made themselves, they're auditing a stream of generated output that has the potential to change the infrastructure in ways that are easy to miss and expensive to get wrong. The review process exists to catch exactly those mistakes, but it wasn't designed to run at this pace. So I'm either keeping up thorough reviews and becoming the bottleneck, or skipping them and accepting the risks.&lt;/p&gt;

&lt;h2&gt;
  
  
  I just want to show off my app
&lt;/h2&gt;

&lt;p&gt;Frontend teams went through this transition years ago and it wasn't just that Vercel was more convenient. It's that the pace of frontend development made the old model impractical. Frameworks were proliferating, build tooling was changing constantly, and the effort required to keep Nginx configs, SSL certs, and CI/CD pipelines current with all of it had quietly become a job in itself.&lt;/p&gt;

&lt;p&gt;When I first used Vercel for a frontend project, what struck me most was the ability to show someone what I'd built straight away, without having to configure a million things and hoping everything was right. Even if there were a few kinks to work out like bot protection or firewall rules, they could be resolved in minutes.&lt;/p&gt;

&lt;p&gt;Teams that moved to Vercel didn't do it because they couldn't manage their own infrastructure. Many of them were very good at it. They did it because the return on that investment had collapsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push to production
&lt;/h2&gt;

&lt;p&gt;There are a few ways to close the gap between writing code and shipping it. Some teams that I have spoken to are adapting their review processes with lighter touch on low-risk changes and stricter gates before production. Others are looking to build or further adapt their internal platforms to encode guardrails and reduce per-change overhead. Both are reasonable, but neither gets you out of the review cycle; they just make it more manageable.&lt;/p&gt;

&lt;p&gt;There's a specific feeling most backend developers rarely get to experience: finishing something and being able to share it immediately. &lt;/p&gt;

&lt;p&gt;That's what we've been working towards with &lt;a href="https://suga.app" rel="noopener noreferrer"&gt;Suga&lt;/a&gt;, and it could be an interesting option for you if you aren’t looking to maintain a shadow project. The review, the auditability, the environment parity still matter, the question is whether they have to come bundled with a separate repo, a pile of YAML, and a review workflow that wasn't designed for the pace we're now working at. &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%2Fgb46ebp1imdbwaficjwl.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%2Fgb46ebp1imdbwaficjwl.png" alt="Suga Canvas" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect your repository, define your services on a canvas, and a push to your branch handles the rest. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>saas</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploying a Sentiment Analysis API with Nitric and Python</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Tue, 08 Apr 2025 18:58:47 +0000</pubDate>
      <link>https://dev.to/rsiv/deploying-a-sentiment-analysis-api-with-nitric-and-python-o1h</link>
      <guid>https://dev.to/rsiv/deploying-a-sentiment-analysis-api-with-nitric-and-python-o1h</guid>
      <description>&lt;h1&gt;
  
  
  Deploying a Sentiment Analysis API with Nitric and Python
&lt;/h1&gt;

&lt;p&gt;In this guide, you’ll build a serverless API using &lt;a href="https://nitric.io" rel="noopener noreferrer"&gt;Nitric&lt;/a&gt; and Python that performs &lt;strong&gt;sentiment analysis&lt;/strong&gt; on text input using a pre-trained machine learning model. You'll use the &lt;code&gt;transformers&lt;/code&gt; library from Hugging Face and keep your project lightweight by installing dependencies directly with &lt;a href="https://github.com/astral-sh/uv" rel="noopener noreferrer"&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You'll Build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;POST /analyze&lt;/code&gt; HTTP endpoint&lt;/li&gt;
&lt;li&gt;A Python function that loads and uses an ML model&lt;/li&gt;
&lt;li&gt;A serverless app you can run locally and deploy to the cloud&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python 3.10+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nitric CLI&lt;/strong&gt;
Install using &lt;a href="https://nitric.io/docs/get-started/installation" rel="noopener noreferrer"&gt;official instructions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;uv&lt;/strong&gt;
Install from &lt;a href="https://github.com/astral-sh/uv" rel="noopener noreferrer"&gt;https://github.com/astral-sh/uv&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;- &lt;em&gt;(optional)&lt;/em&gt; Your choice of an &lt;a href="https://aws.amazon.com" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://cloud.google.com" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; or &lt;a href="https://azure.microsoft.com" rel="noopener noreferrer"&gt;Azure&lt;/a&gt; account&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Create a New Project
&lt;/h2&gt;

&lt;p&gt;Create a new Nitric Python project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric new ml-api py-starter
&lt;span class="nb"&gt;cd &lt;/span&gt;ml-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Install Dependencies
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;uv&lt;/code&gt; to install your required libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add torch &lt;span class="nv"&gt;transformers&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;4.39.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Write the Sentiment API
&lt;/h2&gt;

&lt;p&gt;Open or create the file &lt;code&gt;functions/api.py&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;

&lt;span class="c1"&gt;# Load the model once globally
&lt;/span&gt;&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentiment-analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ProsusAI/finbert&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define an API
&lt;/span&gt;&lt;span class="n"&gt;ml_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ml-api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@ml_api.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/analyze&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentiment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create the Nitric app and run it
&lt;/span&gt;&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines an API route that accepts a &lt;code&gt;POST /analyze&lt;/code&gt; request and returns the sentiment and confidence score based on the input text.&lt;/p&gt;




&lt;h2&gt;
  
  
  Run Locally
&lt;/h2&gt;

&lt;p&gt;Start your app locally using the Nitric CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once running, test it using the dashboard - &lt;a href="http://localhost:49152/" rel="noopener noreferrer"&gt;http://localhost:49152/&lt;/a&gt;&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%2Fqlui1t48d82atsqe7fqc.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%2Fqlui1t48d82atsqe7fqc.png" alt=" " width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or you can use cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:4001/analyze &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text": "The market is showing strong signs of recovery."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"positive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.996&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ The first request may take a few seconds as the model is downloaded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploy to the Cloud
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric stack new dev aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;nitric.dev.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nitric/aws@latest&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can also use &lt;code&gt;gcp&lt;/code&gt; or &lt;code&gt;azure&lt;/code&gt; depending on your cloud preference.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 2: Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nitric will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package your function (including installed ML libraries)&lt;/li&gt;
&lt;li&gt;Provision the API Gateway and runtime&lt;/li&gt;
&lt;li&gt;Deploy to your cloud account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CLI will print the public URL when deployment is complete.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test the Deployed API
&lt;/h2&gt;

&lt;p&gt;Replace &lt;code&gt;&amp;lt;your-url&amp;gt;&lt;/code&gt; with the deployed API base URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;your-url&amp;gt;/analyze &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text": "Interest rates are expected to rise."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;To destroy the cloud stack and avoid charges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You built a serverless ML inference API
&lt;/li&gt;
&lt;li&gt;Ran it locally with Nitric
&lt;/li&gt;
&lt;li&gt;Deployed it to the cloud with one command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now adapt this pattern for other use cases like classification, moderation, or lightweight recommendations.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Separating Concerns: Developers vs. Operations</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Wed, 08 Jan 2025 16:16:22 +0000</pubDate>
      <link>https://dev.to/rsiv/separating-concerns-developers-vs-operations-2jam</link>
      <guid>https://dev.to/rsiv/separating-concerns-developers-vs-operations-2jam</guid>
      <description>&lt;p&gt;When you’re building applications for the cloud, you naturally use a cloud provider’s SDK—AWS, Google Cloud, or Microsoft Azure, for example—to interact with services like storage buckets, queues, or databases. What seems straightforward at first can grow into a challenge. Before you know it, your application logic is tangled up in cloud-specific configuration, and two critical teams—Development and Operations—end up stepping on each other’s toes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Cost of Mixing Code and Cloud Config
&lt;/h3&gt;

&lt;p&gt;Cloud SDKs were designed to make cloud resources easy to access from your codebase. But the convenience they bring also drags provisioning details into application code. In other words, a developer writes logic to “store a file” and ends up having to specify which S3 bucket, its region, and the permission settings. This bleeds infrastructure concerns into the application domain.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;developers&lt;/strong&gt;, this means spending extra time wrangling IAM permissions and resource configurations instead of focusing on product features.&lt;br&gt;&lt;br&gt;
For &lt;strong&gt;operations&lt;/strong&gt;, it forces them to decode the application’s code just to understand everything that needs provisioning. Suddenly, managing the application’s infrastructure is more about code archaeology than consistent, reliable deployment strategies.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Separating Concerns Matters
&lt;/h3&gt;

&lt;p&gt;In well-functioning organizations, developers and operations each have distinct objectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; want to deliver business value quickly. They need to focus on crafting features and refining user experiences, not dealing with the specifics of IAM policies or message queue retention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; want to ensure those applications run reliably, securely, and cost-effectively in production. They need the freedom to optimize and manage cloud services without rewriting application code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When either side is forced to handle the other’s responsibilities, it creates friction. This friction can manifest as slower deployments, increased bugs, and a lot of confusion about who is responsible for what.&lt;/p&gt;
&lt;h3&gt;
  
  
  A Better Way
&lt;/h3&gt;

&lt;p&gt;The key to restoring order is to isolate cloud resource details behind an abstraction. Instead of importing AWS S3 or Google Cloud Storage SDKs directly in your application code, you can use a framework like &lt;a href="https://nitric.io/" rel="noopener noreferrer"&gt;Nitric&lt;/a&gt; that exposes common operations—like creating an API route or storing a file—without tying you to a specific cloud provider.&lt;/p&gt;

&lt;p&gt;Consider this code snippet (TypeScript):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nitric/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profiles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Developer expresses "write" intent&lt;/span&gt;

&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;process-often&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1 days&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Developer doesn't worry about S3 or GCS&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Developer’s Perspective
&lt;/h4&gt;

&lt;p&gt;Developers can now write application logic with minimal infrastructure distractions. They know there’s a storage bucket available, but they don’t need to specify which region it’s in or which encryption rules are in place. Their code focuses on creating user value—storing images, sending messages, processing events, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  Operations’ Perspective
&lt;/h4&gt;

&lt;p&gt;Operations sees a clearly defined “bucket” resource. They decide whether it’s an S3 bucket, a GCS bucket, or something else. They can choose to enable cross-region replication or encryption, all within their own IaC configuration. Developers aren’t forced to refactor or even touch the code; the ops team simply updates the infrastructure modules as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Missing Link: Automating the Specification
&lt;/h3&gt;

&lt;p&gt;Abstracting the cloud details is only half the battle. You also need a mechanism to document and communicate which resources each application needs.  &lt;/p&gt;

&lt;p&gt;Tools like Nitric scan your code to gather a &lt;strong&gt;specification&lt;/strong&gt; of resources used at runtime:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buckets
&lt;/li&gt;
&lt;li&gt;Schedules
&lt;/li&gt;
&lt;li&gt;Queues or Topics
&lt;/li&gt;
&lt;li&gt;Access policies
&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That spec is automatically generated—no guesswork. As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; don’t have to manually create or maintain a list of necessary resources.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; receives a clear map of what the application needs, making provisioning easier and more consistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example of the kind of specification that might be generated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"profiles"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Schedule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"process-often"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"schedule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo_services-hello"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"every"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 days"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting Specifications into IaC
&lt;/h3&gt;

&lt;p&gt;Once you have a consolidated specification, you can “transpile” it into Terraform, CloudFormation, or other Infrastructure as Code (IaC) tools. For instance, Nitric can automatically generate a Terraform stack using the &lt;a href="https://developer.hashicorp.com/terraform/cdktf" rel="noopener noreferrer"&gt;CDK for Terraform (CDKTF)&lt;/a&gt;.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; remain blissfully unaware of the provider-specific config.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; is free to create or reuse modules that set up buckets, schedules, or messages exactly how they want. They can decide on replication strategies, naming conventions, and compliance rules—all outside the application code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Outcome: Clear Boundaries, Less Friction
&lt;/h3&gt;

&lt;p&gt;By pushing cloud-specific details out of application code, teams can return to their core competencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; focus on creating rich features and experiences.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; ensures robust, secure, and efficient infrastructure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a sustainable model that benefits both sides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less context-switching&lt;/strong&gt;: Developers don’t wade into Ops territory.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fewer misconfigurations&lt;/strong&gt;: Operations controls provisioning details centrally.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster deployments&lt;/strong&gt;: Changes no longer hinge on back-and-forth clarifications or digging into code to find resource dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion: Level Up Your Dev–Ops Collaboration
&lt;/h3&gt;

&lt;p&gt;Separation of concerns isn’t a new concept, but it’s often overlooked in cloud development. Mixing infrastructure configurations directly into application code can weigh down both developers and operations, slowing innovation and complicating deployments. By abstracting away cloud SDKs and automating infrastructure specifications, each team can focus on what they do best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to see it in action?&lt;/strong&gt; Explore &lt;a href="https://nitric.io/" rel="noopener noreferrer"&gt;Nitric&lt;/a&gt; to discover how you can simplify your application code, streamline provisioning, and enjoy smoother collaboration between Dev and Ops. It’s a win for developer creativity, a win for operational control—and a big win for your entire organization.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Llama 3.2 on Lambda</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 25 Nov 2024 14:08:21 +0000</pubDate>
      <link>https://dev.to/rsiv/llama-32-on-lambda-k9p</link>
      <guid>https://dev.to/rsiv/llama-32-on-lambda-k9p</guid>
      <description>&lt;p&gt;&lt;a href="https://www.llama.com/" rel="noopener noreferrer"&gt;Llama 3.2 1B&lt;/a&gt; is a lightweight AI model that makes it interesting for serverless applications since it can be run relatively quickly without requiring GPU acceleration.&lt;/p&gt;

&lt;p&gt;We’ll use models from &lt;a href="https://huggingface.co/" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt; and Nitric to demonstrate using it and manage the surrounding infrastructure, such as API routes and deployments.&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.astral.sh/uv/#getting-started" rel="noopener noreferrer"&gt;uv&lt;/a&gt; — for Python dependency management&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://nitric.io/docs/get-started/installation" rel="noopener noreferrer"&gt;Nitric CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(optional) An &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Let’s start by creating a new project using Nitric’s Python starter template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric new llama py-starter
&lt;span class="nb"&gt;cd &lt;/span&gt;llama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s install the base dependencies, then add the extra dependencies we need specifically for loading the language model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the base dependencies&lt;/span&gt;
uv &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;span class="c"&gt;# Add the llama-cpp-python dependency&lt;/span&gt;
uv add llama-cpp-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Choose a Llama Model
&lt;/h2&gt;

&lt;p&gt;Llama 3.2 is available in different sizes and configurations, each with its own trade-offs in terms of performance, accuracy and resource requirements. For serverless applications without GPU acceleration, such as &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; Lambda, it’s important to choose a &lt;a href="https://thenewstack.io/clean-data-trusted-model-ensure-good-data-hygiene-for-your-llms/" rel="noopener noreferrer"&gt;model that is lightweight and efficient&lt;/a&gt; to ensure it runs within the constraints of that environment.&lt;/p&gt;

&lt;p&gt;We’ll use a quantized version of the lightweight Llama 1B model, specifically &lt;a href="https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/blob/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf" rel="noopener noreferrer"&gt;Llama-3.2-1B-Instruct-Q4_K_M.gguf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with &lt;a href="https://huggingface.co/docs/optimum/en/concept_guides/quantization" rel="noopener noreferrer"&gt;quantization&lt;/a&gt;, it’s a technique that reduces a model’s size and resource requirements, which, in our case, makes it suitable for serverless applications but may affect the accuracy of the model.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt; team provides several quantized versions of Llama 3.2 1B on &lt;a href="https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;. Consider trying different versions to find one that best fits your needs, such as Q5_K_M, which is slightly larger but of higher quality.&lt;/p&gt;

&lt;p&gt;Let’s download the chosen model and save it in a models directory in your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf" rel="noopener noreferrer"&gt;Download link for Llama-3.2-1B-Instruct-Q4_K_M.gguf&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;models
&lt;span class="nb"&gt;cd &lt;/span&gt;models
&lt;span class="c"&gt;# This model is 0.81GB, it may take a little while to download&lt;/span&gt;
curl &lt;span class="nt"&gt;-OL&lt;/span&gt; https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your folder structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/llama
    /models
        Llama-3.2-1B-Instruct-Q4_K_M.gguf
    /services
        api.py
    nitric.yaml
    pyproject.toml
    python.dockerfile
    python.dockerfile.ignore
    README.md
    uv.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a Service to Run the Model
&lt;/h2&gt;

&lt;p&gt;Next, we’ll use Nitric to create an HTTP API that allows you to send prompts to the Llama model and receive the output in a response. The API will return the raw output from the model, but you can adjust this as you see fit.&lt;/p&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;services/api.py&lt;/code&gt; with the following code, which loads the Llama model and implements the prompt functionality. Take a little time to understand the code. It defines an API with a single endpoint /prompt that accepts a POST request with a prompt in the body. The &lt;code&gt;process_prompt&lt;/code&gt; function sends the prompt to the Llama model and returns the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_cpp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Llama&lt;/span&gt;

&lt;span class="c1"&gt;# Load the locally stored Llama model
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Llama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./models/Llama-3.2-1B-Instruct-Q4_K_M.gguf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Function to execute a prompt using the Llama model
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# See https://www.llama.com/docs/model-cards-and-prompt-formats/llama3_1/ for details about prompt format
&lt;/span&gt;    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;# System Prompt
&lt;/span&gt;        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|start_header_id|&amp;gt;system&amp;lt;|end_header_id|&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|eot_id|&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="c1"&gt;# User Prompt
&lt;/span&gt;        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|start_header_id|&amp;gt;user&amp;lt;|end_header_id|&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|eot_id|&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="c1"&gt;# Start assistants turn (we leave this open ended as the assistant hasn't started its turn)
&lt;/span&gt;        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|start_header_id|&amp;gt;assistant&amp;lt;|end_header_id|&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;# Unlimited, consider setting a token limit
&lt;/span&gt;        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&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="n"&gt;response&lt;/span&gt;

&lt;span class="c1"&gt;# Define an API for the prompt service
&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@main.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# assume the input is text/plain
&lt;/span&gt;    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error processing prompt: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OK, Let’s Run This Thing!
&lt;/h2&gt;

&lt;p&gt;Now that you have an API defined, we can test it locally. The Python starter template uses python3.11-bookworm-slim as its basic container image, which doesn’t have the right dependencies to load the Llama model; let’s update the Dockerfile to use python3.11-bookworm (the non-slim version) instead.&lt;/p&gt;

&lt;p&gt;Update line 2:&lt;/p&gt;

&lt;p&gt;FROM python:3.11-bookworm&lt;br&gt;
1&lt;br&gt;
FROM python:3.11-bookworm&lt;/p&gt;

&lt;p&gt;Update line 19:&lt;/p&gt;

&lt;p&gt;FROM python:3.11-bookworm&lt;br&gt;
1&lt;br&gt;
FROM python:3.11-bookworm&lt;/p&gt;

&lt;p&gt;Now we can run our services locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;nitric run&lt;/code&gt; will start your application in a container that includes the dependencies to use llama_cpp. If you’d rather use nitric start you’ll need to install dependencies for llama-cpp-python such as &lt;a href="https://cmake.org/download/" rel="noopener noreferrer"&gt;CMake&lt;/a&gt; and &lt;a href="https://releases.llvm.org/download.html" rel="noopener noreferrer"&gt;LLVM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once it starts, you can test it with the Nitric Dashboard.&lt;/p&gt;

&lt;p&gt;You can find the URL to the dashboard in the terminal running the Nitric CLI. By default it’s &lt;a href="http://localhost:49152" rel="noopener noreferrer"&gt;http://localhost:49152&lt;/a&gt;. Add a prompt to the body of the request and send it to the &lt;code&gt;/prompt&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  IMAGE GOES HERE
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Deploying to AWS
&lt;/h2&gt;

&lt;p&gt;When you’re ready to deploy the project, we can create a new Nitric stack file that will target AWS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric stack new dev aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the stack file &lt;code&gt;nitric.dev.yaml&lt;/code&gt; with the appropriate AWS region and memory allocation to handle the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nitric/aws@1.14.0&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# How services will be deployed by default, if you have other services not running models&lt;/span&gt;
  &lt;span class="c1"&gt;# you can add them here too so they don't use the same configuration&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;lambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Set the memory to 6GB to handle the model, this automatically sets additional CPU allocation&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6144&lt;/span&gt;
      &lt;span class="c1"&gt;# Set a timeout of 30 seconds (this is the most API Gateway will wait for a response)&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="c1"&gt;# We add more storage to the lambda function, so it can store the model&lt;/span&gt;
      &lt;span class="na"&gt;ephemeral-storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we’ll use Nitric’s default Pulumi AWS Provider, make sure you’re set up to deploy using it. You can find more information on how to set up the AWS Provider in the &lt;a href="https://nitric.io/docs/providers/pulumi/aws" rel="noopener noreferrer"&gt;Nitric AWS Provider documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to deploy with Terraform or to another cloud provider, that’s also possible. You can find more information about how Nitric can deploy to other platforms in the &lt;a href="https://nitric.io/docs/providers" rel="noopener noreferrer"&gt;Nitric Providers documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can then deploy using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of the API endpoint URL that is output after the deployment is complete.&lt;/p&gt;

&lt;p&gt;If you’re done with the project later, tear it down with &lt;code&gt;nitric down&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing on AWS
&lt;/h2&gt;

&lt;p&gt;To test the service, you can use any API testing tool you like, such as cURL, Postman, etc. Here’s an example using cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="o"&gt;{&lt;/span&gt;your endpoint URL here&lt;span class="o"&gt;}&lt;/span&gt;/prompt &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Hello, how are you?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example Response
&lt;/h3&gt;

&lt;p&gt;The response will include the results, plus other metadata. The output can be found in the choices array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cmpl-61064b38-45f9-496d-86d6-fdae4bc3db97"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text_completion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729655327&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./models/Llama-3.2-1B-Instruct-Q4_K_M.gguf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"choices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;I'm doing well, thank you for asking. I'm here and ready to assist you, so that's a good start! How can I help you today?&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"logprobs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"finish_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stop"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prompt_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"completion_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As you’ve seen in the code example, we’ve set up a fairly basic prompt structure, but you can expand on this to include more complex prompts, including system prompts that help restrict/guide the model’s responses or even more complex interactions with the model. Also, in this example, we expose the model directly as an API, but this limits the response time to 30 seconds on AWS with API Gateway.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://nitric.io/docs/guides" rel="noopener noreferrer"&gt;future guides&lt;/a&gt;, we’ll show how you can go beyond simple one-time responses to more complex interactions, such as maintaining context between requests. We can also include Websockets and streamed responses to provide a better user experience for larger responses.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cloud</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Bringing declarative infrastructure to developers</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 14 Oct 2024 15:13:30 +0000</pubDate>
      <link>https://dev.to/rsiv/bringing-declarative-infrastructure-to-developers-3380</link>
      <guid>https://dev.to/rsiv/bringing-declarative-infrastructure-to-developers-3380</guid>
      <description>&lt;p&gt;Over the past decade, Terraform has revolutionized how operations (Ops) teams manage cloud infrastructure by providing a simple, declarative way to define and provision resources. &lt;br&gt;
Terraform allows us to declare cloud resources using a simple configuration language (HCL), which can then be executed to create or modify resources. &lt;/p&gt;

&lt;p&gt;Here’s a simple example of declaring a bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-example-bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MyExampleBucket"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Dev"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;When you run terraform apply, terraform relies on providers to parse and translate the configuration into API requests. The provider acts as a bridge between Terraform and the AWS API, it constructs the necessary API calls to fulfill the requirements specification and sends them to AWS, where the actual creation of the bucket, tagging, and versioning happens. &lt;/p&gt;

&lt;p&gt;Terraform handles all the communication with cloud providers, ensuring that resources are created or updated according to the configuration file, without requiring direct interaction with cloud APIs. But what about developers who also need to work with cloud infrastructure?&lt;/p&gt;

&lt;p&gt;Our current workflow generally leads us towards building an application first, then handling the infrastructure. &lt;/p&gt;

&lt;p&gt;This means that developers have a few choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create variables for all unknown configuration and throw the ball over the fence to an ops team&lt;/li&gt;
&lt;li&gt;Work intimately with the ops team to detail resource names, configuration etc.&lt;/li&gt;
&lt;li&gt;Do it themselves with tools they aren’t in love with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these options are particularly desirable as they rely on a lot of manual steps which often lead to communication breakdowns and runtime/provision time errors. We need a way of shifting the declaration of resources forward in the workflow without introducing additional complexity to our teams. &lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative infrastructure for developers
&lt;/h2&gt;

&lt;p&gt;I’m not suggesting that developers don’t need to know how storage buckets, databases or APIs work, they just don’t need to know the specifics of how they will be provisioned while they are writing their application code.&lt;/p&gt;

&lt;p&gt;With Infrastructure from Code (IfC), developers can declaratively define and request cloud resources such as APIs, storage buckets, and queues directly within their application logic. This declarative approach allows developers to specify what they want without worrying about how they will be provisioned. &lt;/p&gt;

&lt;p&gt;Here’s an example of how this might work, and, actually, this should look extremely familiar, the only addition a declaration of the developers intent - to allow reading and writing on the bucket and this information is the missing link to assign the right permissions for the resources to interact with each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nitric/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Define a bucket for storing images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imagesBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Define an API for serving images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mainApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;mainApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/images/:key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="c1"&gt;// Your image-serving logic here&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this example it's simple enough to recognize that we need the following cloud resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bucket('images').allow('read', 'write'): Creates a cloud storage bucket named "images" and sets access permissions for reading and writing.&lt;/li&gt;
&lt;li&gt;api('main'): Creates an API named "main" with a route to serve images. The developer can then define application logic for handling image requests.&lt;/li&gt;
&lt;li&gt;OpenAPI specification: From the routes configured we can automatically create the configuration required for our API gateways.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can automatically generate the requirements specification -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"openapi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;components&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;info&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;v1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;openapi&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3.0.1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;paths&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/images/{key}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;operationId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;imageskeyget&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;responses&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;}},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;x-nitric-target&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;api-testing_services-hello&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;parameters&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;in&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:true,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;schema&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}]}}}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bridging Runtime and Provisioning Gaps
&lt;/h2&gt;

&lt;p&gt;One of the major challenges developers face is ensuring that the resources their application depends on are correctly provisioned at runtime. The generated specification ensures that the correct resources (storage buckets, APIs, etc.) are provisioned with the appropriate settings (permissions, paths, etc.). This removes the potential for mismatches between what the application requires and what is provisioned, ensuring a smoother deployment process.&lt;/p&gt;

&lt;p&gt;We can now use it to orchestrate the necessary IaC, like the Terraform module above, to provision the required resources. This approach minimizes runtime and provision-time errors, ensuring that resources are automatically provisioned in line with application requirements. &lt;/p&gt;

&lt;p&gt;It also means that the same application can be deployed across different cloud providers, which means you can pivot at any time to take advantage of the latest technologies and disruptors like AI.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>cloudcomputing</category>
      <category>infrastructureascode</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Nitric is Terraform for Developers</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 19 Aug 2024 14:45:40 +0000</pubDate>
      <link>https://dev.to/rsiv/nitric-is-terraform-for-developers-3560</link>
      <guid>https://dev.to/rsiv/nitric-is-terraform-for-developers-3560</guid>
      <description>&lt;p&gt;As of the 29th of July this year, Terraform has been around for an entire decade! That’s right a one and a zero, Happy Birthday Terraform 🎉.&lt;/p&gt;

&lt;p&gt;Terraform is best known for its declarative approach to infrastructure engineering, the product of which is executable documentation. Not only do we get clean documents that allow a reader to quickly reason about the architecture of a deployment, but that documentation can also be executed by Terraform to produce and execute a plan for that deployment.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, after a full decade of modern Infrastructure as Code (IaC) tooling we’re starting to see a whole new category of software emerge around it, Infrastructure from Code (IfC).&lt;/p&gt;

&lt;p&gt;The core goal of the IfC paradigm is to bring to application development the same capability that tools like Terraform have allowed infrastructure engineers to enjoy for the past 10 years: the ability to document the requirements of our applications in a clear, concise, and executable fashion.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://github.com/nitrictech/nitric" rel="noopener noreferrer"&gt;Nitric&lt;/a&gt; as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nitric/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Document we need a bucket&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imagesBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Document we need an API&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mainApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mainApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/images/:key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="c1"&gt;// Code here&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Nitric SDKs allow us to not only express our requirements but also allow interaction with those requirements as well.&lt;/p&gt;

&lt;p&gt;While these requirements can be spread out across a number of different services as files, it’s simple for us to capture this in a single highly declarative format using the Nitric CLI if we run:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nitric spec -o spec.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Against our project, your Nitric infrastructure specification can be viewed as a simple JSON document, detailing your application’s requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"openapi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;components&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;info&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;v1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;openapi&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3.0.1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;paths&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/images/{key}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;operationId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;imageskeyget&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;responses&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;}},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;x-nitric-target&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;api-testing_services-hello&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}},&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;parameters&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;in&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:true,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;schema&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}]}}}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3ed1c501e832fef808a464f42959858c"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"principals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-testing_services-hello"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"BucketFileList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"BucketFileGet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"BucketFilePut"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-testing_services-hello"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-testing_services-hello"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"workers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, Nitric uses this infrastructure specification to provide support for multiple clouds and IaC technologies.&lt;/p&gt;

&lt;p&gt;If you check out this recent blog, &lt;a href="https://nitric.io/blog/terraform-api" rel="noopener noreferrer"&gt;Putting Terraform behind an API&lt;/a&gt; you can see how the above JSON could be provided as a payload. Suddenly you would have an API that outputs Terraform directly from application code.&lt;/p&gt;

&lt;p&gt;The point here is that while Terraform and similar technologies have existed for the past decade, providing Ops and Infrastructure teams with the tools they need to cleanly document their infrastructure, application development teams have had access to no such tools in this time. This is what the emergence of IfC is all about.&lt;/p&gt;

&lt;p&gt;If you’re someone who works with infrastructure to help organizations deliver value by getting software out into the world, and care about a smooth and cohesive development experience, I would urge you to take a closer look at IfC as a whole and how you could leverage existing technology in this space or even build your own version of it.&lt;/p&gt;

&lt;p&gt;If you'd like to support us, please check out the framework and give it a try, also help us spread the word by &lt;a href="https://github.com/nitrictech/nitric" rel="noopener noreferrer"&gt;🌟 starring us on GitHub!&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>cloudcomputing</category>
      <category>terraform</category>
      <category>automation</category>
    </item>
    <item>
      <title>Secure Upload URLs Buckets with Nitric in Python</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 11 Mar 2024 18:54:15 +0000</pubDate>
      <link>https://dev.to/rsiv/secure-upload-urls-buckets-with-nitric-in-python-495o</link>
      <guid>https://dev.to/rsiv/secure-upload-urls-buckets-with-nitric-in-python-495o</guid>
      <description>&lt;p&gt;Creating a secure method for users to upload files directly to an S3 bucket is a common requirement in modern web applications. &lt;/p&gt;

&lt;p&gt;By using pre-signed URLs, you can allow a client to upload a file directly to S3 without exposing your AWS credentials, they also provide a secure mechanism to upload files directly to S3 from a client, avoiding the need to pass the file data through your server.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will leverage the Nitric SDK in Python to generate secure upload URLs for an S3 bucket, which can then be used from your front-end application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't used the &lt;a href="https://nitric.io" rel="noopener noreferrer"&gt;Nitric SDK&lt;/a&gt; before, then start with this &lt;a href="https://dev.to/rsiv/your-first-nitric-api-using-python-m8p"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1: Initialize Nitric Bucket Instance
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;main&lt;/code&gt; function, initialize a new Nitric bucket instance with the name of your S3 bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;

&lt;span class="n"&gt;main_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reading&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Generate a Secure Upload URL
&lt;/h3&gt;

&lt;p&gt;Now, generate a secure upload URL for a specific file. In this example, we are creating a URL for uploading a file named &lt;code&gt;cat.png&lt;/code&gt;. We also specify a time-to-live (TTL) for the URL of 300 seconds (5 minutes).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;

&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;logo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logo_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;logo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you have a secure upload URL for your &lt;code&gt;cat.png&lt;/code&gt; file. You can use this URL in your front-end application to securely upload the &lt;code&gt;cat.png&lt;/code&gt; file to your S3 bucket. The URL will expire after 1 hour, ensuring that the upload URL cannot be misused after a reasonable amount of time.&lt;/p&gt;

&lt;p&gt;This setup abstracts much of the boilerplate and complexity involved in generating pre-signed URLs for S3, allowing developers to focus more on building their applications. &lt;/p&gt;

&lt;p&gt;In the case that you want to get an image url for rendering, then simply redirect the URL with a 303 status and a location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;

&lt;span class="n"&gt;main_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reading&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@main_api.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/images/:id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;download_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;303&lt;/span&gt;
  &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Location&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://nitric.io/docs/guides/getting-started/concepts" rel="noopener noreferrer"&gt;Nitric SDK&lt;/a&gt; offers a streamlined approach to interacting with AWS S3, among other cloud services, in a cloud-agnostic manner.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>cloudcomputing</category>
      <category>productivity</category>
      <category>python</category>
    </item>
    <item>
      <title>Running a scheduled task with Nitric in Python</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 11 Mar 2024 18:09:32 +0000</pubDate>
      <link>https://dev.to/rsiv/running-a-scheduled-task-with-nitric-in-python-53lp</link>
      <guid>https://dev.to/rsiv/running-a-scheduled-task-with-nitric-in-python-53lp</guid>
      <description>&lt;p&gt;Setting up a scheduled job is a common requirement for many modern applications. &lt;/p&gt;

&lt;p&gt;With the Nitric SDK, this task is simplified, and you can even test your scheduled jobs offline without needing Terraform. &lt;/p&gt;

&lt;p&gt;In this tutorial, we'll walk through setting up a scheduled job that aggregates data every three days using the Nitric SDK in Python.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't used the &lt;a href="https://nitric.io" rel="noopener noreferrer"&gt;Nitric SDK&lt;/a&gt; before, then start with this &lt;a href="https://dev.to/rsiv/your-first-nitric-api-using-python-m8p"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: Import Necessary Libraries
&lt;/h2&gt;

&lt;p&gt;Start by importing the necessary libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IntervalContext&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Define Your Scheduled Job
&lt;/h2&gt;

&lt;p&gt;Next, define a function for your scheduled job. In this example, we're creating a new scheduled job named "aggregate-data" that will run every three days. &lt;/p&gt;

&lt;p&gt;Inside the function, we've added a simple print statement "aggregating data" to demonstrate the job's activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IntervalContext&lt;/span&gt;

&lt;span class="n"&gt;report_schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;run-a-report&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@report_schedule.every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1 days&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IntervalContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="c1"&gt;# do some processing
&lt;/span&gt;  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aggregating data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Your Scheduled Job Offline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the advantages of using the Nitric SDK is the ability to test your scheduled jobs offline. This is particularly useful for debugging and ensuring your jobs are working as expected before deploying them to the cloud.&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%2Feoj1bpzcc5mv0yemi331.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%2Feoj1bpzcc5mv0yemi331.png" alt="Testing schedules offline with the Nitric Dashboard" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In just a few steps, you've now created a scheduled job that aggregates data every three days, here are a few more examples showing you how to set up other frequencies.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;th&gt;example schedule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every day&lt;/td&gt;
&lt;td&gt;@schedule("work").every("day")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every 14 hours&lt;/td&gt;
&lt;td&gt;@schedule("work").every("14 hours")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every 30 minutes&lt;/td&gt;
&lt;td&gt;@schedule("work").every("30 minutes")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every day (cron)&lt;/td&gt;
&lt;td&gt;@schedule("work").cron("0 0 * * *")&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cloudcomputing</category>
      <category>python</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Creating Bucket (S3) Notifications on AWS with Python</title>
      <dc:creator>Rk </dc:creator>
      <pubDate>Mon, 11 Mar 2024 17:59:11 +0000</pubDate>
      <link>https://dev.to/rsiv/creating-bucket-s3-notifications-on-aws-with-python-22pa</link>
      <guid>https://dev.to/rsiv/creating-bucket-s3-notifications-on-aws-with-python-22pa</guid>
      <description>&lt;p&gt;In this tutorial, we’ll explore how to set up bucket notifications in Python, enabling your applications to react to changes in file storage buckets. This is particularly useful in scenarios where you need to monitor a bucket for new file uploads or deletions. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't used the &lt;a href="https://nitric.io" rel="noopener noreferrer"&gt;Nitric SDK&lt;/a&gt; before, then start with this &lt;a href="https://dev.to/rsiv/your-first-nitric-api-using-python-m8p"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Create a Bucket Instance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Import the necessary packages and instantiate a new bucket object. In this tutorial, we'll name our bucket 'assets'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;

&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;accessible_assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reading&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Set Up Notifications for File Write Events&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To monitor for file write events, specifically for files starting with a certain prefix, use the &lt;code&gt;On&lt;/code&gt; method on the bucket object. Let’s trigger notifications for files starting with '/users/images'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;

&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;accessible_assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reading&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The request will contain the name of the file `key` and the type of event `type`
&lt;/span&gt;&lt;span class="nd"&gt;@assets.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delete&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_anything&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a file named &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was deleted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@assets.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/images/cat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_cat_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A cat image was written&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# If `on` is called with a permissioned bucket, a file will also be provided with the request
&lt;/span&gt;&lt;span class="nd"&gt;@accessible_assets.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/images/dog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;access_dog_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;dog_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dog_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Set Up Notifications for File Delete Events&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Similarly, to monitor for file deletion events for any file, set up a notification like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nitric.application&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Nitric&lt;/span&gt;

&lt;span class="c1"&gt;# Create a reference to an 'assets' bucket with permissions to delete
&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deleting&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;images/logo.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Nitric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Avoid writing or deleting to the bucket from within a notification as this can trigger the notification again, potentially causing an infinite loop which can be costly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Let's run this thing!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nitric start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now you have set up bucket notifications in Python using the Nitric SDK. &lt;/p&gt;

&lt;p&gt;This setup will help you monitor file write and delete events, allowing your application to react to changes in your bucket accordingly. &lt;/p&gt;

</description>
      <category>cloudcomputing</category>
      <category>python</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
