<?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: Jim Zandueta</title>
    <description>The latest articles on DEV Community by Jim Zandueta (@jimzandueta).</description>
    <link>https://dev.to/jimzandueta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F184387%2Fbff514f7-ba23-468a-9a24-6e44b5c53f33.jpg</url>
      <title>DEV Community: Jim Zandueta</title>
      <link>https://dev.to/jimzandueta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jimzandueta"/>
    <language>en</language>
    <item>
      <title>The Codex Setup That Worked for Us: Memory, Manifests, and Structured Context</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Wed, 01 Apr 2026 07:14:45 +0000</pubDate>
      <link>https://dev.to/jimzandueta/from-ai-chaos-to-team-flow-codex-boilerplate-that-actually-worked-5fa1</link>
      <guid>https://dev.to/jimzandueta/from-ai-chaos-to-team-flow-codex-boilerplate-that-actually-worked-5fa1</guid>
      <description>&lt;p&gt;&lt;strong&gt;The past few weeks have been wild.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our team recently adopted &lt;strong&gt;AI-assisted programming&lt;/strong&gt; (not vibe-coding). We wanted speed, consistency, and fewer repetitive tasks.&lt;br&gt;&lt;br&gt;
What we got in week one was... chaos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
We had &lt;strong&gt;inconsistent AI output&lt;/strong&gt; across teammates, treated it as a &lt;strong&gt;systems problem&lt;/strong&gt;, applied Agentic SDLC principles, and built a &lt;code&gt;.codex&lt;/code&gt; structure that made Codex outputs &lt;strong&gt;far more consistent&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Quick Jump
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What worked (and what didn’t)&lt;/li&gt;
&lt;li&gt;The shift: Agentic Software Development Lifecycle&lt;/li&gt;
&lt;li&gt;What’s inside &lt;code&gt;.codex&lt;/code&gt; (and why it matters)&lt;/li&gt;
&lt;li&gt;How we used it in this repo&lt;/li&gt;
&lt;li&gt;What’s next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even with detailed technical tickets, our AI agents kept producing outputs that didn’t line up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different &lt;strong&gt;naming conventions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;different &lt;strong&gt;folder structures&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;different &lt;strong&gt;approaches to the same problem&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;different &lt;strong&gt;error-handling and testing styles&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every merge felt like stitching code from three different universes.&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%2Fx9qa4grriown1y2r5vf8.jpg" 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%2Fx9qa4grriown1y2r5vf8.jpg" alt="Distracted Boyfriend Meme" width="750" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Week one energy:&lt;/strong&gt; &lt;em&gt;us trying to keep standards while random AI output keeps walking by.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  What worked (and what didn’t)
&lt;/h2&gt;

&lt;p&gt;We tested different setups.&lt;/p&gt;

&lt;p&gt;A strong &lt;code&gt;CLAUDE.md&lt;/code&gt; helped a lot early. Claude Opus was excellent at reasoning and breaking work down step by step.&lt;br&gt;&lt;br&gt;
The issue for us was practical: &lt;strong&gt;limits and timeouts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Since our company sponsors Codex, we moved to Codex as our main tool. First impression? &lt;strong&gt;A bit lackluster&lt;/strong&gt; compared to Claude out of the box.&lt;/p&gt;

&lt;p&gt;That pushed us to a better question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maybe this isn’t just a model problem. Maybe it’s a &lt;u&gt;system problem&lt;/u&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//i.imgflip.com/ao4a58.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//i.imgflip.com/ao4a58.jpg" alt="Drake Meme"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The shift: Agentic Software Development Lifecycle
&lt;/h2&gt;

&lt;p&gt;Quick diff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traditional SDLC: &lt;strong&gt;mostly human implementation through linear phases&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A-SDLC: &lt;strong&gt;human + AI-agent collaboration in tight feedback loops&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In A-SDLC, developers don’t just write code. We &lt;strong&gt;orchestrate&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;guardrails&lt;/li&gt;
&lt;li&gt;context&lt;/li&gt;
&lt;li&gt;fast reviews&lt;/li&gt;
&lt;li&gt;memory updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yes, we’re &lt;strong&gt;actively applying&lt;/strong&gt; these principles in real day-to-day work, not just talking about them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better prompts&lt;/li&gt;
&lt;li&gt;tighter feedback loops&lt;/li&gt;
&lt;li&gt;stronger project memory&lt;/li&gt;
&lt;li&gt;clear constraints, patterns, and checklists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we treated this as a systems problem, &lt;strong&gt;output quality improved fast&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s why I built this boilerplate: to showcase the &lt;code&gt;.codex&lt;/code&gt; setup that worked best for us.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/jimzandueta/nestjs-http-server-boilerplate-codex-ai-assisted" rel="noopener noreferrer"&gt;github.com/jimzandueta/codex-nestjs&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What’s inside &lt;code&gt;.codex&lt;/code&gt; (and why it matters)
&lt;/h2&gt;

&lt;p&gt;This isn’t just a random folder. It’s the &lt;strong&gt;operating system&lt;/strong&gt; for consistent AI-assisted engineering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.codex/
  START_HERE.md
  RULES.md
  MANIFEST.yaml
  instructions/
  patterns/
  anti-patterns/
  checklists/
  skills/
  prompts/
  templates/
  overrides/
  memory/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F%2F%2Fi.imgflip.com%2Fao4aax.jpg" 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%2F%2F%2Fi.imgflip.com%2Fao4aax.jpg" alt="Expanding Brain Meme" width="500" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1) &lt;code&gt;START_HERE.md&lt;/code&gt; + &lt;code&gt;RULES.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;These are your baseline guardrails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Output Rules&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Reuse existing patterns before inventing new ones.
&lt;span class="p"&gt;2.&lt;/span&gt; Keep diffs minimal.
&lt;span class="p"&gt;3.&lt;/span&gt; Never commit secrets.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone prevents a lot of “same task, five coding styles” situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) &lt;code&gt;MANIFEST.yaml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is context routing. It tells the agent what to load for each task type.&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;task_routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;new-feature&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.codex/instructions/global.md&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.codex/patterns/repo-structure.md&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.codex/patterns/error-handling.md&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.codex/skills/new-feature/SKILL.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So agents don’t start cold. They start with the right playbook.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) &lt;code&gt;instructions/&lt;/code&gt;, &lt;code&gt;patterns/&lt;/code&gt;, &lt;code&gt;anti-patterns/&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;instructions/&lt;/code&gt;: how to work&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;patterns/&lt;/code&gt;: preferred way to build&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anti-patterns/&lt;/code&gt;: what to avoid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of this as turning tribal team knowledge into repeatable, machine-readable engineering practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) &lt;code&gt;checklists/&lt;/code&gt;, &lt;code&gt;skills/&lt;/code&gt;, &lt;code&gt;prompts/&lt;/code&gt;, &lt;code&gt;templates/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the day-to-day execution layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;checklists/&lt;/code&gt;: quality gates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skills/&lt;/code&gt;: repeatable workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prompts/&lt;/code&gt;: reusable prompt scaffolds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;templates/&lt;/code&gt;: starter artifacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example checklist snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Tests&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] New logic has happy path + failure test
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Coverage stays at 100% threshold
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No flaky tests introduced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5) &lt;code&gt;overrides/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This lets you keep generic Codex assets while declaring project reality.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;generic pattern: “recommended structure”&lt;/li&gt;
&lt;li&gt;project override: “this NestJS repo uses &lt;code&gt;src/common&lt;/code&gt;, &lt;code&gt;src/clients&lt;/code&gt;, &lt;code&gt;src/modules&lt;/code&gt;, etc.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6) &lt;code&gt;memory/&lt;/code&gt; (the secret sauce)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;This is where consistency compounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;memory/project-facts.md&lt;/code&gt; → stable project truths&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memory/decisions.md&lt;/code&gt; → ADR-style decisions/tradeoffs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memory/learned-patterns.md&lt;/code&gt; → recurring conventions discovered during work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As new decisions are made between the developer and AI agent, memory gets updated so future tasks inherit the &lt;strong&gt;same context and tradeoffs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A realistic flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer: “We need &lt;code&gt;requestId&lt;/code&gt; in logs for traceability.”&lt;/li&gt;
&lt;li&gt;Agent: “Two options: &lt;code&gt;AsyncLocalStorage&lt;/code&gt; or explicit propagation.”&lt;/li&gt;
&lt;li&gt;Team decision: explicit propagation first (simpler + easier to test).&lt;/li&gt;
&lt;li&gt;Memory updates: ADR + project convention + learned pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample ADR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### ADR-002: Standardize request correlation IDs in HTTP logs&lt;/span&gt;

&lt;span class="gs"&gt;**Date**&lt;/span&gt;: 2026-04-02
&lt;span class="gs"&gt;**Status**&lt;/span&gt;: Accepted

&lt;span class="gs"&gt;**Context**&lt;/span&gt;: Debugging incidents was slow because logs across layers were hard to correlate.
&lt;span class="gs"&gt;**Decision**&lt;/span&gt;: Add &lt;span class="sb"&gt;`requestId`&lt;/span&gt; at the HTTP boundary and propagate it through services/clients.
&lt;span class="gs"&gt;**Consequences**&lt;/span&gt;: Better traceability, with slight method-signature overhead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample project facts update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Logging: include &lt;span class="sb"&gt;`requestId`&lt;/span&gt; in structured logs for HTTP flows.
&lt;span class="p"&gt;-&lt;/span&gt; Request context: generate/forward &lt;span class="sb"&gt;`x-request-id`&lt;/span&gt; at ingress and propagate downstream.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample learned pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### LP-001: Propagate requestId from boundary to integrations&lt;/span&gt;

&lt;span class="gs"&gt;**Observed**&lt;/span&gt;: Missing correlation fields made multi-step failures harder to debug.
&lt;span class="gs"&gt;**Rule**&lt;/span&gt;: Controllers create context; services/clients forward &lt;span class="sb"&gt;`requestId`&lt;/span&gt;; logs include it at each layer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This memory layer is the difference between &lt;strong&gt;“new agent, same mistakes”&lt;/strong&gt; and &lt;strong&gt;“new agent, same team brain.”&lt;/strong&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%2F3hmq2wec5te94fpyjhfq.jpg" 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%2F3hmq2wec5te94fpyjhfq.jpg" alt="Change My Mind Meme" width="578" height="433"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How we used it in this repo (&lt;a href="https://github.com/jimzandueta/nestjs-http-server-boilerplate-codex-ai-assisted" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;Using this &lt;code&gt;.codex&lt;/code&gt; setup, we built a NestJS sample HTTP server with consistent architecture and quality gates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear boundaries (&lt;code&gt;common&lt;/code&gt;, &lt;code&gt;clients&lt;/code&gt;, &lt;code&gt;integrations&lt;/code&gt;, &lt;code&gt;modules&lt;/code&gt;, &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;errors&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;validated runtime config (&lt;code&gt;HOST&lt;/code&gt;, &lt;code&gt;PORT&lt;/code&gt;, &lt;code&gt;NODE_ENV&lt;/code&gt;, &lt;code&gt;LOG_LEVEL&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;structured logging&lt;/li&gt;
&lt;li&gt;reusable HTTP client with timeout/retry&lt;/li&gt;
&lt;li&gt;typed external API errors + global exception filter&lt;/li&gt;
&lt;li&gt;sample feature module (&lt;code&gt;posts&lt;/code&gt;) using JSONPlaceholder&lt;/li&gt;
&lt;li&gt;strict tests with &lt;strong&gt;100% coverage thresholds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;open-source docs (&lt;code&gt;LICENSE&lt;/code&gt;, &lt;code&gt;CONTRIBUTING&lt;/code&gt;, &lt;code&gt;CODE_OF_CONDUCT&lt;/code&gt;, &lt;code&gt;SECURITY&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this repo isn’t just “another Nest starter.”&lt;br&gt;&lt;br&gt;
It’s a &lt;strong&gt;working example of structured AI-assisted delivery&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  One important note
&lt;/h2&gt;

&lt;p&gt;Codex setups are usually &lt;strong&gt;stack-specific&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;.codex&lt;/code&gt; is tuned for a NestJS HTTP app. I maintain a different Codex baseline for Terraform/infrastructure because workflows, anti-patterns, and quality gates are different.&lt;/p&gt;

&lt;p&gt;Same core idea, different playbook.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;I’ll keep evolving this repo with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;richer feature module examples&lt;/li&gt;
&lt;li&gt;better integration patterns&lt;/li&gt;
&lt;li&gt;stricter review automations&lt;/li&gt;
&lt;li&gt;stack-specific Codex variants&lt;/li&gt;
&lt;li&gt;deeper AI-agent orchestration experiments using open-source tools like LangChain, Langfuse, and local models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team is in that “week one AI chaos” phase, start with structure first.&lt;br&gt;&lt;br&gt;
Model quality matters, but &lt;strong&gt;system quality matters more&lt;/strong&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%2Fq6uwzflh2jp2aw0om185.jpg" 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%2Fq6uwzflh2jp2aw0om185.jpg" alt="One Does Not Simply Meme" width="651" height="384"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  IMPORTANT: Here's a picture of my cat!
&lt;/h2&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%2Fnhike758eb3ofx0pshgu.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%2Fnhike758eb3ofx0pshgu.png" alt="Hi Chidi!" width="800" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>ai</category>
      <category>nestjs</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Allow Users to Upload and Download Data from AWS S3 Without Direct Access Using Pre-Signed URLs?</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Sat, 24 Dec 2022 05:49:34 +0000</pubDate>
      <link>https://dev.to/jimzandueta/how-to-allow-users-to-upload-and-download-data-from-aws-s3-without-direct-access-using-pre-signed-urls-h33</link>
      <guid>https://dev.to/jimzandueta/how-to-allow-users-to-upload-and-download-data-from-aws-s3-without-direct-access-using-pre-signed-urls-h33</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Amazon Simple Storage Service (AWS S3) is a popular cloud storage service that allows users to store and retrieve data from a variety of sources. While S3 buckets are set to public by default, there are times when it is necessary to restrict access to specific users or groups. Direct access to the bucket in these scenarios may pose security risks and limit flexibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Direct access to an AWS S3 bucket necessitates exposing the AWS access keys and secret keys to users, which can result in unauthorized access to the bucket and its contents. Furthermore, using this method, it may be impossible to control specific actions or limit access time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Pre-signed URLs provide a secure solution by allowing users to access specific objects in the bucket without exposing the AWS access keys and secret keys. Pre-signed URLs can also be used to restrict specific actions that users can take and to limit the amount of time that a user has access to the data.&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;To generate pre-signed URLs in TypeScript, you can use the &lt;code&gt;aws-sdk&lt;/code&gt; package, which provides a &lt;code&gt;getSignedUrl&lt;/code&gt; method that can be used to generate a pre-signed URL for a specific S3 object. Here's an example of how to use &lt;code&gt;getSignedUrl&lt;/code&gt; to generate a pre-signed URL for downloading an S3 object:&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="c1"&gt;// Generate a pre-signed URL for an S3 object&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generatePresignedUrl&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&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="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;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;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="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSignedUrlPromise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getObject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&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;url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-s3-bucket&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path/to/my/object.jpg&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;expiration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;// URL will expire in 60 seconds&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&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="nf"&gt;generatePresignedUrl&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="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Outputs a pre-signed URL that can be used to access the object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To generate a pre-signed URL for uploading an S3 object, you can use the putObject method in a similar way:&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;AWS&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;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the AWS SDK with your S3 bucket's credentials&lt;/span&gt;
&lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCESS_KEY_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SECRET_ACCESS_KEY&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Create an Express.js router&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Route for generating a pre-signed URL for uploading a file to S3&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/upload&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if the file was included in the request&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No file provided&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="c1"&gt;// Get the file and other parameters from the request body&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate a pre-signed URL for uploading the file to S3&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;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="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ACL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Set the ACL to "private"&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;putObject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Send the pre-signed URL back to the client&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send an error response if something went wrong&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Export the router&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="c1"&gt;// Example Usage&lt;/span&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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Function to upload a file to S3 using a pre-signed URL&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&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;// Set the headers for the request&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&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;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Make the PUT request to the pre-signed URL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Check the status of the response&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Outputs 201 if upload was successful&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&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;hello.txt&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;server_url&amp;gt;/upload&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="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Pre-signed URLs are a convenient and secure way for users to access data in an AWS S3 bucket without granting direct access. To use pre-signed URLs, use the AWS SDK to generate a URL for a specific object in the bucket. The generated URL can then be provided to the user, who can use it to access the object within the time period specified in the URL.&lt;/p&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html" rel="noopener noreferrer"&gt;AWS documentation on pre-signed URLs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property" rel="noopener noreferrer"&gt;AWS SDK for JavaScript documentation on getSignedUrl method&lt;/a&gt;: &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>How to connect to the same host with different SSH keys?</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Tue, 16 Aug 2022 14:12:00 +0000</pubDate>
      <link>https://dev.to/jimzandueta/setup-your-ssh-configuration-file-1m9e</link>
      <guid>https://dev.to/jimzandueta/setup-your-ssh-configuration-file-1m9e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Required Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/jimzandueta/generate-an-ssh-key-pair-2fcc"&gt;Generate an SSH key pair&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optional Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup your SSH configuration file
&lt;/h2&gt;

&lt;p&gt;When connecting to a remote server via SSH, we would use the command line to specify the remote user name, hostname, and port, as shown below:&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="nv"&gt;$ &lt;/span&gt;ssh tony@avengers.com &lt;span class="nt"&gt;-p&lt;/span&gt; 4321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can connect to the server using the same options as in the previous command by typing &lt;code&gt;ssh avengers&lt;/code&gt; after adding the following lines to your &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Host avengers
    HostName avengers.com
    User tony
    Port 4321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also specify which SSH key to use when connecting to a specific server in the configuration file by setting &lt;code&gt;IdentityFile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Host avengers
    HostName avengers.com
    User tony
    Port 4321
    IdentityFile ~/.ssh/id_rsa_avengers

Host marvel
    HostName marvel.com
    User tony
    Port 8080
    IdentityFile ~/.ssh/id_rsa_marvel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The use of &lt;code&gt;IdentityFile&lt;/code&gt; is especially useful when connecting to the same hostname with different SSH keys. A real-world example would be having multiple Github accounts, such as personal and work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Host github_personal
    HostName github.com
    IdentityFile ~/.ssh/github_personal

Host github_work
    HostName github.com
    IdentityFile ~/.ssh/github_work

IdentityFile ~/.ssh/id_rsa 
&lt;span class="c"&gt;#default ssh key to use for all other hosts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the configuration file above, we can clone repositories from your personal Github account with:&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="nv"&gt;$ &lt;/span&gt;git clone git@github_personal:tonystark/ultron.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that instead of &lt;code&gt;@github.com&lt;/code&gt; as the host name, we use &lt;code&gt;@github_personal&lt;/code&gt; as defined in the configuration file.&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating the configuration file:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your favorite terminal&lt;/li&gt;
&lt;li&gt;Create/Open the SSH configuration file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;-&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ssh.com/academy/ssh/config"&gt;git config file syntax&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>How to generate a secure SSH key pair?</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Tue, 16 Aug 2022 13:39:00 +0000</pubDate>
      <link>https://dev.to/jimzandueta/generate-an-ssh-key-pair-2fcc</link>
      <guid>https://dev.to/jimzandueta/generate-an-ssh-key-pair-2fcc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Required Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optional Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate an SSH key pair
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open your favorite terminal&lt;/li&gt;
&lt;li&gt;Run the ssh-keygen command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"tonystark@avengers.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;INFO: For increased security, the type flag &lt;code&gt;-t rsa&lt;/code&gt; and the bits &lt;code&gt;flag -b 4096&lt;/code&gt; are required. The comment flag &lt;code&gt;-C "tonystark@avengers.com"&lt;/code&gt; allows us to easily identify who owns the SSH key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Specify the key file name. Default is &lt;code&gt;id_rsa&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enter a passphrase *&lt;em&gt;optional&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kBWO3CPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8qdopzivgs2inx9xt69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kBWO3CPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8qdopzivgs2inx9xt69.png" alt="ssh-keygen-sample" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WARNING: If you already have a default SSH key ~/.ssh/id_rsa, &lt;strong&gt;DO NOT OVERWRITE IT&lt;/strong&gt;. If you are not careful, you will lose SSH access to your cloud servers and git platforms. Instead, give your new SSH key a new name, such as id_rsa_avengers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ssh.com/academy/ssh/keygen"&gt;ssh-keygen | SSH Tutorial &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>How to setup your global (and local) git profile?</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Tue, 16 Aug 2022 13:29:00 +0000</pubDate>
      <link>https://dev.to/jimzandueta/setup-your-git-profile-3i06</link>
      <guid>https://dev.to/jimzandueta/setup-your-git-profile-3i06</guid>
      <description>&lt;p&gt;&lt;strong&gt;Required Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optional Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup your git profile
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open your favorite terminal&lt;/li&gt;
&lt;li&gt;Set your git username
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Tony Stark"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set your git email
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email tonystark@avengers.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set your git editor *&lt;em&gt;optional&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; core.editor nano 
&lt;span class="c"&gt;# you can also use emacs (nostalgic) or vim (adventurous). &lt;/span&gt;
&lt;span class="c"&gt;# Vim is the default editor.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Review your settings
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="nt"&gt;--show-origin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The global flag --global will apply the configuration to the current operating system profile/user. If you want to apply your configuration at the local level, navigate to the project's root directory (where the.git folder is located) and run the git config commands without the global flag, or use the local flag --local to be more verbose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-config"&gt;git config | Atlassian Git Tutorial &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup"&gt;Git - First-Time Git Setup &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>macOS: Moving Your Notification to a Different Display</title>
      <dc:creator>Jim Zandueta</dc:creator>
      <pubDate>Fri, 08 May 2020 09:26:07 +0000</pubDate>
      <link>https://dev.to/jimzandueta/macos-moving-your-notification-to-a-different-display-17bi</link>
      <guid>https://dev.to/jimzandueta/macos-moving-your-notification-to-a-different-display-17bi</guid>
      <description>&lt;p&gt;My current display configuration includes my Macbook to the right and a 27" BenQ Monitor in front of me. The larger screen houses all of my work-related applications, while the smaller one houses my calendar, calculator, and Spotify.&lt;/p&gt;

&lt;p&gt;For as long as I can remember, all of my notifications would appear on the screen of my Macbook. I never thought about it before, but after getting in trouble for missing several critical Slack messages, I realized I needed to do something about it.&lt;/p&gt;

&lt;p&gt;I did some research and here's what I discovered:&lt;/p&gt;

&lt;p&gt;Setting up multiple displays on macOS is a simple process. Simply navigate to System Preferences &amp;gt; Displays &amp;gt; Arrangement and drag the screens to the desired location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ip3QzgpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sopy9q2td40hfmviem9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ip3QzgpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sopy9q2td40hfmviem9z.png" alt="Display Arrangement" width="780" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the white bar on top of the small screen icon. That, believe it or not, is the &lt;strong&gt;Menu Bar&lt;/strong&gt;. It handles a lot of things, including notifications! &lt;/p&gt;

&lt;p&gt;I simply dragged it to the screen icon on the left, and after a few flashes, my 27" display became my main monitor (displaying notifications), and my Macbook became the extension monitor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---_Wocll2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ljonxhabfks995jzll4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---_Wocll2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ljonxhabfks995jzll4x.png" alt="Alt Text" width="780" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, moving the menu bar icon will &lt;strong&gt;rearrange&lt;/strong&gt; your desktop. So, if you have your applications organized across multiple desktops, consider cleaning them up first.&lt;/p&gt;

&lt;p&gt;I hope this was helpful. Happy coding!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>macos</category>
      <category>productivity</category>
      <category>tips</category>
    </item>
  </channel>
</rss>
