<?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: Lucas Duarte</title>
    <description>The latest articles on DEV Community by Lucas Duarte (@lwwcas).</description>
    <link>https://dev.to/lwwcas</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%2F3636127%2Fd83671df-6458-4ecf-80d3-dc4fcf01a58c.png</url>
      <title>DEV Community: Lucas Duarte</title>
      <link>https://dev.to/lwwcas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lwwcas"/>
    <language>en</language>
    <item>
      <title>Stop Calling External APIs for Country Data in Laravel — Seed It Into Your Database Instead</title>
      <dc:creator>Lucas Duarte</dc:creator>
      <pubDate>Tue, 14 Jul 2026 21:27:20 +0000</pubDate>
      <link>https://dev.to/lwwcas/stop-calling-external-apis-for-country-data-in-laravel-seed-it-into-your-database-instead-k7a</link>
      <guid>https://dev.to/lwwcas/stop-calling-external-apis-for-country-data-in-laravel-seed-it-into-your-database-instead-k7a</guid>
      <description>&lt;p&gt;If you've ever needed a list of countries in a Laravel app — for a signup form, a shipping calculator, a "select your country" dropdown — you've probably reached for a remote API.&lt;/p&gt;

&lt;p&gt;And it usually looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://some-country-api.example/countries'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, until it doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API is down and now your signup form is broken&lt;/li&gt;
&lt;li&gt;You hit a rate limit during a traffic spike&lt;/li&gt;
&lt;li&gt;You need country names in Portuguese and Spanish, and the API only returns English&lt;/li&gt;
&lt;li&gt;Your CI pipeline is making real HTTP calls in tests (or worse, mocking them badly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are exotic edge cases — they're just what happens when a core piece of your app's data lives on someone else's server.&lt;/p&gt;

&lt;h2&gt;
  
  
  A different approach: put the data in your own database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/lwwcas/laravel-countries" rel="noopener noreferrer"&gt;Laravel Countries&lt;/a&gt; takes a simpler approach: instead of calling an API every time you need country data, you seed it &lt;strong&gt;once&lt;/strong&gt;, into &lt;strong&gt;your own database&lt;/strong&gt;, and query it forever after like any other Eloquent model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Laravel Countries&lt;/th&gt;
&lt;th&gt;Typical remote country API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data lives in&lt;/td&gt;
&lt;td&gt;Your own database&lt;/td&gt;
&lt;td&gt;A remote server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works offline / in CI&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limits&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Often yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translations included&lt;/td&gt;
&lt;td&gt;9 languages, out of the box&lt;/td&gt;
&lt;td&gt;Usually English only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query with Eloquent&lt;/td&gt;
&lt;td&gt;✅ — it's just a model&lt;/td&gt;
&lt;td&gt;Usually a custom HTTP client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra network calls per request&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Installing it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require lwwcas/laravel-countries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan w-countries:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single command runs the migrations and seeds countries, regions, and translations. No config files to hand-edit first, no JSON to download and parse yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying countries like any other model
&lt;/h2&gt;

&lt;p&gt;Once it's seeded, countries behave exactly like the rest of your app's data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Lwwcas\LaravelCountries\Models\Country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whereIso&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'BR'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whereIsoAlpha3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'BRA'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whereSlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'brasil'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Need a country's name in a specific language? Translations are first-class, not bolted on — the package ships with &lt;strong&gt;9 languages out of the box&lt;/strong&gt;: Arabic, Dutch, English, German, Italian, Portuguese, Russian, Spanish, and Turkish, with more added as the community contributes reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production and CI, without the awkward prompts
&lt;/h2&gt;

&lt;p&gt;Laravel normally asks for confirmation before running seeders in production. Laravel Countries handles that automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan w-countries:install &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if your migrations already ran during deploy and you only need the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan w-countries:seed &lt;span class="nt"&gt;--languages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en,pt,es
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both commands are safe to drop straight into a deploy script — no hidden prompts, no hanging processes waiting for input that will never come.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more than it sounds
&lt;/h2&gt;

&lt;p&gt;Country data feels like a small thing, until it's the piece that makes your signup flow flaky in production, or the reason your test suite needs network access to run. Moving it into your own database removes an entire category of "why did this fail today" — the data is just... there, versioned alongside the rest of your schema, tested in CI the same way as everything else.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require lwwcas/laravel-countries
php artisan w-countries:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it saves you the trouble of building this yourself, a star on the repo genuinely helps other developers find it:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/lwwcas/laravel-countries" rel="noopener noreferrer"&gt;github.com/lwwcas/laravel-countries&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Full docs, including production deployment and manual seeder classes, are here: &lt;strong&gt;&lt;a href="https://lwwcas.github.io/laravel-countries/" rel="noopener noreferrer"&gt;lwwcas.github.io/laravel-countries&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Missing a language, or found a translation that needs fixing? Contributions are very welcome — check &lt;a href="https://github.com/lwwcas/laravel-countries/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt; for how translation reviews work.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>opensource</category>
      <category>countries</category>
    </item>
  </channel>
</rss>
