<?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: Achraf Ben Hamou</title>
    <description>The latest articles on DEV Community by Achraf Ben Hamou (@achraf_benhamou).</description>
    <link>https://dev.to/achraf_benhamou</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%2F1688780%2F209a258f-77b8-4661-a078-4c18141b0d5d.jpg</url>
      <title>DEV Community: Achraf Ben Hamou</title>
      <link>https://dev.to/achraf_benhamou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/achraf_benhamou"/>
    <language>en</language>
    <item>
      <title>How to Rename a Django Application Safely</title>
      <dc:creator>Achraf Ben Hamou</dc:creator>
      <pubDate>Mon, 24 Aug 2026 15:14:47 +0000</pubDate>
      <link>https://dev.to/achraf_benhamou/how-to-rename-a-django-application-safely-3mpo</link>
      <guid>https://dev.to/achraf_benhamou/how-to-rename-a-django-application-safely-3mpo</guid>
      <description>&lt;p&gt;Renaming a Django application looks simple at first: rename the directory and update the imports.&lt;/p&gt;

&lt;p&gt;However, if the application already has migrations and data in production, renaming it can be tricky. Django uses the application name in several places, including migration history, content types, permissions, and database table names.&lt;/p&gt;

&lt;p&gt;In this article, we'll look at a safe way to rename a Django app, especially when using PostgreSQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  The important distinction: package name vs. app label
&lt;/h2&gt;

&lt;p&gt;Before changing anything, it's important to understand that Django has several concepts related to an app's name.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myproject/
├── manage.py
├── config/
└── foo/
    ├── __init__.py
    ├── apps.py
    ├── models.py
    └── migrations/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python package is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the application configuration might be:&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;django.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FooConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;default_auto_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.db.models.BigAutoField&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Django also has an &lt;strong&gt;app label&lt;/strong&gt;, which is used internally by migrations and content types.&lt;/p&gt;

&lt;p&gt;By default, the app label is derived from the last part of &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This distinction is important because changing the Python package name does not necessarily mean that you need to change the Django app label.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 1: Rename only the Python package
&lt;/h1&gt;

&lt;p&gt;If your main goal is to rename:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while keeping the existing Django application identity, this is usually the safest approach.&lt;/p&gt;

&lt;p&gt;Rename the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update &lt;code&gt;apps.py&lt;/code&gt;:&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;django.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BarConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;default_auto_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.db.models.BigAutoField&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part here is:&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="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By keeping the old label, Django continues to recognize the application as the same application from the migration/content-type perspective.&lt;/p&gt;

&lt;p&gt;Then update your imports and references:&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;foo.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MyModel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&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;bar.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MyModel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INSTALLED_APPS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apps.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;urls.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;views.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;admin.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forms.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;signals.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Celery configuration&lt;/li&gt;
&lt;li&gt;tests&lt;/li&gt;
&lt;li&gt;templates&lt;/li&gt;
&lt;li&gt;static files&lt;/li&gt;
&lt;li&gt;management commands&lt;/li&gt;
&lt;li&gt;any imports elsewhere in the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&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="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bar.apps.BarConfig&lt;/span&gt;&lt;span class="sh"&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;With this approach, &lt;strong&gt;you generally don't need to manually modify &lt;code&gt;django_content_type&lt;/code&gt; or &lt;code&gt;django_migrations&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is usually the preferred solution when you simply want to rename the Python package.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 2: Change the Django app label as well
&lt;/h1&gt;

&lt;p&gt;Sometimes you don't only want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also want Django to consider the application itself to have changed from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more complicated.&lt;/p&gt;

&lt;p&gt;For example, Django's default database table name for:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the app label changes to &lt;code&gt;bar&lt;/code&gt;, Django may expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You therefore need to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Migration history&lt;/li&gt;
&lt;li&gt;Content types&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Database tables&lt;/li&gt;
&lt;li&gt;Foreign keys and many-to-many tables&lt;/li&gt;
&lt;li&gt;Generic relations&lt;/li&gt;
&lt;li&gt;Existing migration files&lt;/li&gt;
&lt;li&gt;Third-party packages referencing the old app label&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Don't blindly edit &lt;code&gt;django_migrations&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A common solution found online is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;django_migrations&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'bar'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this can be useful in very specific migration-management procedures, &lt;strong&gt;it should not be treated as the standard first step for renaming a Django application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;django_migrations&lt;/code&gt; table represents Django's migration history. Its contents must remain consistent with the migration files that Django loads.&lt;/p&gt;

&lt;p&gt;Simply changing this table without carefully changing the migration graph can result in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrations being considered unapplied;&lt;/li&gt;
&lt;li&gt;migrations being applied twice;&lt;/li&gt;
&lt;li&gt;broken migration dependencies;&lt;/li&gt;
&lt;li&gt;inconsistent production databases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, changing the database record alone does not rename a Django application.&lt;/p&gt;




&lt;h1&gt;
  
  
  What about &lt;code&gt;django_content_type&lt;/code&gt;?
&lt;/h1&gt;

&lt;p&gt;Django stores content types using the application's &lt;code&gt;app_label&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app_label = foo
model = customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the app label really changes to &lt;code&gt;bar&lt;/code&gt;, existing content types may need to be updated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;django_content_type&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;app_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'bar'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;app_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this should be done &lt;strong&gt;only if you are intentionally changing the Django app label&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should also consider the permissions associated with these content types.&lt;/p&gt;

&lt;p&gt;Before doing this on production, always take a database backup and test the complete migration on a copy of the production database.&lt;/p&gt;




&lt;h1&gt;
  
  
  Renaming database tables
&lt;/h1&gt;

&lt;p&gt;If your models use Django's default table names, changing the app label can also change the table names Django expects.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;could become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With PostgreSQL, a table can be renamed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;foo_customer&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;bar_customer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For several tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;foo_customer&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;bar_customer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;foo_order&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;bar_order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, &lt;strong&gt;don't assume that every model uses &lt;code&gt;&amp;lt;app_label&amp;gt;_&amp;lt;model_name&amp;gt;&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A model can define its own database table:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, renaming the Django app does not require renaming the &lt;code&gt;customers&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;So before writing SQL, inspect your actual database schema.&lt;/p&gt;




&lt;h1&gt;
  
  
  A safer migration approach
&lt;/h1&gt;

&lt;p&gt;If you really need to change the app label and database table names, database changes should preferably be represented explicitly in Django migrations.&lt;/p&gt;

&lt;p&gt;For example, you can use &lt;code&gt;SeparateDatabaseAndState&lt;/code&gt; when the database operation and Django's model state need to be handled separately.&lt;/p&gt;

&lt;p&gt;A simplified example:&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;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;# Your actual migration dependency
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SeparateDatabaseAndState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;database_operations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RunSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALTER TABLE foo_customer RENAME TO bar_customer;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;reverse_sql&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;ALTER TABLE bar_customer &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RENAME TO foo_customer;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state_operations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="c1"&gt;# Django state operations go here.
&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;The exact migration depends heavily on the existing project and migration history, so this should not be copied blindly into a production project.&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating an empty migration
&lt;/h1&gt;

&lt;p&gt;The original article contains a small syntax error here.&lt;/p&gt;

&lt;p&gt;The correct command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py makemigrations bar &lt;span class="nt"&gt;--empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./manage.py makemigrations bar &lt;span class="nt"&gt;--empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;--empty&lt;/code&gt; is one option.&lt;/p&gt;

&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;makemigrations bar - empty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Be careful with migration dependencies
&lt;/h1&gt;

&lt;p&gt;Another important issue is migration dependencies.&lt;/p&gt;

&lt;p&gt;For example:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;bar&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;0020_auto_20230530_1535&lt;/span&gt;&lt;span class="sh"&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;This is only valid if Django actually has an application with the label &lt;code&gt;bar&lt;/code&gt; and a migration named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0020_auto_20230530_1535
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should never simply replace:&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="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&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;0020_...&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;p&gt;with:&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="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bar&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;0020_...&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;p&gt;without understanding how Django's migration graph will change.&lt;/p&gt;

&lt;p&gt;Existing migrations also contain dependencies such as:&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="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;foo&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;0019_previous_migration&lt;/span&gt;&lt;span class="sh"&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;If you rename the application label, these historical dependencies need to be handled consistently.&lt;/p&gt;




&lt;h1&gt;
  
  
  A practical recommendation
&lt;/h1&gt;

&lt;p&gt;For most projects, I recommend the following strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you only want to rename the Python package
&lt;/h3&gt;

&lt;p&gt;Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foo/ → bar/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and keep:&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="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in &lt;code&gt;AppConfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then update imports and application references.&lt;/p&gt;

&lt;p&gt;This minimizes the risk of breaking your migration history.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you absolutely need the Django app label to change
&lt;/h3&gt;

&lt;p&gt;Treat it as a &lt;strong&gt;database and migration migration&lt;/strong&gt;, not simply as a folder rename.&lt;/p&gt;

&lt;p&gt;Before making the change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back up the database.&lt;/li&gt;
&lt;li&gt;Test the operation on a copy of production.&lt;/li&gt;
&lt;li&gt;Inspect all existing migrations.&lt;/li&gt;
&lt;li&gt;Identify all database tables belonging to the application.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;django_content_type&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check permissions.&lt;/li&gt;
&lt;li&gt;Check generic foreign keys.&lt;/li&gt;
&lt;li&gt;Check third-party applications that reference the old app label.&lt;/li&gt;
&lt;li&gt;Plan the migration graph carefully.&lt;/li&gt;
&lt;li&gt;Deploy the database changes together with the corresponding application code.&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Renaming a Django application is not just a matter of renaming a directory.&lt;/p&gt;

&lt;p&gt;The safest solution depends on what you actually want to rename:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Python package
      ↓
foo/ → bar/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is relatively straightforward.&lt;/p&gt;

&lt;p&gt;But changing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Django app label
      ↓
foo → bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can affect migrations, content types, permissions and database tables.&lt;/p&gt;

&lt;p&gt;Therefore, before manually modifying tables such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django_migrations
django_content_type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;make sure you understand which Django identity you are changing.&lt;/p&gt;

&lt;p&gt;For an existing production application, &lt;strong&gt;keeping the original app label while renaming the Python package is often the least risky solution&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And, as always, test the complete procedure against a copy of your production database before deploying it.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>database</category>
      <category>django</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>RPC in Python: gRPC, Protocol Buffers, and REST vs GraphQL</title>
      <dc:creator>Achraf Ben Hamou</dc:creator>
      <pubDate>Mon, 24 Aug 2026 15:11:55 +0000</pubDate>
      <link>https://dev.to/achraf_benhamou/rpc-in-python-grpc-protocol-buffers-and-rest-vs-graphql-2klf</link>
      <guid>https://dev.to/achraf_benhamou/rpc-in-python-grpc-protocol-buffers-and-rest-vs-graphql-2klf</guid>
      <description>&lt;p&gt;When building backend systems, choosing the right communication protocol can have a significant impact on performance, maintainability, and scalability.&lt;/p&gt;

&lt;p&gt;REST is still the most common choice for public APIs, while GraphQL has become popular when clients need flexible data fetching.&lt;/p&gt;

&lt;p&gt;But when services need to communicate efficiently with each other, another approach becomes particularly interesting: &lt;strong&gt;RPC&lt;/strong&gt;, and more specifically &lt;strong&gt;gRPC&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What RPC actually means&lt;/li&gt;
&lt;li&gt;How gRPC works&lt;/li&gt;
&lt;li&gt;What Protocol Buffers are&lt;/li&gt;
&lt;li&gt;How to build a gRPC service with Python&lt;/li&gt;
&lt;li&gt;gRPC vs REST vs GraphQL&lt;/li&gt;
&lt;li&gt;When you should use each approach&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. What is RPC?
&lt;/h2&gt;

&lt;p&gt;RPC stands for &lt;strong&gt;Remote Procedure Call&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea is simple: instead of thinking in terms of HTTP resources, we think in terms of &lt;strong&gt;calling a method on a remote service&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, with a traditional REST API, we might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users/42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With RPC, the mental model is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UserService.GetUser(42)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client calls a method, and the server executes that method remotely.&lt;/p&gt;

&lt;p&gt;Of course, the method isn't actually local. The RPC framework takes care of the communication between the client and the server.&lt;/p&gt;

&lt;p&gt;gRPC follows this model and allows a client application to call methods on another machine almost like calling local methods.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. What is gRPC?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;gRPC&lt;/strong&gt; is an RPC framework originally developed at Google.&lt;/p&gt;

&lt;p&gt;It allows services to communicate using strongly defined service contracts.&lt;/p&gt;

&lt;p&gt;A typical gRPC architecture looks 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;┌─────────────┐
│   Client    │
│   Python    │
└──────┬──────┘
       │
       │ gRPC
       │
       ▼
┌─────────────────┐
│      Server     │
│     Python      │
└─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of manually designing HTTP endpoints and JSON payloads, we define the service contract in a &lt;code&gt;.proto&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;gRPC can use Protocol Buffers as both the interface definition language and the message format.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Protocol Buffers: the contract
&lt;/h1&gt;

&lt;p&gt;Protocol Buffers, commonly called &lt;strong&gt;Protobuf&lt;/strong&gt;, are used to define the messages and services exchanged between applications.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetUserRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&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="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file describes our API contract.&lt;/p&gt;

&lt;p&gt;We have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;UserService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;GetUser&lt;/code&gt; RPC method&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;GetUserRequest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;User&lt;/code&gt; response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that the contract is language-independent.&lt;/p&gt;

&lt;p&gt;A Python service could communicate with a Go, Java, C#, or Node.js service using the same &lt;code&gt;.proto&lt;/code&gt; definition.&lt;/p&gt;

&lt;p&gt;gRPC officially supports multiple languages, including Python, Go, Java, C#, Node.js, Ruby, PHP and others.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Building a gRPC API with Python
&lt;/h1&gt;

&lt;p&gt;Let's build a very small service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;grpcio grpcio-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official Python gRPC documentation uses &lt;code&gt;grpcio&lt;/code&gt; for the runtime and &lt;code&gt;grpcio-tools&lt;/code&gt; for generating Python code from &lt;code&gt;.proto&lt;/code&gt; definitions.&lt;/p&gt;

&lt;p&gt;Our project could 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;grpc-demo/
│
├── proto/
│   └── user.proto
│
├── server.py
├── client.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  5. Define the service
&lt;/h1&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proto/user.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetUserRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&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="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have now defined our API contract.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Generate Python code
&lt;/h1&gt;

&lt;p&gt;We can generate the Python client/server code with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; grpc_tools.protoc &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;./proto &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--python_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--grpc_python_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ./proto/user.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates files similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_pb2.py
user_pb2_grpc.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first contains the generated Protocol Buffer message classes, while the second contains the gRPC-specific client and server code.&lt;/p&gt;

&lt;p&gt;You normally &lt;strong&gt;don't edit these generated files manually&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Implement the Python server
&lt;/h1&gt;

&lt;p&gt;Now we can implement the service.&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;concurrent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;futures&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_pb2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_pb2_grpc&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_pb2_grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserServiceServicer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&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;user_pb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;User&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;request&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="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Doe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;john@example.com&lt;/span&gt;&lt;span class="sh"&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;def&lt;/span&gt; &lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;user_pb2_grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_UserServiceServicer_to_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_insecure_port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[::]:50051&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gRPC server running on port 50051&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_termination&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&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="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated &lt;code&gt;UserServiceServicer&lt;/code&gt; provides the interface that our Python implementation extends.&lt;/p&gt;

&lt;p&gt;The official gRPC Python examples follow the same general pattern: create a server, register the generated servicer, bind a port, and start the server.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Create the Python client
&lt;/h1&gt;

&lt;p&gt;Now let's consume the service.&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;import&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_pb2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user_pb2_grpc&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insecure_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost:50051&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_pb2_grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UserServiceStub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;user_pb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetUserRequest&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="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is:&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the developer's perspective, it looks very similar to calling a normal Python method.&lt;/p&gt;

&lt;p&gt;Behind the scenes, however, the request is sent to another process or machine.&lt;/p&gt;

&lt;p&gt;The gRPC Python API generates a client stub from the &lt;code&gt;.proto&lt;/code&gt; service definition.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. gRPC supports streaming
&lt;/h1&gt;

&lt;p&gt;One of the features that makes gRPC particularly interesting for backend systems is &lt;strong&gt;streaming&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are four RPC patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  Unary RPC
&lt;/h3&gt;

&lt;p&gt;One request → one response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client ── Request ──&amp;gt; Server
Client &amp;lt;── Response ─ Server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Server streaming
&lt;/h3&gt;

&lt;p&gt;One request → multiple responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ListUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListUsersRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful when a server needs to continuously send data to a client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client streaming
&lt;/h3&gt;

&lt;p&gt;Multiple requests → one response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;UploadUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UploadSummary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful for sending a stream of data to the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bidirectional streaming
&lt;/h3&gt;

&lt;p&gt;Multiple requests ↔ multiple responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both sides can continuously exchange messages.&lt;/p&gt;

&lt;p&gt;gRPC's Python documentation demonstrates all four communication patterns.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. gRPC vs REST
&lt;/h1&gt;

&lt;p&gt;So when should we use gRPC instead of REST?&lt;/p&gt;

&lt;p&gt;Let's compare them.&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;gRPC&lt;/th&gt;
&lt;th&gt;REST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;HTTP/2&lt;/td&gt;
&lt;td&gt;Usually HTTP/1.1 or HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data format&lt;/td&gt;
&lt;td&gt;Protobuf&lt;/td&gt;
&lt;td&gt;Usually JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.proto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAPI often used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code generation&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;More limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser support&lt;/td&gt;
&lt;td&gt;More complex&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human-readable payload&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microservices&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public APIs&lt;/td&gt;
&lt;td&gt;Less common&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;REST is extremely convenient for public APIs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users/42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is easy to understand and easy to consume using:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A JSON response is also easy to inspect:&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="mi"&gt;42&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;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@example.com"&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;This simplicity is one of REST's biggest advantages.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. gRPC vs GraphQL
&lt;/h1&gt;

&lt;p&gt;GraphQL solves a different problem.&lt;/p&gt;

&lt;p&gt;Instead of exposing multiple REST endpoints, GraphQL usually exposes a schema through which clients can request exactly the fields they need.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&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="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;email&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;This is extremely useful when different clients have different data requirements.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mobile App
     │
     ▼
 GraphQL
     │
 ┌───┼─────────┐
 ▼   ▼         ▼
User Order   Product
Service      Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GraphQL is particularly attractive for frontend-facing APIs.&lt;/p&gt;

&lt;p&gt;gRPC, on the other hand, is often a better fit when backend services need a strongly typed and efficient service-to-service contract.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Which one should you choose?
&lt;/h1&gt;

&lt;p&gt;There isn't a universal winner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose REST when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You are building a public API&lt;/li&gt;
&lt;li&gt;Browser compatibility matters&lt;/li&gt;
&lt;li&gt;Simplicity is important&lt;/li&gt;
&lt;li&gt;Human-readable JSON is useful&lt;/li&gt;
&lt;li&gt;You want a familiar HTTP interface&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose GraphQL when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clients need different subsets of data&lt;/li&gt;
&lt;li&gt;You have complex frontend requirements&lt;/li&gt;
&lt;li&gt;You want clients to control the shape of responses&lt;/li&gt;
&lt;li&gt;You are building a frontend-facing aggregation layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose gRPC when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You are building microservices&lt;/li&gt;
&lt;li&gt;You control both client and server&lt;/li&gt;
&lt;li&gt;Strong contracts are important&lt;/li&gt;
&lt;li&gt;Performance matters&lt;/li&gt;
&lt;li&gt;You need streaming&lt;/li&gt;
&lt;li&gt;You have multiple programming languages in your architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common production architecture can even combine them.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Internet
                    │
                    ▼
              ┌───────────┐
              │ REST/API  │
              │ Gateway   │
              └─────┬─────┘
                    │
              ┌─────▼─────┐
              │ User      │
              │ Service   │
              └─────┬─────┘
                    │ gRPC
          ┌─────────┼──────────┐
          ▼         ▼          ▼
       Orders    Payments   Products
       Service    Service    Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST can be the external interface while gRPC handles internal service-to-service communication.&lt;/p&gt;




&lt;h1&gt;
  
  
  13. The biggest advantage: a contract-first approach
&lt;/h1&gt;

&lt;p&gt;For me, one of the strongest advantages of gRPC isn't simply performance.&lt;/p&gt;

&lt;p&gt;It's the &lt;strong&gt;contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the API contract is explicit.&lt;/p&gt;

&lt;p&gt;From the same definition, tooling can generate client/server code.&lt;/p&gt;

&lt;p&gt;This reduces a class of problems that can happen when API contracts are maintained manually.&lt;/p&gt;

&lt;p&gt;Protocol Buffers also provide generated Python classes rather than requiring developers to manually serialize and deserialize every message.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. But gRPC isn't always the right choice
&lt;/h1&gt;

&lt;p&gt;There are trade-offs.&lt;/p&gt;

&lt;p&gt;gRPC can be more complex than a simple REST API.&lt;/p&gt;

&lt;p&gt;For example, debugging this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  │
  ▼
gRPC
  │
HTTP/2
  │
Protobuf
  │
Server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be less convenient than inspecting a JSON REST request.&lt;/p&gt;

&lt;p&gt;Also, browser clients typically require additional considerations such as gRPC-Web rather than directly using the standard gRPC protocol.&lt;/p&gt;

&lt;p&gt;So choosing gRPC simply because it is "faster" is not enough.&lt;/p&gt;

&lt;p&gt;The architecture and communication requirements should drive the decision.&lt;/p&gt;




&lt;h1&gt;
  
  
  15. Final thoughts
&lt;/h1&gt;

&lt;p&gt;As a Python Backend Developer, understanding gRPC is valuable because modern distributed systems often involve multiple services communicating with each other.&lt;/p&gt;

&lt;p&gt;REST remains an excellent choice for many APIs.&lt;/p&gt;

&lt;p&gt;GraphQL is powerful when clients need flexible access to data.&lt;/p&gt;

&lt;p&gt;gRPC becomes particularly interesting for internal service-to-service communication where strong contracts, generated code, and streaming are important.&lt;/p&gt;

&lt;p&gt;The key lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't choose a protocol because it is popular. Choose it because it fits the communication problem you're solving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a Python backend engineer, knowing &lt;strong&gt;REST + GraphQL + gRPC&lt;/strong&gt; gives you a much broader understanding of API architecture than knowing only how to build CRUD endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://grpc.io/docs/languages/python/" rel="noopener noreferrer"&gt;gRPC Python documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grpc.io/docs/languages/python/quickstart/" rel="noopener noreferrer"&gt;gRPC Python Quick Start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grpc.io/docs/languages/python/basics/" rel="noopener noreferrer"&gt;gRPC Python Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protobuf.dev/reference/python/python-generated/" rel="noopener noreferrer"&gt;Protocol Buffers — Python Generated Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;I'm a Python Backend Developer interested in distributed systems, APIs, microservices, databases, and backend architecture.&lt;/p&gt;

&lt;p&gt;I'm documenting what I learn while building backend systems and exploring technologies such as Python, FastAPI, PostgreSQL, Docker, gRPC, and cloud infrastructure.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>python</category>
    </item>
  </channel>
</rss>
