<?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: Mario García</title>
    <description>The latest articles on DEV Community by Mario García (@mattdark).</description>
    <link>https://dev.to/mattdark</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%2F126701%2F0c3e8efe-0b8f-440f-a3f8-f9f5f738ab31.jpg</url>
      <title>DEV Community: Mario García</title>
      <link>https://dev.to/mattdark</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mattdark"/>
    <language>en</language>
    <item>
      <title>Anatomy of a RAG System Architecture</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Tue, 17 Mar 2026 21:39:59 +0000</pubDate>
      <link>https://dev.to/letstalkoss/anatomy-of-a-rag-system-architecture-1l96</link>
      <guid>https://dev.to/letstalkoss/anatomy-of-a-rag-system-architecture-1l96</guid>
      <description>&lt;p&gt;For deploying a RAG System Architecture, consider that in a production environment requirements may vary when choosing a vector database, amount of data to be ingested, models used for creating embeddings, and architecture design when choosing a cloud platform. A RAG system can be built from scratch or implemented using solutions that already have the necessary components.&lt;/p&gt;

&lt;p&gt;Following best practices is also critical when designing the system, to avoid common issues like hallucinations, or data exposure. Also consider that the model can be changed through the time, and using layer architecture may be helpful for future changes or updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a RAG System?
&lt;/h2&gt;

&lt;p&gt;Presenting false or inaccurate information when not knowing the response, using unvalidated sources, or giving outdated data are some of the challenges for LLMs. How to solve this, and improve the knowledge base? Retrieval-Augmented Generation Architecture (RAG) is the approach for solving this problem.&lt;/p&gt;

&lt;p&gt;RAG uses some methods, and tools for acquiring knowledge from additional data sources. This information is then converted into a format that LLMs can understand, using different database technologies for saving this information, providing advanced search capabilities for data retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG System Architecture Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Sources
&lt;/h3&gt;

&lt;p&gt;RAG systems can have a variety of data sources from which to learn or acquire information. Those include but are not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documents in different formats (PDF, Word, TXT, Markdown)&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Audio or transcriptions&lt;/li&gt;
&lt;li&gt;Datasets (CSV, JSON)&lt;/li&gt;
&lt;li&gt;Application Programming Interfaces (APIs)&lt;/li&gt;
&lt;li&gt;Relational databases (SQL)&lt;/li&gt;
&lt;li&gt;NoSQL databases&lt;/li&gt;
&lt;li&gt;Wikis or knowledge base&lt;/li&gt;
&lt;li&gt;Web content&lt;/li&gt;
&lt;li&gt;Internal files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data ingestion in a RAG system goes through a process where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content is extracted from the data sources&lt;/li&gt;
&lt;li&gt;Divided into chunks&lt;/li&gt;
&lt;li&gt;Converted to embeddings&lt;/li&gt;
&lt;li&gt;Stored in a vector database&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Embedding Model
&lt;/h3&gt;

&lt;p&gt;An embedding model is a machine learning model used for transforming data sources into vectors. Also known as embeddings, and are a series of float values representing the meaning of the data source. The model determines not only how embeddings are created but also the size of the vector, and is mainly provided by LLMs.&lt;/p&gt;

&lt;p&gt;For apps built with Python use Gemini or OpenAI models through their official SDKs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI Python AI library → &lt;a href="https://github.com/openai/openai-python" rel="noopener noreferrer"&gt;github.com/openai/openai-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google GenAI Python SDK → &lt;a href="https://github.com/googleapis/python-genai" rel="noopener noreferrer"&gt;github.com/googleapis/python-genai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another option is to use &lt;a href="https://github.com/langchain-ai/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;, a framework for building agents, and LLM-powered applications. Requests to those models are sent through this framework.&lt;/p&gt;

&lt;p&gt;LLMs can also be executed locally without sending data to the cloud, and is possible by using Open Source tools like &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. Models are downloaded from its own &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;catalog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A similar library is &lt;a href="https://sbert.net/" rel="noopener noreferrer"&gt;Sentence Transformers&lt;/a&gt;. It has access to the &lt;a href="https://huggingface.co/models" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt; catalog from which models are downloaded to use locally, generally via &lt;a href="https://pytorch.org/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;, without having to create an API key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vector Database
&lt;/h3&gt;

&lt;p&gt;In a vector database, information is stored in vectors, better known as embeddings, containing numerical values that represent the meaning of the stored data. Query results are determined by the values in the vectors, returning nearest vectors.&lt;/p&gt;

&lt;p&gt;Having this text: &lt;code&gt;"Open source software is transforming the technology ecosystem.”&lt;/code&gt;, when converting the value to an embedding, will look like this: &lt;code&gt;[-0.007894928, 0.0010742444, -0.03274113, -0.066677086, 0.004894953, 0.013967406, 0.0068471087, 0.009695383, 0.020278132, 0.019470839]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Which vector database to use? Depends on the requirements of the project, experience using vector databases, or how the RAG system is being implemented. If the system is being built from the ground up, the best option is the one that serves the needs of the project, in terms of requirements, and budget. Choose solutions like &lt;a href="https://ragflow.io/" rel="noopener noreferrer"&gt;RAGFlow&lt;/a&gt; that already provide all the necessary components to implement the system when time plays a key role.&lt;/p&gt;

&lt;p&gt;A few options to consider when choosing a vector database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;&lt;strong&gt;pgvector&lt;/strong&gt;&lt;/a&gt;: An extension for PostgreSQL that adds the vector datatype, and search capabilities found in vector databases. The extension can be installed manually by compiling the source code from the official repository, or deployed via Docker or Kubernetes, as well as on cloud platforms that support PostgreSQL like Amazon Web Services (AWS), Google Cloud Platform (GCP) or Supabase.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pinecone.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pinecone&lt;/strong&gt;&lt;/a&gt;: A vector database designed for scalability, and to be used in production. Run as a managed service in AWS, GCP, and Azure. Every request to Pinecone is sent through an API that routes it to a control plane or a data plane, for managing projects, and indexes; reading, and writing data respectively.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://weaviate.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Weaviate&lt;/strong&gt;&lt;/a&gt;: An Open Source vector database. Can be configured via the Weaviate Cloud, using a cloud provider like GCP, to deploy an instance of the database. It uses an API key for authentication, and sending requests. Docker image is available for running locally.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elastic/elasticsearch" rel="noopener noreferrer"&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt;&lt;/a&gt;: Distributed, and analytics search engine, scalable data store, vector database focused on speed, production-ready, and scalability support. RAGFlow component, used as its default vector database, while testing, and improving Infinity. Can be used in production environments through Elastic Cloud or configured manually in the cloud platform of choice. A Docker image is available to use it locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges in RAG System Architecture Deployment
&lt;/h2&gt;

&lt;p&gt;Ready to deploy the RAG system? Some challenges may be encountered when designing the architecture, and preparing the system to deploy in a production environment.&lt;/p&gt;

&lt;p&gt;A few challenges to take into consideration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hallucinations&lt;/strong&gt;: Occur when a model in a RAG system answers with unexpected information due to poor prompt engineering, passing irrelevant chunks, or weak context selection. To fix it, use templates to tell the model how to answer when no information is found, validate periodically the data in the knowledge base to check for outdated or wrong information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Exposure &amp;amp; Prompt Injection&lt;/strong&gt;: When adopting RAG systems, some security issues are expected to happen if the architecture has a poor design. These issues include exposing sensitive data or tricking the model to bypass the rules when receiving malicious prompts. A way to avoid it is by applying input/output sanitization, and setting up guardrails.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices for RAG Deployment
&lt;/h2&gt;

&lt;p&gt;Data Exposure &amp;amp; Prompt Injection: When adopting RAG systems, some security issues are expected to happen if the architecture has a poor design. These issues include exposing sensitive data or tricking the model to bypass the rules when receiving malicious prompts. A way to avoid it is by applying input/output sanitization, and setting up guardrails.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decouple the Retrieval layer from the Generation layer&lt;/strong&gt;: Following a layer approach when designing the system could help reduce hallucinations, maintain the information updated, and upgrades can be applied independently. The Retrieval layer is when data ingestion, embedding conversion, chunk division, and information storing happen, as well as query conversion into vectors for semantic search. While the Generation layer is where the retrieved information, and the original prompt are taken to produce a human-like response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding model replaceability&lt;/strong&gt;: To prevent vendor lock-in, and allow upgrades to better models, design the system to enable embedding model replacement without having to make critical changes or having to rewrite the implementation. How to get it? Create a wrapper function to change between models, use a framework like LangChain instead of using individual SDKs, create metadata with model ID, and version, separate ingestion from retrieval, create a dataset for benchmarking that is constantly evaluated to determine when to change the model, and normalize data before embedding.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Each component in a RAG system architecture is key for the system to work, and provides better answers by improving the knowledge base, but depending on the requirements, and budget of the project, embedding models, vector databases, and cloud platforms are chosen. Already familiar with PostgreSQL? Choose  pgvector, otherwise evaluate other solutions. Planning to build the system from scratch? Consider using Python for implementing the system, as there are frameworks, and libraries available for this language. And don’t forget to follow the recommendations.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>rag</category>
    </item>
    <item>
      <title>PostgreSQL: First Approach to Vector Databases with pgvector and Python</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Sun, 15 Mar 2026 04:31:27 +0000</pubDate>
      <link>https://dev.to/letstalkoss/postgresql-first-approach-to-vector-databases-with-pgvector-and-python-20nm</link>
      <guid>https://dev.to/letstalkoss/postgresql-first-approach-to-vector-databases-with-pgvector-and-python-20nm</guid>
      <description>&lt;p&gt;If you're already familiar with relational databases like PostgreSQL, you're one step closer to start with vector databases and build AI applications. Through this tutorial you'll learn how to enable vector capabilities on your PostgreSQL instance using &lt;code&gt;pgvector&lt;/code&gt;, transform raw text into the required format using Python, and perform searches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set PostgreSQL as a Vector Database
&lt;/h2&gt;

&lt;p&gt;First of all, what is a vector database? In a vector database, information is stored as vectors—often referred to as embeddings. These contain numerical values that represent the meaning of the data, allowing LLMs to interpret and relate information. Query results are determined by the values within these vectors, returning the nearest vectors based on similarity.&lt;/p&gt;

&lt;p&gt;Imagine having a text like: 'Artificial Intelligence is transforming the way we process data in PostgreSQL.'. When converting this value using an embedding model, you'll get a high-dimensional vector that 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;[-0.015540238, 0.0014074693, 0.009978753, -0.07941696, -0.027072648, 0.02588584, 0.0045492477, 0.050993927, 0.019187931, 0.0050778543]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll discuss models, and vectors in detail later. For now, let’s get PostgreSQL ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Installation
&lt;/h3&gt;

&lt;p&gt;Suppose your PostgreSQL instance was installed manually or via your Linux distribution's package manager. To install &lt;code&gt;pgvector&lt;/code&gt;, follow the instructions provided in the official &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. Before starting, make sure &lt;code&gt;make&lt;/code&gt;, &lt;code&gt;clang&lt;/code&gt;, and &lt;code&gt;llvm&lt;/code&gt; are installed on your system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the repository:
&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="nb"&gt;cd&lt;/span&gt; /tmp
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v0.8.2 https://github.com/pgvector/pgvector.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Compile the source code:
&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="nb"&gt;cd &lt;/span&gt;pgvector
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install the extension
&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="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the extension is installed, enable it within your PostgreSQL instance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to your instance:
&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="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Verify the extension was installed correctly:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;dx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                      List of installed extensions
  Name   | Version | Default version |   Schema   |                     Description                      
---------+---------+-----------------+------------+------------------------------------------------------
 plpgsql | 1.0     | 1.0             | pg_catalog | PL/pgSQL procedural language
 vector  | 0.8.2   | 0.8.2           | public     | vector data type and ivfflat and hnsw access methods

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set up a database to store movie synopses
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select the database: &lt;code&gt;\c movies&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable the extension:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running a Docker Container
&lt;/h3&gt;

&lt;p&gt;If you prefer to deploy your PostgreSQL instance using Docker, just run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 pgvector/pgvector:pg17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;password&lt;/code&gt; with your desired password.&lt;/p&gt;

&lt;p&gt;The image used for creating the container is the official Docker image of &lt;code&gt;pgvector&lt;/code&gt; that also includes the PostgreSQL server.&lt;/p&gt;

&lt;p&gt;Log in to the PostgreSQL instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up a database to store movie synopses&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select the database: &lt;code&gt;\c movies&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enable the extension:&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;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Embedding Models
&lt;/h2&gt;

&lt;p&gt;An embedding model is a machine learning model used to transform data sources into vectors.&lt;/p&gt;

&lt;p&gt;The model determines not only how embeddings are created but also the dimensionality of the vector. While these are often provided by LLMs, they can also be specialized models designed solely for embedding tasks.&lt;/p&gt;

&lt;p&gt;For apps built with Python, you can utilize Gemini or OpenAI models through their official SDKs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI Python library → &lt;a href="https://github.com/openai/openai-python" rel="noopener noreferrer"&gt;https://github.com/openai/openai-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google GenAI Python SDK → &lt;a href="https://github.com/googleapis/python-genai" rel="noopener noreferrer"&gt;https://github.com/googleapis/python-genai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another option is to use &lt;a href="https://github.com/langchain-ai/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;, a framework for building agents, and LLM-powered applications, which can facilitate requests to these models.&lt;/p&gt;

&lt;p&gt;LLMs can also be executed locally without sending data to the cloud. This is possible using Open Source tools like &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, where models are downloaded from its own &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A similar and highly popular library is &lt;a href="https://github.com/huggingface/sentence-transformers" rel="noopener noreferrer"&gt;Sentence Transformers&lt;/a&gt;. It provides access to the &lt;a href="https://huggingface.co/models" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt; catalog, allowing you to download models to run locally—typycally via &lt;a href="https://pytorch.org/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;—without the need for an API key.&lt;/p&gt;

&lt;p&gt;Here's a comparison table of the most used embedding models, including name, provider, and dimensionality. Dimensionality refers to the size of the vector created by the model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model Name&lt;/th&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Dimensionality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;text-embedding-3-large&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;3072&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;text-embedding-3-small&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;1536&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;text-embedding-ada-002&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;1536&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gemini-embedding-001&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;3072&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Transforming Data into Vectors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gemini
&lt;/h3&gt;

&lt;p&gt;If you're planning to use the model provided by Google, create an API key first. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the API Keys page in the &lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Create API key&lt;/code&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgitlab.com%2Fmattdark%2Fblog%2F-%2Fblob%2Fmain%2Fimages%2Fmailpit.png" alt="Create API Key" width="" height=""&gt;
&lt;/li&gt;
&lt;li&gt;Assign a name to the key&lt;/li&gt;
&lt;li&gt;Choose a project or create a new one&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Create key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click on the created API key to see the details&lt;/li&gt;
&lt;li&gt;Copy the API key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now set the environment variable for the API key by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replacing &lt;code&gt;YOUR_API_KEY&lt;/code&gt; with the value of the key you prevously created.&lt;/p&gt;

&lt;p&gt;Install the Python library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;google-genai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to transform this text: "A futuristic journey through the stars." into a vector. Here's how you can use the &lt;code&gt;gemini-embedding-001&lt;/code&gt; model from a Python script:&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;google&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genai&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;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A futuristic journey through the stars.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;model_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;gemini-embedding-001&lt;/span&gt;&lt;span class="sh"&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="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dimension: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector preview: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above script executes these tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize the client&lt;/li&gt;
&lt;li&gt;Define the text to transform and the model to be used&lt;/li&gt;
&lt;li&gt;Generate the embedding&lt;/li&gt;
&lt;li&gt;Extract the vector, print the values, and the vector size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python get_embeddings.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dimension: 3072
Vector preview: [-0.003496697, 0.004707519, 0.02058491, -0.0735851, 0.0041175582]...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Showing only first 5 dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sentence Transformers
&lt;/h3&gt;

&lt;p&gt;You can run the models locally via Sentence Transformers without the need of an API key.&lt;/p&gt;

&lt;p&gt;Install the Python library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;sentence-transformers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CHoose a model from the &lt;a href="https://huggingface.co/models" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt; catalog. Here's a Python script that uses the &lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt; whose dimensionality is &lt;code&gt;384&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;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;

&lt;span class="n"&gt;movies&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;A futuristic journey through the stars and the mysteries of the universe.&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;A lighthearted story about two strangers falling in love in New York City.&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;A gritty detective story set in a neon-lit city ruled by artificial intelligence.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Movie &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; dimension: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector preview: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previous script executes the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a list of texts to transform&lt;/li&gt;
&lt;li&gt;Load the model&lt;/li&gt;
&lt;li&gt;Generate the embeddings&lt;/li&gt;
&lt;li&gt;Extract the vectors, print the values, and vector size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python get_embeddings.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Movie 1 dimension: 384
Vector preview: [-0.04579255  0.01413548 -0.01935582]...
Movie 2 dimension: 384
Vector preview: [ 0.02027559 -0.03948853  0.06786963]...
Movie 3 dimension: 384
Vector preview: [-0.01461564  0.01758054  0.00982607]...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storing Generated Vectors in Your Database
&lt;/h2&gt;

&lt;p&gt;Create the &lt;code&gt;synopses&lt;/code&gt; table in the movies database:&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="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;synopses&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3072&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;Note: The dimensionality defined for your VECTOR column must match exactly the output size of the embedding model you are using. For example, if you set VECTOR(3072) for a Gemini model, PostgreSQL will reject any attempts to insert embeddings from a model like all-MiniLM-L6-v2, which outputs 384 dimensions. If you decide to switch models in the future, you will need to either recreate the table or alter the column definition.&lt;/p&gt;

&lt;p&gt;Install required Python libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;psycopg2 pgvector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the Gemini script, updated to transform the list of movie synopses, and later insert the vectors into the database:&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;psycopg2&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pgvector.psycopg2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_vector&lt;/span&gt;

&lt;span class="n"&gt;movies&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;A futuristic journey through the stars and the mysteries of the universe.&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;A lighthearted story about two strangers falling in love in New York City.&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;A gritty detective story set in a neon-lit city ruled by artificial intelligence.&lt;/span&gt;&lt;span class="sh"&gt;"&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;genai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&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="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-embedding-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dbname=movies user=postgres password=secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;register_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO synopses (content, embedding) VALUES (%s, %s)&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="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&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;Vectors from Gemini successfully stored!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to set the environment variable for the API key, and replace the value of the following variable: &lt;code&gt;password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python vectors-to-postgresql.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previous script executes the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a list of texts to transform&lt;/li&gt;
&lt;li&gt;Initialize the client&lt;/li&gt;
&lt;li&gt;Generate the embeddings&lt;/li&gt;
&lt;li&gt;Establish a connection to the database&lt;/li&gt;
&lt;li&gt;Insert each synopses with their respective vector&lt;/li&gt;
&lt;li&gt;Close the connection&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You've got your PostgreSQL instance up and running as a vector database. You’ve learned how to enable pgvector, how to transform raw text into embeddings using both cloud-based tools like Gemini and local models like Sentence Transformers, and how to store those vectors so your database can finally manage more than just plain text. You’ve built the foundation for an AI-powered app—now you’re all set to start experimenting with the data you’ve stored!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
      <category>postgres</category>
    </item>
    <item>
      <title>AI-Assisted Python: Refactoring and Reviewing with Copilot</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Tue, 10 Mar 2026 22:44:53 +0000</pubDate>
      <link>https://dev.to/letstalkoss/ai-assisted-python-refactoring-and-reviewing-with-copilot-4ejh</link>
      <guid>https://dev.to/letstalkoss/ai-assisted-python-refactoring-and-reviewing-with-copilot-4ejh</guid>
      <description>&lt;p&gt;How much time do you spend debugging, implementing features, or refactoring code? Sometimes, these tasks consume hours. While pair programming often optimizes the process, you may find yourself working on a solo project. In the era of AI, you can integrate tools directly into your favorite editor and have a constant collaborator to help you improve your development workflow.&lt;/p&gt;

&lt;p&gt;General-purpose LLMs like ChatGPT or Gemini can help you with these tasks. However, nothing compares to using a tool without having to leave your editor. In Visual Studio Code, you can use Copilot by installing the extension and logging in with your GitHub account. Let's see how it works.&lt;/p&gt;

&lt;p&gt;To demonstrate, I selected a collection of Python scripts designed to generate test data for database schemas, which you can find in &lt;a href="https://github.com/mattdark/data-generator" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Review
&lt;/h2&gt;

&lt;p&gt;The following code block is from the &lt;code&gt;sql.py&lt;/code&gt; script that generates data for MySQL and PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == "__main__":
    num_cores = cpu_count() - 1
    with Pool() as pool:
        data = pd.concat(pool.map(create_dataframe, range(num_cores)))

    data.to_sql(name='employees', con=engine, if_exists='append', index=False, dtype=schema)
    with engine.connect() as conn:
        conn.execute("ALTER TABLE employees ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;")
        # conn.execute("ALTER TABLE employees ADD COLUMN id SERIAL PRIMARY KEY;")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked Copilot to perform a code review. By selecting the code block, right-clicking, and choosing Generate Code &amp;gt; Review, the tool analyzed the logic and provided seven suggestions to improve the script's quality.&lt;/p&gt;

&lt;p&gt;Copilot made the following suggestions:&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%2Fyffothso9b2erikwgyza.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%2Fyffothso9b2erikwgyza.png" alt="Copilot - Code Review" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Process Safety: It warned that &lt;code&gt;cpu_count() - 1&lt;/code&gt; could result in zero cores on single-core systems, causing the multiprocessing pool to fail or hang.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Database Compatibility: It identified that the &lt;code&gt;ALTER TABLE&lt;/code&gt; statement was MySQL-specific and would fail on other backends like PostgreSQL or SQLite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security: To prevent SQL injection, it recommended wrapping raw SQL strings with the &lt;code&gt;sqlalchemy.text()&lt;/code&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance: For large datasets, it suggested using the chunksize parameter in to_sql() to ensure efficient batching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input Validation: It noted that pd.concat would fail if create_dataframe didn't return a valid DataFrame for every call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Explicit Control: It recommended passing the processes argument explicitly to the Pool for better clarity and execution control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Documentation: It suggested documenting the purpose of the schema argument, especially when imported from custom modules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After reviewing the suggestions, I manually implemented the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == "__main__":
    num_cores = cpu_count() if cpu_count() &amp;lt;= 2 else cpu_count() - 1

    with Pool(processes=num_cores) as pool:
        data = pd.concat(pool.map(create_dataframe, range(num_cores)))

    data.to_sql(name='employees', con=engine, if_exists='append', index=False, dtype=schema)

    with engine.begin() as conn:
        conn.execute(text("ALTER TABLE employees ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;"))
        # conn.execute("ALTER TABLE employees ADD COLUMN id SERIAL PRIMARY KEY;")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Refactoring
&lt;/h2&gt;

&lt;p&gt;Within the &lt;code&gt;modules/&lt;/code&gt; directory, you will find the core logic of the generator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;base.py&lt;/code&gt;: Handles the database connection setup and session management for both MySQL and PostgreSQL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dataframe.py&lt;/code&gt;: Contains the logic to generate synthetic data and store it temporarily in a pandas DataFrame before ingestion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;schema.py&lt;/code&gt;: Defines the database schema and data types required for cross-database compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open the &lt;code&gt;base.py&lt;/code&gt; script and use the Copilot Chat to refactor the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine("mysql+pymysql://user:password@localhost/company")
#engine = create_engine("postgresql+psycopg2://user:password@localhost:5432/company")
Session = sessionmaker(bind=engine)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I asked the tool to enable dynamic database connection selection for all the technologies supported by this project. The prompt used was the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refactor my database logic into base.py. Create a function get_client() that returns either a SQLAlchemy Session (for MySQL/Postgres) or a PyMongo MongoClient (for MongoDB) based on the DB_TYPE environment variable. Ensure the function handles the connection logic for all three databases using credentials from os.getenv. Use PyMongo instead of Motor to keep the workflow synchronous. Follow PEP 8 strictly and include type hints to distinguish between the SQL session and the NoSQL client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After analyzing the prompt, Copilot will review the code and suggest changes that you can apply directly from the chat interface, as shown in the following image. &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%2Ffhycuhk9065fgxj32mwy.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%2Ffhycuhk9065fgxj32mwy.png" alt="Copilot - Refactoring" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After applying the changes, this is how the script looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def _build_sql_url(db_type: str) -&amp;gt; str:
    """Build a SQLAlchemy URL for MySQL or PostgreSQL."""
    user = os.getenv("DB_USER", "")
    password = os.getenv("DB_PASSWORD", "")
    host = os.getenv("DB_HOST", "localhost")
    port = os.getenv("DB_PORT")
    db_name = os.getenv("DB_NAME", "")

    if not db_name:
        raise ValueError("DB_NAME environment variable must be set")

    if db_type == "mysql":
        driver = "mysql+pymysql"
        port = port or "3306"
    elif db_type == "postgres":
        driver = "postgresql+psycopg2"
        port = port or "5432"
    else:
        raise ValueError(f"Unsupported SQL DB_TYPE: {db_type!r}")

    return f"{driver}://{user}:{password}@{host}:{port}/{db_name}"


def _build_mongo_uri() -&amp;gt; str:
    """Return a MongoDB URI using authSource=admin."""
    user = os.getenv("DB_USER", "")
    password = os.getenv("DB_PASSWORD", "")
    host = os.getenv("DB_HOST", "localhost")
    port = os.getenv("DB_PORT", "27017")
    db_name = os.getenv("DB_NAME", "")

    if not db_name:
        raise ValueError("DB_NAME environment variable must be set")

    return (
        f"mongodb://{user}:{password}@{host}:{port}/{db_name}"
        "?authSource=admin"
    )


def get_client() -&amp;gt; Union[SASession, MongoClient]:
    """Return a DB client based on DB_TYPE.

    For 'mysql' or 'postgres' returns a SQLAlchemy Session instance.
    For 'mongodb' returns a pymongo.MongoClient.
    """
    db_type = os.getenv("DB_TYPE", "mysql").lower()

    if db_type in {"mysql", "postgres"}:
        url = _build_sql_url(db_type)
        engine = create_engine(url)
        return db_type, engine

    if db_type == "mongodb":
        uri = _build_mongo_uri()
        return db_type, MongoClient(uri)

    raise ValueError(f"Unsupported DB_TYPE: {db_type!r}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must create an &lt;code&gt;.env&lt;/code&gt; file and set the environment variables for the database connection details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.example
DB_TYPE=mysql
DB_USER=your_user
DB_PASSWORD=your_password
DB_HOST=localhost
DB_PORT=3306
DB_NAME=your_database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;DB_TYPE&lt;/code&gt; can be set to any of the following values: &lt;code&gt;mysql&lt;/code&gt;, &lt;code&gt;postgres&lt;/code&gt;, and &lt;code&gt;mongodb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that the database connection selection is configured, the &lt;code&gt;sql.py&lt;/code&gt; script must be adapted to automatically select the database and insert the data based on the environment variables in the &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == "__main__":
    num_cores = cpu_count() if cpu_count() &amp;lt;= 2 else cpu_count() - 1

    db_type, client = get_client()

    with Pool(processes=num_cores) as pool:
        data = pd.concat(pool.map(create_dataframe, range(num_cores)))

    if db_type in {"mysql", "postgres"}:
        data.to_sql(name='employees', con=client, if_exists='append', index=False, dtype=schema)

        with client.begin() as conn:
            if db_type == "mysql":
                conn.execute(text("ALTER TABLE employees ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;"))
            elif db_type == "postgres":
                conn.execute(text("ALTER TABLE employees ADD COLUMN id SERIAL PRIMARY KEY;"))
    elif db_type == "mongodb":
        data_dict = data.to_dict('records')
        db = client["company"]
        collection = db["employees"]
        collection.insert_many(data_dict)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must rename the &lt;code&gt;sql.py&lt;/code&gt; script to &lt;code&gt;generate_data.py&lt;/code&gt;, and delete the &lt;code&gt;mongodb.py&lt;/code&gt; script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Tests
&lt;/h2&gt;

&lt;p&gt;Configure your database instance to validate that the script is working. Run any of the following instructions to start the corresponding container. Choose the one you're using for your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MariaDB:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d \
  --name mariadb \
  -p 3306:3306 \
  -e MARIADB_ROOT_PASSWORD=12345 \
  -e MARIADB_DATABASE=company \
  mariadb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;PostgreSQL:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d \
  --name postgres \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=12345 \
  -e POSTGRES_DB=company \
  postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;MongoDB
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d \
  --name db-mongo \
  -p 27017:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=username \
  -e MONGO_INITDB_ROOT_PASSWORD=12345 \
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then edit the &lt;code&gt;.env&lt;/code&gt; file and replace the values of the environment variables with the connection details.&lt;/p&gt;

&lt;p&gt;Now you can run the script:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The script is designed to create data for a database that stores information about employees, but you can adapt it to your needs.&lt;/p&gt;

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

&lt;p&gt;While GitHub Copilot is a powerful tool to integrate within your workflow, it is essential to remember that you must always review and validate the suggested code, as it may not work as expected. The quality of the output depends heavily on your input; therefore, your prompts should be as descriptive as possible, providing clear context about your architectural requirements and dependencies.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>From Prototype to Pharmacy Dashboard: Scaling an AI-Generated App with Google Gemini</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Sun, 01 Mar 2026 01:33:35 +0000</pubDate>
      <link>https://dev.to/letstalkoss/from-prototype-to-pharmacy-dashboard-scaling-an-ai-generated-app-with-google-gemini-1c8j</link>
      <guid>https://dev.to/letstalkoss/from-prototype-to-pharmacy-dashboard-scaling-an-ai-generated-app-with-google-gemini-1c8j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/mlh-built-with-google-gemini-02-25-26"&gt;Built with Google Gemini: Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built with Google Gemini
&lt;/h2&gt;

&lt;p&gt;Last year, a friend hired me to develop a custom dashboard for his pharmacy. At the time, I was working daily with Vue.js and TypeScript, but I wanted to find a platform to accelerate the initial implementation. That is when I discovered Tempo, an AI-powered design and development tool for React.&lt;/p&gt;

&lt;p&gt;The platform provided enough daily credits on its free tier to get started, and I loved that the code synced directly with a GitHub repository. I used most of my credits for bug fixing and feature implementation; however, the platform eventually tightened its credit limits, resetting them every 24 hours. Despite these constraints, I managed to get an initial implementation ready for testing, which included the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;User Management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Branch Management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inventory Management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monthly Sales Reporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multi-language Support (Spanish &amp;amp; English)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IndexedDB for Local Data Storage&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Due to these limitations and several features yet to be built, I transitioned the development to Google Gemini to take the project to production.&lt;/p&gt;

&lt;p&gt;Gemini played a key role in bug fixing, feature development, and migration of the database from local storage to Cloud Firestore. Additionally, it guided the deployment strategy, using GitLab Pages and Vercel to take the project to a production-ready state.&lt;/p&gt;

&lt;p&gt;Additional features implemented with Gemini:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dual-Layer Inventory: Divided into Pharmacy and Warehouse stocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Daily and Weekly Sales Reporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inventory Search and Editing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Orders Module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Daily, Weekly and Monthly Orders Reporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Daily Sales and Orders Editing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;POS Module with Barcode Scanner Support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Firestore User Management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stock Alerts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PDF Reports Download&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;I don't have a public demo instance available since the only live version is the one my client is using (and it requires a login, of course!). So, I’ve put together some screenshots to show you how the dashboard looks like.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access &amp;amp; Security (Sign In): The entry point of the app, integrated with Firebase Authentication for secure access.&lt;/li&gt;
&lt;/ul&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%2F5kqaqp47p53zhs0bkaej.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%2F5kqaqp47p53zhs0bkaej.png" alt="Sign In" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Command Center (Dashboard): A high-level overview of the pharmacy's health, from stock alerts to quick sales stats.&lt;/li&gt;
&lt;/ul&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%2Fmowotqauslz05zfsms5y.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%2Fmowotqauslz05zfsms5y.png" alt="Dashboard" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dual-Layer Inventory: This is where the magic happens: managing stock between the Pharmacy and the Warehouse.&lt;/li&gt;
&lt;/ul&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%2Fiyppppgk6fzmq8cyl698.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%2Fiyppppgk6fzmq8cyl698.png" alt="Dual-Layer Inventory" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Point of Sale (Sales Module): The real-time interface for pharmacists, featuring barcode scanner support for quick checkouts.&lt;/li&gt;
&lt;/ul&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%2Fd9i0zre0ugn727kb1c6c.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%2Fd9i0zre0ugn727kb1c6c.png" alt="Sales" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data-Driven Decisions (Sales Report): Daily, weekly, and monthly analytics that help the owner understand their revenue trends.&lt;/li&gt;
&lt;/ul&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%2Fvxm1otookxtvu5ozqcs1.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%2Fvxm1otookxtvu5ozqcs1.png" alt="Sales Report" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supply Chain Management (Orders): The module built to handle restocking and tracking orders for new medicine supplies.&lt;/li&gt;
&lt;/ul&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%2Freibgfen2wqzhd7v9d03.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%2Freibgfen2wqzhd7v9d03.png" alt="Orders" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This project was a continuous learning journey. Here are the key technical milestones I reached with Gemini’s guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Mastering AI-Assisted Development: I learned to write better, more contextual prompts. By using tools like Tempo for the initial UI and Gemini to refactor auto-generated code, I was able to transform a simple prototype into a production-ready system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From IndexedDB to Cloud Firestore: I hadn't used IndexedDB or SQLite before. While I learned to configure their connections and CRUD methods, I quickly realized their biggest constraint: local data doesn't sync across devices. As a developer with experience in MySQL, and PostgreSQL, I needed a scalable, cloud-native solution. Gemini suggested Cloud Firestore, and its free tier perfectly satisfied the project's requirements. Although I had previous experience with NoSQL databases like MongoDB, this was my first time with Firestore. I learned how data is structured and how to use Firebase Authentication to handle users securely without storing sensitive login data in plain documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scalable Solution (Two Repositories): I decided to separate the project into two distinct repositories:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/mattdark/firebase-admin-api" rel="noopener noreferrer"&gt;The Backend API&lt;/a&gt;: Built to solve a specific challenge: creating users without terminating the current administrator's session. This API manages user creation, updates, and authentication independently. I deployed it using Vercel (my first time using the platform) and configured a GitLab CI/CD pipeline for automated deployment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/mattdark/pharmacare" rel="noopener noreferrer"&gt;The Frontend Application&lt;/a&gt;: Deployed via GitLab Pages. I also configured a dedicated GitLab CI pipeline to build and deploy the application automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Overcoming SPA Routing Hurdles on GitLab Pages: During the deployment of the Frontend, I faced a common issue with Single Page Applications (SPAs): 404 errors when refreshing routes. By diving into the GitLab documentation and working with Gemini, I learned how GitLab Pages handles routing and implemented the necessary configuration to ensure the SPA router functioned correctly in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Local Development with gcloud Emulator: To ensure a safe and efficient workflow, I learned how to test database changes using the gcloud Emulator. This allowed me to validate Firestore security rules and data structures locally before deploying them to the cloud. I found this process so valuable that I documented it in a dedicated &lt;a href="https://dev.to/mattdark/local-firestore-development-with-the-gcloud-emulator-52bl"&gt;article&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Google Gemini Feedback
&lt;/h2&gt;

&lt;p&gt;Working with Gemini was a game-changer for this project, but like any powerful tool, it has its learning curve. Here’s my feedback:&lt;/p&gt;

&lt;p&gt;What Worked Well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Guidance: Gemini excelled when I asked concrete questions about technology stacks (like the move from IndexedDB to Firestore). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactoring: When I shared specific code snippets and explained the required business logic, Gemini was incredibly effective at suggesting precise changes and handling complex refactors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Problem-Solving: It was instrumental in debugging the SPA routing issues on GitLab Pages and configuring the CI/CD pipelines for Vercel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Areas for Improvement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Repository Integration: Currently, Gemini struggles to analyze an entire repository at once. I believe it would be a massive productivity boost if Gemini could connect directly to a GitHub or GitLab repository to understand the full codebase context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IDE Extension: While the web interface is great, having a more integrated experience directly within the code editor (beyond simple autocomplete) would reduce the friction of switching back and forth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accuracy &amp;amp; Verification: AI responses are not always 100% accurate. I quickly learned that you must always review and test suggested changes before merging them, as some suggestions can inadvertently break existing module logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>geminireflections</category>
      <category>gemini</category>
    </item>
    <item>
      <title>App Localization with Python and Argos Translate</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Fri, 27 Feb 2026 03:14:36 +0000</pubDate>
      <link>https://dev.to/letstalkoss/app-localization-with-python-and-argos-translate-2oab</link>
      <guid>https://dev.to/letstalkoss/app-localization-with-python-and-argos-translate-2oab</guid>
      <description>&lt;p&gt;Last year, I worked on localizing a platform from Spanish to English. The strings were stored in JSON files within a directory called &lt;code&gt;es&lt;/code&gt;, and the goal was to generate the same files translated into English and save them in a directory named &lt;code&gt;en&lt;/code&gt;. Here's &lt;a href="https://dev.to/mattdark/localization-made-easy-with-python-and-deepl-1l1e"&gt;an article&lt;/a&gt; I wrote on how I optimized the process by generating the initial localization using Python and DeepL.&lt;/p&gt;

&lt;p&gt;However, DeepL is not Open Source and its usage is limited depending on the plan you choose. So, what’s a good Open Source alternative? &lt;a href="https://www.argosopentech.com/" rel="noopener noreferrer"&gt;Argos Translate&lt;/a&gt;—and in this article, I’ll show you how to use it for your localization projects.&lt;/p&gt;

&lt;p&gt;Argos Translate is an Open Source tool that uses &lt;a href="https://opennmt.net/" rel="noopener noreferrer"&gt;OpenNMT&lt;/a&gt; for translations and works offline. They also offer &lt;a href="https://libretranslate.com/" rel="noopener noreferrer"&gt;LibreTranslate&lt;/a&gt;, an API built on top of Argos Translate that doesn't require creating an account.&lt;/p&gt;

&lt;p&gt;It can be used as a Python library, a command-line tool, or a GUI application. For this workflow, it’s recommended to use the Python library along with the API provided by LibreTranslate.&lt;/p&gt;

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

&lt;p&gt;Before importing it to your Python script, you may install it by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install argostranslate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that compatible versions of the following dependencies are installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;urllib3&lt;/li&gt;
&lt;li&gt;charset-normalizer&lt;/li&gt;
&lt;li&gt;chardet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get them just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install --upgrade --force-reinstall urllib3==1.26.19 charset-normalizer==2.1.1 chardet==4.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to try LibreTranslate, install it by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install libretranslate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Download Language Model
&lt;/h2&gt;

&lt;p&gt;Language models can be installed via the Python library, downloaded using the command-line tool, or obtained manually from the &lt;a href="https://www.argosopentech.com/argospm/index/" rel="noopener noreferrer"&gt;Argos Translate Package Index&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Via the command-line tool, by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argospm install translate-es_en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;es&lt;/code&gt; is the original language, and &lt;code&gt;en&lt;/code&gt; is the target language.&lt;/p&gt;

&lt;p&gt;If you use &lt;code&gt;argospm&lt;/code&gt;, downloading the model from Python is optional, but here's the script to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import argostranslate.package

from_code = "es"
to_code = "en"

argostranslate.package.update_package_index()
available_packages = argostranslate.package.get_available_packages()
package_to_install = next(
    filter(
        lambda x: x.from_code == from_code and x.to_code == to_code, available_packages
    )
)
argostranslate.package.install_from_path(package_to_install.download())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, import the required library: &lt;code&gt;argotranslate.package&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Set source and target language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from_code = "es"
to_code = "en"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the package index and get a list of the available packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argostranslate.package.update_package_index()
available_packages = argostranslate.package.get_available_packages()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find and get the name of the package to install by filtering from the available packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package_to_install = next(
    filter(
        lambda x: x.from_code == from_code and x.to_code == to_code, available_packages
    )
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, install the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argostranslate.package.install_from_path(package_to_install.download())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Command-line Tool
&lt;/h2&gt;

&lt;p&gt;It will work for direct translation, but not for translating JSON files. If you want to translate text, you can use it this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argos-translate --from es --to en "¡Hola mundo!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get the following output: &lt;code&gt;Hey, World!&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Python Library
&lt;/h2&gt;

&lt;p&gt;Suppose you have the following content in a JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "tipo-perfil": {
    "label": "Tipo de perfil",
    "description": "Tipo de perfil",
    "tooltip": "Tipo de perfil",
    "validations": {
        "required": "El campo Tipo de perfil es requerido",
        "minMessage": "El número de caracteres debe ser de al menos {min}",
        "maxMessage": "El número de caracteres debe ser máximo de {max}",
        "regexMessage": "Formato de Tipo de perfil inválido"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the language model is already installed, you can translate the string in the JSON file with the following Python script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import argostranslate.translate

installed_languages = argostranslate.translate.get_installed_languages()
spanish = next(filter(lambda l: l.code == "es", installed_languages))
english = next(filter(lambda l: l.code == "en", installed_languages))
translation = spanish.get_translation(english)

def translate_json(obj, translator):
    if isinstance(obj, dict):
        return {k: translate_json(v, translator) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [translate_json(i, translator) for i in obj]
    elif isinstance(obj, str):
        return translator.translate(obj)
    else:
        return obj

with open("input.json", "r", encoding="utf-8") as f:
    data = json.load(f)

translated_data = translate_json(data, translation)

with open("translated.json", "w", encoding="utf-8") as f:
    json.dump(translated_data, f, indent=2, ensure_ascii=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the required libraries: &lt;code&gt;json&lt;/code&gt; &amp;amp; &lt;code&gt;argostranslate.translate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Load the language models installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;installed_languages = argostranslate.translate.get_installed_languages()
spanish = next(filter(lambda l: l.code == "es", installed_languages))
english = next(filter(lambda l: l.code == "en", installed_languages))
translation = spanish.get_translation(english)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the JSON file and read the content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with open("input.json", "r", encoding="utf-8") as f:
    data = json.load(f)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call the function that translates the content of the JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;translated_data = translate_json(data, translation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And save the result in a new file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with open("translated.json", "w", encoding="utf-8") as f:
    json.dump(translated_data, f, indent=2, ensure_ascii=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recursive &lt;code&gt;translate_json&lt;/code&gt; function takes an object that can be a dictionary, list, or string. Strings are translated directly, while dictionaries and lists are processed recursively to translate all nested strings, ensuring the entire JSON is translated correctly regardless of depth.&lt;/p&gt;

&lt;p&gt;If you have multiple JSON files in a folder and subfolders, you can extend the script to process all of them automatically.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;os&lt;/code&gt; module to the imports.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Set input and output folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input_folder = "es"
output_folder = "en"
os.makedirs(output_folder, exist_ok=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use the following code to recursively translate all JSON files while preserving the folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for root, dirs, files in os.walk(input_folder):
    for filename in files:
        if filename.endswith(".json"):
            input_path = os.path.join(root, filename)
            # Mantener la misma estructura de subcarpetas en output
            relative_path = os.path.relpath(input_path, input_folder)
            output_path = os.path.join(output_folder, relative_path)
            os.makedirs(os.path.dirname(output_path), exist_ok=True)

            with open(input_path, "r", encoding="utf-8") as f:
                data = json.load(f)
            translated_data = translate_json(data)
            with open(output_path, "w", encoding="utf-8") as f:
                json.dump(translated_data, f, indent=2, ensure_ascii=False)

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

&lt;/div&gt;



&lt;p&gt;The above code will replace this single-file block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with open("input.json", "r", encoding="utf-8") as f:
    data = json.load(f)

translated_data = translate_json(data, translation)

with open("translated.json", "w", encoding="utf-8") as f:
    json.dump(translated_data, f, indent=2, ensure_ascii=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the LibreTranslate API
&lt;/h2&gt;

&lt;p&gt;You can also translate your JSON files using the LibreTranslate API instead of using local models. The workflow is almost identical to the previous script, with only a few changes.&lt;/p&gt;

&lt;p&gt;Remove &lt;code&gt;argostranslate.translate&lt;/code&gt; from imports and add &lt;code&gt;requests&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;translate_text&lt;/code&gt; function, set the source and target languages, and send a request to the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def translate_text(text):
    source = "es"
    target = "en"
    response = requests.post(
        "https://translate.argosopentech.com/translate",
        json={"q": text, "source": source, "target": target},
        headers={"Content-Type": "application/json"}
    )
    return response.json()["translatedText"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;translate_json&lt;/code&gt; function will now call &lt;code&gt;translate_text&lt;/code&gt;, which uses the API, instead of the local translator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def translate_json(obj, translator):
    if isinstance(obj, dict):
        return {k: translate_json(v, translator) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [translate_json(i, translator) for i in obj]
    elif isinstance(obj, str):
        return translator.translate(obj)
    else:
        return obj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're translating a single file, you must open it first, and load the data, call the &lt;code&gt;translate_json&lt;/code&gt; function, and save the result in another file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with open("input.json", "r", encoding="utf-8") as f:
    data = json.load(f)

translated_data = translate_json(data)

with open("translated.json", "w", encoding="utf-8") as f:
    json.dump(translated_data, f, indent=2, ensure_ascii=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to translate multiple JSON files recursively, use the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for root, dirs, files in os.walk(input_folder):
    for filename in files:
        if filename.endswith(".json"):
            input_path = os.path.join(root, filename)
            relative_path = os.path.relpath(input_path, input_folder)
            output_path = os.path.join(output_folder, relative_path)
            os.makedirs(os.path.dirname(output_path), exist_ok=True)

            with open(input_path, "r", encoding="utf-8") as f:
                data = json.load(f)

            translated_data = translate_json(data)

            with open(output_path, "w", encoding="utf-8") as f:
                json.dump(translated_data, f, indent=2, ensure_ascii=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And don't forget to add the &lt;code&gt;os&lt;/code&gt; module to the imports, and set input and output folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os

input_folder = "es"
output_folder = "en"
os.makedirs(output_folder, exist_ok=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In this article, you learned how to use Argos Translate and LibreTranslate to simplify translating JSON-based applications.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>BSON to JSON: Efficient Data Conversion with Java</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Mon, 02 Feb 2026 21:36:53 +0000</pubDate>
      <link>https://dev.to/letstalkoss/bson-to-json-efficient-data-conversion-with-java-2gpf</link>
      <guid>https://dev.to/letstalkoss/bson-to-json-efficient-data-conversion-with-java-2gpf</guid>
      <description>&lt;p&gt;Through this blog post, you will learn how to convert a BSON document to JSON using Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSON to JSON with Java
&lt;/h2&gt;

&lt;p&gt;If you’re a Java developer, there are two ways to read BSON documents and convert them to JSON.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using the MongoDB Java Driver to query an active database&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reading and parsing local .bson files at the byte level&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create a sample project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn archetype:generate \
  -DgroupId=com.your-domain \
  -DartifactId=bson-to-json \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, add dependencies and configure the project by editing the &lt;code&gt;pom.xml&lt;/code&gt; file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;
  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

  &amp;lt;groupId&amp;gt;com.your-domain&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;bson-to-json&amp;lt;/artifactId&amp;gt;
  &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt;
  &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
  &amp;lt;name&amp;gt;bson-to-json&amp;lt;/name&amp;gt;

  &amp;lt;properties&amp;gt;
    &amp;lt;maven.compiler.release&amp;gt;11&amp;lt;/maven.compiler.release&amp;gt;
    &amp;lt;project.build.sourceEncoding&amp;gt;UTF-8&amp;lt;/project.build.sourceEncoding&amp;gt;
  &amp;lt;/properties&amp;gt;

  &amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.mongodb&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;mongodb-driver-sync&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.3.1&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;slf4j-nop&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;2.0.9&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;4.13.2&amp;lt;/version&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
  &amp;lt;/dependencies&amp;gt;

  &amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
      &amp;lt;plugin&amp;gt;
        &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;exec-maven-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.1.0&amp;lt;/version&amp;gt;
        &amp;lt;configuration&amp;gt;
          &amp;lt;mainClass&amp;gt;com.your-domain.App&amp;lt;/mainClass&amp;gt;
          &amp;lt;cleanupDaemonThreads&amp;gt;false&amp;lt;/cleanupDaemonThreads&amp;gt;
        &amp;lt;/configuration&amp;gt;
      &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
  &amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Lock the project to a specific Java version (in this case, Java 11) to ensure consistent compilation across different environments&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;properties&amp;gt;
    &amp;lt;maven.compiler.release&amp;gt;11&amp;lt;/maven.compiler.release&amp;gt;
    &amp;lt;project.build.sourceEncoding&amp;gt;UTF-8&amp;lt;/project.build.sourceEncoding&amp;gt;
  &amp;lt;/properties&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the MongoDB driver as dependency&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.mongodb&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;mongodb-driver-sync&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.3.1&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Silence internal MongoDB driver logs using SLF4J-NOP&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;slf4j-nop&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;2.0.9&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Automate dependency linking and silence MongoDB's background thread warnings by using the &lt;code&gt;exec-maven-plugin&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
      &amp;lt;plugin&amp;gt;
        &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;exec-maven-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.1.0&amp;lt;/version&amp;gt;
        &amp;lt;configuration&amp;gt;
        &amp;lt;mainClass&amp;gt;com.your-domain.App&amp;lt;/mainClass&amp;gt;
        &amp;lt;cleanupDaemonThreads&amp;gt;false&amp;lt;/cleanupDaemonThreads&amp;gt;
        &amp;lt;/configuration&amp;gt;
      &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
  &amp;lt;/build&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h3&gt;
  
  
  Using the MongoDB Java Driver to query an active database
&lt;/h3&gt;

&lt;p&gt;Now, edit the &lt;code&gt;App.java&lt;/code&gt; file stored in the &lt;code&gt;src/main/java/com/your-domain/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.your-domain;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.bson.json.JsonWriterSettings;

import java.io.FileWriter;
import java.io.IOException;

public class App {
    public static void main(String[] args) {
        String uri = "mongodb://user:password@localhost:27017/?authSource=admin";

        try (MongoClient mongoClient = MongoClients.create(uri)) {
            MongoDatabase database = mongoClient.getDatabase("database");
            MongoCollection&amp;lt;Document&amp;gt; collection = database.getCollection("collection");

            JsonWriterSettings settings = JsonWriterSettings.builder().indent(true).build();

            try (FileWriter file = new FileWriter("collection.json")) {
                file.write("[\n");

                for (Document doc : collection.find()) {
                    file.write(doc.toJson(settings) + ",\n");
                }

                file.write("]");
                System.out.println("Exported successfully!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Declares the package that the Java class belongs to&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.your-domain;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Establish connection to the MongoDB instance&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        try (MongoClient mongoClient = MongoClients.create(uri))
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Where &lt;code&gt;uri&lt;/code&gt; is the connection string.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Target the specific database and collection from which the data will be exported&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            MongoDatabase database = mongoClient.getDatabase("company");
            MongoCollection&amp;lt;Document&amp;gt; collection = database.getCollection("employees");
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pretty format the content of the JSON file&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            JsonWriterSettings settings = JsonWriterSettings.builder().indent(true).build();
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Manually construct and write a JSON array by wrapping the converted documents in brackets&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                file.write("[\n");
                for (Document doc : collection.find()) {
                    file.write(doc.toJson(settings) + ",\n");
                }
                file.write("]");
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h3&gt;
  
  
  Reading and parsing local .bson files at the byte level
&lt;/h3&gt;

&lt;p&gt;Now, edit the &lt;code&gt;App.java&lt;/code&gt; file stored in the &lt;code&gt;src/main/java/com/your-domain/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.your-domain;

import org.bson.Document;
import org.bson.BsonType;
import org.bson.codecs.DocumentCodec;
import org.bson.codecs.DecoderContext;
import org.bson.BsonBinaryReader;
import org.bson.io.ByteBufferBsonInput;
import org.bson.ByteBufNIO;
import org.bson.json.JsonWriterSettings;

import java.io.FileInputStream;
import java.io.FileWriter;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class App {
    public static void main(String[] args) throws Exception {
        String inputPath = "collection.bson";
        String outputPath = "collection.json";
        JsonWriterSettings settings = JsonWriterSettings.builder().indent(true).build();
        DocumentCodec codec = new DocumentCodec();

        try (FileInputStream fis = new FileInputStream(inputPath);
             FileChannel channel = fis.getChannel();
             FileWriter writer = new FileWriter(outputPath)) {

            ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());
            channel.read(buffer);
            buffer.flip();

            try (ByteBufferBsonInput bsonInput = new ByteBufferBsonInput(new ByteBufNIO(buffer));
                 BsonBinaryReader reader = new BsonBinaryReader(bsonInput)) {

                writer.write("[\n");
                boolean first = true;

                while (buffer.hasRemaining()) {

                    if (reader.readBsonType() == BsonType.END_OF_DOCUMENT) {
                        break;
                    }

                    if (!first) {
                        writer.write(",\n");
                    }

                    Document doc = codec.decode(reader, DecoderContext.builder().build());
                    writer.write(doc.toJson(settings));
                    first = false;
                }

                writer.write("\n]");
                System.out.println("Conversion completed successfully: " + outputPath);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Initialize the file paths, configures the JSON output with indentation for readability and sets up the engine for translating binary data into Java objects&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    String inputPath = "collection.bson";
    String outputPath = "collection.json";
    JsonWriterSettings settings = JsonWriterSettings.builder().indent(true).build();
    DocumentCodec codec = new DocumentCodec();
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allocates a memory buffer the exact size of the file and uses a &lt;code&gt;FileChannel&lt;/code&gt; to load the binary data for processing&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());
            channel.read(buffer);
            buffer.flip();
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wraps the memory buffer into a &lt;code&gt;ByteBufferBsonInput&lt;/code&gt; and creates a &lt;code&gt;BsonBinaryReader&lt;/code&gt; to navigate the binary structure&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                try (ByteBufferBsonInput bsonInput = new ByteBufferBsonInput(new ByteBufNIO(buffer));
                  BsonBinaryReader reader = new BsonBinaryReader(bsonInput)) {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Writes the opening bracket and uses a loop to iterate through the buffer until no documents remain
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  writer.write("[\n");
                  boolean first = true;

                  while (buffer.hasRemaining()) {
                      if (reader.readBsonType() == BsonType.END_OF_DOCUMENT) {
                          break;
                      }
                      // ... loop logic
                  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Translates each binary BSON segment into a Java Document and writes it to the file as a formatted JSON string
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  Document doc = codec.decode(reader, DecoderContext.builder().build());
                  writer.write(doc.toJson(settings));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The BSON file must be in the root directory of your Maven project. Now, you can run the above code by executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean compile exec:java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will do a clean build, delete the &lt;code&gt;target&lt;/code&gt; folder, compile the Java code, and run the project.&lt;/p&gt;

&lt;p&gt;After running, you will find the corresponding JSON file in the root directory of your project&lt;/p&gt;

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

&lt;p&gt;If you’re a developer, you can use the MongoDB Java driver to query and analyze database collections before exporting them to JSON, or directly parse local BSON files for a quick format conversion.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>mongodb</category>
      <category>java</category>
    </item>
    <item>
      <title>BSON to JSON: The Python Way</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Tue, 27 Jan 2026 03:51:41 +0000</pubDate>
      <link>https://dev.to/letstalkoss/bson-to-json-the-python-way-16d9</link>
      <guid>https://dev.to/letstalkoss/bson-to-json-the-python-way-16d9</guid>
      <description>&lt;p&gt;Through this blog post, you will learn how to convert a BSON document to JSON using Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSON to JSON with Python
&lt;/h2&gt;

&lt;p&gt;If you’re a Python developer, there are two ways for reading a BSON document and converting it to JSON.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;bson&lt;/code&gt; module from &lt;a href="https://pymongo.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;PyMongo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&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;bson&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;decode_all&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bson.json_util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dumps&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./data.bson&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;rb&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;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decode_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./data.json&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;w&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;outfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the script is doing:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Import the `decode_all`  and `dumps` methods from the `bson` module&lt;/li&gt;
  &lt;li&gt;Open the file to read the content and decode the data&lt;/li&gt;
  &lt;li&gt;Create a JSON file, and write the JSON document created from the data of the BSON file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The script works with BSON files generated by mongodump. Before running the script, you must install PyMongo: &lt;code&gt;pip install pymongo&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to the database and querying the data with PyMongo, the Python driver for MongoDB.
&lt;/li&gt;
&lt;/ul&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;pymongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MongoClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bson.json_util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dumps&lt;/span&gt;

&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongodb://username:password@host:port/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;db&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="n"&gt;company&lt;/span&gt;
&lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;

&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;list_cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;json_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&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="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data.json&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;w&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="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_data&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 what the script is doing:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Import the `MongoClient` method from the `pymongo` library, and the `dumps` method from the `bson` module&lt;/li&gt;
  &lt;li&gt;Establish the connection to the database&lt;/li&gt;
  &lt;li&gt;Set the database (e.g., `company` ) and the collection (e.g., `employees`) you want to query&lt;/li&gt;
  &lt;li&gt;Retrieve the documents in the collection with the `find()`  method and create a list with the result. If you don’t pass any parameter to this method, the result will be similar to `SELECT *`  in MySQL&lt;/li&gt;
  &lt;li&gt;Create a JSON object by calling the `dumps` method. The `indent = 2` parameter will tell `dumps()` to pretty format the JSON object&lt;/li&gt;
  &lt;li&gt;Write the content of the `json_data`  variable to the `data.json` file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before running the script, you must install PyMongo: &lt;code&gt;pip install pymongo&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;If you’re a developer, you can use the MongoDB driver of your programming language of choice and query the data to analyze the content of the collections in your database. For Python, you can install PyMongo, connect to the database, query the data and use the bson module to save the content as a JSON document.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>mongodb</category>
      <category>python</category>
    </item>
    <item>
      <title>BSON to JSON: The Standard Tools</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Tue, 27 Jan 2026 03:44:03 +0000</pubDate>
      <link>https://dev.to/letstalkoss/bson-to-json-the-standard-tools-5c68</link>
      <guid>https://dev.to/letstalkoss/bson-to-json-the-standard-tools-5c68</guid>
      <description>&lt;p&gt;Binary Javascript Object Notation (BSON) is a bin­ary-en­coded seri­al­iz­a­tion of JSON documents. JSON is easier to understand as it is human-readable, but compared to BSON, it supports fewer data types. BSON has been extended to add some optional non-JSON-native data types, like dates and binary data.&lt;/p&gt;

&lt;p&gt;MongoDB stores data in BSON format both internally and over the network. It is also the format used for the output files generated by mongodump. To read the content of a BSON document, you have to convert it to a human-readable format like JSON.&lt;/p&gt;

&lt;p&gt;Through this blog post, you will learn how to convert a BSON document to JSON. Some of the methods I will explain include using bsondump, mongoexport, and Bash.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSON to JSON with bsondump
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.mongodb.com/docs/database-tools/bsondump/" rel="noopener noreferrer"&gt;bsondump&lt;/a&gt; converts &lt;a href="https://www.mongodb.com/docs/manual/reference/glossary/#std-term-BSON" rel="noopener noreferrer"&gt;BSON&lt;/a&gt; files into human-readable formats, including &lt;a href="https://www.mongodb.com/docs/manual/reference/glossary/#std-term-JSON" rel="noopener noreferrer"&gt;JSON&lt;/a&gt;. For example, bsondump is useful for reading the output files generated by &lt;a href="https://www.mongodb.com/docs/database-tools/mongodump/#mongodb-binary-bin.mongodump" rel="noopener noreferrer"&gt;mongodump&lt;/a&gt;. The bsondump tool is part of the &lt;a href="https://www.mongodb.com/docs/database-tools/installation/installation/" rel="noopener noreferrer"&gt;MongoDB Database Tools&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;bsondump&lt;/code&gt; from the system command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bsondump --outFile=collection.json collection.bson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will create a JSON file (&lt;code&gt;collection.json&lt;/code&gt;) from an existing BSON document (&lt;code&gt;collection.bson&lt;/code&gt;), like the ones created after backing up your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSON to JSON with mongoexport
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/docs/database-tools/mongoexport" rel="noopener noreferrer"&gt;mongoexport&lt;/a&gt; is a command-line tool that produces a JSON or CSV export of data stored in a MongoDB instance. The mongoexport tool is part of the &lt;a href="https://www.mongodb.com/docs/database-tools/installation/installation/" rel="noopener noreferrer"&gt;MongoDB Database Tools&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;mongoexport&lt;/code&gt; from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongoexport --collection=employees --db=company --out=employees.json --pretty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect to a local MongoDB instance running on port 27017, you do not have to specify the host or port. If otherwise needed, check the &lt;a href="https://www.mongodb.com/docs/database-tools/mongoexport/#connect-to-a-mongodb-instance" rel="noopener noreferrer"&gt;Connect to a MongoDB Instance&lt;/a&gt; section in the documentation for more information.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--pretty&lt;/code&gt; option will pretty format the content of the JSON file.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSON to JSON with Bash
&lt;/h2&gt;

&lt;p&gt;I asked the AI at &lt;a href="//phind.com"&gt;phind.com&lt;/a&gt; to tell me how to convert a BSON file to JSON, and one of the solutions that it showed me was to create a Bash script in the directory where the BSON files are.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
declare -a bson_files
bson_files=( $(ls -d $PWD/*.bson) )

for file in "${bson_files[@]}"; 
do 
bsondump $file --outFile=$file.json
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script lists all the BSON files in the present directory and saves the result in an array, then loops through the array and converts every BSON file to JSON files. The script uses &lt;code&gt;bsondump&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To run the script&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add execution permission to the script: &lt;code&gt;chmod +x bson_to_json.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute this command in the command line:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



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

&lt;p&gt;If you want to read the content of a BSON document, you can use bsondump and mongoexport to convert a BSON document to a human-readable format like JSON. These tools are part of the &lt;a href="https://www.mongodb.com/docs/database-tools/installation/installation/" rel="noopener noreferrer"&gt;MongoDB Database Tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are other solutions like online tools, but here you learned some ways to do it.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>mongodb</category>
      <category>bash</category>
    </item>
    <item>
      <title>Local Email Testing with Python and Mailpit</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Mon, 26 Jan 2026 23:27:16 +0000</pubDate>
      <link>https://dev.to/letstalkoss/local-email-testing-with-python-and-mailpit-4cn6</link>
      <guid>https://dev.to/letstalkoss/local-email-testing-with-python-and-mailpit-4cn6</guid>
      <description>&lt;p&gt;I'm currently building an app that automates the logistics of tech conferences. It generates certificates of participation for both attendees and speakers and also takes care of sending invitations to prospective presenters. Since it emails multiple recipients, the question arises: in a development environment, how do you test email sending without using real accounts?&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to configure a fake SMTP server and run email tests for Python apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure a Local SMTP Server
&lt;/h2&gt;

&lt;p&gt;I'm using &lt;a href="https://github.com/axllent/mailpit" rel="noopener noreferrer"&gt;Mailpit&lt;/a&gt;, an Open Source email testing tool. It can be installed following the instructions in the &lt;a href="https://github.com/axllent/mailpit#installation" rel="noopener noreferrer"&gt;Installation&lt;/a&gt; section of the official repository, or by using Docker.&lt;/p&gt;

&lt;p&gt;To ensure your data survives a container restart, run the Docker container with a volume to enable persistence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d \
  --name mailpit \
  -p 1025:1025 \
  -p 8025:8025 \
  -v $(pwd)/mailpit-data:/data \
  axllent/mailpit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server listens for SMTP traffic on port 1025, while the web-based dashboard is accessible via port 8025.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Tests for Python
&lt;/h2&gt;

&lt;p&gt;Let's create a script to test our email logic. The script will perform the following tasks: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a list of random names and emails using Faker&lt;/li&gt;
&lt;li&gt;Construct MIME headers (From, To, Subject)&lt;/li&gt;
&lt;li&gt;Render the HTML body&lt;/li&gt;
&lt;li&gt;Establish and SMTP connection and transmit the data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a recipient list.
&lt;/h3&gt;

&lt;p&gt;First, we generate a list of participants.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from faker import Faker

fake = Faker('en_US')

if __name__ == "__main__":
    participants = [(fake.name(), fake.ascii_company_email()) for _ in range(10)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated data will 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;Name                      | Email                         
-------------------------------------------------------
Jessica Powell            | ryan40@atkinson.com           
Chelsey Glover            | pstevens@hurst.com            
Sheryl Williams           | kenneth61@williams-jacobson.com
Paula Boyd                | larsontheresa@dean.com        
Maxwell Kelly             | justinestrada@willis.org      
Carl Morrow               | pmorris@cross.biz             
David Webb                | abigailfields@holt.com        
Tyler Wolfe               | williamsanna@martinez.info    
Joshua Medina             | williamsrodney@medina.biz     
Mrs. Donna Butler         | williamsmartin@eaton.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Construct MIME Headers
&lt;/h3&gt;

&lt;p&gt;We use Python's built-in &lt;code&gt;email.mime&lt;/code&gt; library to structure the message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
from email.mime.multipart import MIMEMultipart

def send_simple_email(recipient_email, recipient_name):
    SENDER_EMAIL = "hello@name.com"

    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = recipient_email
    msg['Subject'] = f"Invitation: {recipient_name}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Render the HTML body
&lt;/h3&gt;

&lt;p&gt;We attach the HTML content to our MIME message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
from email.mime.text import MIMEText

def send_simple_email(recipient_email, recipient_name):
    ...

    html_body = f"""
    &amp;lt;html&amp;gt;
        &amp;lt;body style="font-family: sans-serif;"&amp;gt;
            &amp;lt;h2 style="color: #2c3e50;"&amp;gt;Hello, {recipient_name}!&amp;lt;/h2&amp;gt;
            &amp;lt;p&amp;gt;You are formally invited to participate as a speaker at our next event.&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;This is a test email captured locally by &amp;lt;strong&amp;gt;Mailpit&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
    """
    msg.attach(MIMEText(html_body, 'html'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Establish SMTP connection and transmit email data
&lt;/h3&gt;

&lt;p&gt;Finally, we connect to the local Mailpit server and send the message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import smtplib
...

def send_simple_email(recipient_email, recipient_name):
    ...

    SMTP_SERVER = "localhost"
    SMTP_PORT = 1025

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.send_message(msg)
            return True
    except Exception as e:
        print(f"❌ Error: {e}")
        return False

if __name__ == "__main__":
    ...
    print(f"\n📧 Starting email delivery to {len(participants)} recipients...")

    for name, email in participants:
        if send_simple_email(email, name):
            print(f" ✅ Sent: {email}")

    print("\n🚀 Check your emails at: http://localhost:8025")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Complete Script
&lt;/h2&gt;

&lt;p&gt;Here is the full implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import smtplib
from faker import Faker
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

fake = Faker('en_US')

def send_simple_email(recipient_email, recipient_name):
    SENDER_EMAIL = "hello@name.com"

    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = recipient_email
    msg['Subject'] = f"Invitation: {recipient_name}"

    html_body = f"""
    &amp;lt;html&amp;gt;
        &amp;lt;body style="font-family: sans-serif;"&amp;gt;
            &amp;lt;h2 style="color: #2c3e50;"&amp;gt;Hello, {recipient_name}!&amp;lt;/h2&amp;gt;
            &amp;lt;p&amp;gt;You are formally invited to participate as a speaker at our next event.&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;This is a test email captured locally by &amp;lt;strong&amp;gt;Mailpit&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
    """
    msg.attach(MIMEText(html_body, 'html'))

    SMTP_SERVER = "localhost"
    SMTP_PORT = 1025

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.send_message(msg)
            return True
    except Exception as e:
        print(f"❌ Error: {e}")
        return False

if __name__ == "__main__":
    participants = [(fake.name(), fake.ascii_company_email()) for _ in range(10)]


    print(f"\n📧 Starting email delivery to {len(participants)} recipients...")

    for name, email in participants:
        if send_simple_email(email, name):
            print(f" ✅ Sent: {email}")

    print("\n🚀 Check your emails at: http://localhost:8025")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Viewing the Results
&lt;/h2&gt;

&lt;p&gt;After running the script, navigate to &lt;a href="http://localhost:8025" rel="noopener noreferrer"&gt;http://localhost:8025&lt;/a&gt; in your browser. You will find the Mailpit dashboard with an inbox containing all the successfully intercepted test emails.&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%2F65cq9y72cs6d5uvyzgco.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%2F65cq9y72cs6d5uvyzgco.png" alt="Mailpit Dashboard" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can safely test email features before deploying to production.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>Manjaro: How to Reinstall GRUB on a BTRFS and UEFI System</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Thu, 22 Jan 2026 02:38:37 +0000</pubDate>
      <link>https://dev.to/letstalkoss/manjaro-how-to-reinstall-grub-on-a-btrfs-and-uefi-system-52nm</link>
      <guid>https://dev.to/letstalkoss/manjaro-how-to-reinstall-grub-on-a-btrfs-and-uefi-system-52nm</guid>
      <description>&lt;p&gt;I'm running Manjaro alongside Windows on a Dell XPS 13 Plus (9320), using BTRFS for the &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; mount points and a FAT32 partition for the EFI system. After an update, I lost the GRUB bootloader. In this tutorial, you'll learn how to reinstall GRUB on a BTRFS and UEFI system using a Live USB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identify Partitions
&lt;/h2&gt;

&lt;p&gt;To begin, check which partitions belong to your Manjaro installation. Run the following command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll get an output 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;NAME       MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0        7:0    0  92.9M  1 loop /run/miso/sfs/livefs
loop1        7:1    0   1.2G  1 loop /run/miso/sfs/mhwdfs
loop2        7:2    0   1.8G  1 loop /run/miso/sfs/desktopfs
loop3        7:3    0 939.3M  1 loop /run/miso/sfs/rootfs
sda          8:16   1  14.9G  0 disk /run/miso/bootmnt
sda1         8:17   1   4.2G  0 part
sda2         8:18   1     4M  0 part
nvme0n1    259:0    0 476.9G  0 disk
nvme0n1p1  259:1    0   240M  0 part
nvme0n1p2  259:2    0   128M  0 part
nvme0n1p3  259:3    0 299.2G  0 part
nvme0n1p4  259:4    0   1.2G  0 part
nvme0n1p5  259:5    0 104.3G  0 part
nvme0n1p6  259:6    0   300M  0 part
nvme0n1p7  259:7    0   1.1G  0 part
nvme0n1p8  259:8    0    52G  0 part
nvme0n1p9  259:9    0  17.1G  0 part
nvme0n1p10 259:10   0   1.4G  0 part
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, the partition layout is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nvme0n1p6 --&amp;gt; EFI system&lt;/li&gt;
&lt;li&gt;nvme0n1p8 --&amp;gt; &lt;code&gt;/&lt;/code&gt; partition&lt;/li&gt;
&lt;li&gt;nvme0n1p5 --&amp;gt; &lt;code&gt;/home&lt;/code&gt; partition&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mount the Top-level BTRFS Volume
&lt;/h2&gt;

&lt;p&gt;As Manjaro is installed on a BTRFS partition, identify which subvolumes are available in the root partition to mount the system correctly.&lt;/p&gt;

&lt;p&gt;First, mount the &lt;code&gt;/&lt;/code&gt; partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount -o subvolid=5 /dev/nvme0n1p8 /mnt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p8&lt;/code&gt; with the corresponding &lt;code&gt;/&lt;/code&gt; partition of your installation.&lt;/p&gt;

&lt;p&gt;Now, list available subvolumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo btrfs subvolume list /mnt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get an output 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;ID 256 gen 87366 top level 5 path @
ID 257 gen 87347 top level 5 path @cache
ID 258 gen 87365 top level 5 path @log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have the &lt;code&gt;/home&lt;/code&gt; directory in the same root partition, you may get the &lt;code&gt;@home&lt;/code&gt; subvolume listed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mount the Correct Root Subvolume
&lt;/h2&gt;

&lt;p&gt;Once identified, mount the correct root volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo umount /mnt
sudo mount -o subvol=@ /dev/nvme0n1p8 /mnt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p8&lt;/code&gt; with the corresponding &lt;code&gt;/&lt;/code&gt; partition of your installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Mount Points
&lt;/h2&gt;

&lt;p&gt;These folders should already exist; we are just ensuring they are present before mounting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /mnt/var/cache
sudo mkdir -p /mnt/var/log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mount the Subvolumes
&lt;/h2&gt;

&lt;p&gt;Mount the cache subvolume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount -o subvol=@cache /dev/nvme0n1p8 /mnt/var/cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p8&lt;/code&gt; with the corresponding &lt;code&gt;/&lt;/code&gt; partition of your installation.&lt;/p&gt;

&lt;p&gt;Mount the log subvolume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount -o subvol=@log /dev/nvme0n1p8 /mnt/var/log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p8&lt;/code&gt; with the corresponding &lt;code&gt;/&lt;/code&gt; partition of your installation.&lt;/p&gt;

&lt;p&gt;(Optional) Mount the separate home partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount /dev/nvme0n1p5 /mnt/home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p5&lt;/code&gt; with the corresponding &lt;code&gt;/home&lt;/code&gt; partition of your installation.&lt;/p&gt;

&lt;p&gt;Now, verify that all necessary BTRFS subvolumes are mounted and that the mount options are correct (specifically, ensure they are listed as rw for read-write). Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount | grep btrfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get an output 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;/dev/nvme0n1p8 on /mnt type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/@)
/dev/nvme0n1p8 on /mnt/var/cache type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=257,subvol=/@cache)
/dev/nvme0n1p8 on /mnt/var/log type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/@log)
/dev/nvme0n1p5 on /mnt/home type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mount the EFI Partition
&lt;/h2&gt;

&lt;p&gt;Finally mount the EFI partition by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount /dev/nvme0n1p6 /mnt/boot/efi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/dev/nvme0n1p6&lt;/code&gt; with the corresponding EFI partition of your installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bind System Directories
&lt;/h2&gt;

&lt;p&gt;Next, bind the system directories. These 'virtual' folders act as a bridge between the Live USB and your Manjaro installation, allowing the repair tools to interact with your hardware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount --bind /dev  /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys  /mnt/sys
sudo mount --bind /run  /mnt/run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Chroot and Reinstall GRUB
&lt;/h2&gt;

&lt;p&gt;Now that all subvolumes and partitions are mounted, and system directories are bound, you can access your Manjaro installation and reinstall GRUB. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chroot /mnt
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Manjaro --recheck
grub-mkconfig -o /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GRUB is reinstalled and the bootloader would appear after rebooting.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>linux</category>
    </item>
    <item>
      <title>SVG: Image Editing and Convertion with Python</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Mon, 20 Oct 2025 23:23:40 +0000</pubDate>
      <link>https://dev.to/letstalkoss/svg-image-editing-and-convertion-with-python-3482</link>
      <guid>https://dev.to/letstalkoss/svg-image-editing-and-convertion-with-python-3482</guid>
      <description>&lt;p&gt;For creating or editing images for social media, I generally use any of the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://inkscape.org/" rel="noopener noreferrer"&gt;Inkscape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gimp.org/" rel="noopener noreferrer"&gt;GIMP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://krita.org/" rel="noopener noreferrer"&gt;Krita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I usually prefer SVG over other formats, and have some templates for Instagram, that I modify whenever I have to announce a new blog post.&lt;/p&gt;

&lt;p&gt;There's a GitLab repository where I upload JSON files with the content of every new post, where there's a Python script that takes care of posting to DEV, then shares the link on a new Post on X. I needed another script for editing the following image:&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%2Fr727fcnmnyqlupau5ogc.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%2Fr727fcnmnyqlupau5ogc.png" alt="New Blog Post Template" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Editing
&lt;/h2&gt;

&lt;p&gt;In the SVG, there's a &lt;code&gt;&amp;lt;tspan&amp;gt;&lt;/code&gt; element with &lt;code&gt;id='tspan1'&lt;/code&gt; that contains the text to modify &lt;code&gt;Text&lt;/code&gt;. To edit it, you can use the &lt;code&gt;xml.etree.ElementTree&lt;/code&gt; Python module as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import subprocess
import os
import tempfile
import xml.etree.ElementTree as ET

file_name = "new-post.svg"
new title = "SVG: Image Editing and Convertion with Python"

with open(file_name, 'r', encoding='utf-8') as file:
    svg_content = file.read()

root = ET.fromstring(svg_content)

target_element = root.find(".//*[@id='tspan1']")

target_element.text = new_title

modified_svg = ET.tostring(root, encoding='unicode')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previous code block does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the content of the SVG file&lt;/li&gt;
&lt;li&gt;Treat the content of the SVG file as XML with the &lt;code&gt;ElementTree&lt;/code&gt; module&lt;/li&gt;
&lt;li&gt;Find the &lt;code&gt;&amp;lt;tspan&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;Replace the title&lt;/li&gt;
&lt;li&gt;Convert the modified XML back to string&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Convertion
&lt;/h2&gt;

&lt;p&gt;Now to convert the modified SVG to PNG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output_file = "new_post.png"
width = 1080
height = 1080

with tempfile.NamedTemporaryFile(mode='w', suffix='.svg', delete=False, encoding='utf-8') as temp_svg:
    temp_svg.write(modified_svg)
    temp_svg_path = temp_svg.name

command = [
    "inkscape",
    temp_svg_path,
    f"--export-filename={output_file}",
    f"--export-width={width}",
    f"--export-height={height}"
]

result = subprocess.run(command, check=True, capture_output=True, text=True)

os.remove(temp_svg_path)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previous code does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a temporary SVG file with the content of the &lt;code&gt;modified_svg&lt;/code&gt; variable&lt;/li&gt;
&lt;li&gt;Run Inkscape from the command line to convert the file to PNG&lt;/li&gt;
&lt;li&gt;Remove the temporary file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you run previous script, you will get the following picture in the same directory:&lt;br&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%2Fukohwhyouyghmlohmnc9.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%2Fukohwhyouyghmlohmnc9.png" alt="New Blog Post Template" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>Local Firestore Development with the gcloud Emulator</title>
      <dc:creator>Mario García</dc:creator>
      <pubDate>Mon, 13 Oct 2025 07:13:27 +0000</pubDate>
      <link>https://dev.to/mattdark/local-firestore-development-with-the-gcloud-emulator-52bl</link>
      <guid>https://dev.to/mattdark/local-firestore-development-with-the-gcloud-emulator-52bl</guid>
      <description>&lt;p&gt;Worked on a &lt;a href="https://gitlab.com/mattdark/pharmacare" rel="noopener noreferrer"&gt;dashboard for a pharmacy&lt;/a&gt;, deployed on GitLab Pages, for which I chose the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Cloud Firestore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Previously used Firestore to enable &lt;a href="https://blowfish.page/docs/firebase-views/" rel="noopener noreferrer"&gt;Views and Likes&lt;/a&gt; for &lt;a href="https://blowfish.page" rel="noopener noreferrer"&gt;Blowfish&lt;/a&gt;, a Hugo theme I configured for my website. This time I needed a free cloud database solution to store information about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locations&lt;/li&gt;
&lt;li&gt;Medicines&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;li&gt;Sales&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the free Spark Plan of Firebase, you can't create a second database within the same project if you're planning to use it for running tests against it instead of the default database. One workaround is to create a separate project, destined to be used for testing.&lt;/p&gt;

&lt;p&gt;A better solution would be to use the Firestore Emulator, intended to use for local testing, provided by the &lt;a href="https://cloud.google.com/sdk" rel="noopener noreferrer"&gt;Google Cloud CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Google Cloud CLI
&lt;/h2&gt;

&lt;p&gt;Follow the instructions from the &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On Linux, download the corresponding file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the contents of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tar -xf google-cloud-cli-linux-x86_64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the installation script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./google-cloud-sdk/install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update your gcloud CLI installation to get the latest features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud components update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Backup your database
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a directory to store the backup and credentials.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir local-firestore
cd local-firestore
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;firestore-backfire&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g firestore-backfire
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get Service Account credentials.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Firebase Console → Project Settings → Service Accounts.&lt;/li&gt;
&lt;li&gt;Click Generate new private key and download the JSON file and rename it as &lt;code&gt;credentials.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy the file to the directory created above.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export all documents from specific collections to a local &lt;code&gt;.ndjson&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backfire export ./data.ndjson \
  -P your-project-id \
  -K ./credentials.json \
  --paths users products
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The name assigned to the backup of your database is &lt;code&gt;data.ndjson&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Where &lt;code&gt;your-project-id&lt;/code&gt; is the ID of your project that you can get from the Firebase console.&lt;/p&gt;

&lt;p&gt;The Service Account credentails are in the &lt;code&gt;credentials.json&lt;/code&gt; file downloaded before.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;users&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt; are the collections being backed up. Replace with the collections in your database.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run the emulator
&lt;/h2&gt;

&lt;p&gt;Run the emulator with the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud emulators firestore start --host-port=127.0.0.1:8304
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restore your database
&lt;/h2&gt;

&lt;p&gt;To restore your database from the &lt;code&gt;data.ndjson&lt;/code&gt; file, run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backfire import ./data.ndjson \
  -E 127.0.0.1:8304 \
  --mode overwrite \
  -P your-project-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update your app
&lt;/h2&gt;

&lt;p&gt;I have a &lt;code&gt;.env.local&lt;/code&gt; file with the following variables set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_FIREBASE_API_KEY=""
VITE_FIREBASE_AUTH_DOMAIN=""
VITE_FIREBASE_PROJECT_ID=""
VITE_FIREBASE_STORAGE_BUCKET=""
VITE_FIREBASE_MESSAGING_SENDER_ID=""
VITE_FIREBASE_APP_ID=""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added the following ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_USE_EMULATOR=true
VITE_FIRESTORE_EMULATOR_HOST=127.0.0.1
VITE_FIRESTORE_EMULATOR_PORT=8304
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the &lt;code&gt;firebase.ts&lt;/code&gt; file, import &lt;code&gt;connectFirestoreEmulator&lt;/code&gt; from &lt;code&gt;firebase/firestore&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  getFirestore,
  Firestore,
  collection,
  doc,
  getDoc,
  setDoc,
  updateDoc,
  deleteDoc,
  query,
  where,
  getDocs,
  onSnapshot,
  writeBatch,
  connectFirestoreEmulator
} from 'firebase/firestore';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    app = initializeApp(firebaseConfig);
    auth = getAuth(app);
    db = getFirestore(app);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added the following lines to validate when the project is running locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if (import.meta.env.VITE_USE_EMULATOR === 'true') {
      const EMULATOR_HOST = import.meta.env.VITE_FIRESTORE_EMULATOR_HOST || "127.0.0.1";
      const EMULATOR_PORT = Number(import.meta.env.VITE_FIRESTORE_EMULATOR_PORT) || 8304;

      connectFirestoreEmulator(db, EMULATOR_HOST, EMULATOR_PORT);
      console.log(`Firebase.ts: Connected to Firestore Emulator at http://${EMULATOR_HOST}:${EMULATOR_PORT}`);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when the app is run locally, it will connect to the Firestore emulator.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>firestore</category>
      <category>firebase</category>
      <category>gcloud</category>
    </item>
  </channel>
</rss>
