<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>landgraf.dev Blog</title>
        <link>https://www.landgraf.dev/en</link>
        <description>landgraf.dev Blog</description>
        <lastBuildDate>Wed, 02 Apr 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Que tal um evento sobre Delphi na sua cidade?]]></title>
            <link>https://www.landgraf.dev/en/delphi-na-estrada-delphi-event-in-brazil</link>
            <guid>delphi-na-estrada-delphi-event-in-brazil</guid>
            <pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Delphi na Estrada é o mais abrangente evento de Delphi do Brasil, acontecendo em maio nas cidades de Porto Alegre, Chapecó, Curitiba, Campinas, Rio de Janeiro, Teresópolis, Brasília e Fortaleza]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.delphinaestrada.com.br" target="_blank" rel="noopener noreferrer"><img loading="lazy" alt="delphi-na-estrada" src="/assets/images/delphi-na-estrada-og-ebac3201757f4784994b90d3a593ec0e.png" width="1200" height="630" class="img_ev3q"></a></p><p>O <a href="https://www.delphinaestrada.com.br" target="_blank" rel="noopener noreferrer"><strong>Delphi na Estrada 2025</strong></a> está chegando para movimentar a comunidade Delphi em todo o Brasil! Durante o mês de <strong>maio de 2025</strong>, o Delphi na Estrada vai passar por <strong>oito cidades diferentes</strong> (Porto Alegre, Chapecó, Curitiba, Campinas, Rio de Janeiro, Teresópolis, Brasília e Fortaleza), oferecendo palestras e atividades de alto nível para desenvolvedores de todos os níveis — de iniciantes a veteranos da linguagem.</p><p>Com uma programação diversificada e <strong>palestrantes renomados</strong>, incluindo <strong>Embarcadero MVPs</strong>, desenvolvedores de bibliotecas populares, consultores especializados e até convidados internacionais, o Delphi na Estrada promete ser uma experiência única em cada local. Cada cidade contará com temas e profissionais diferentes, garantindo conteúdos valiosos e trocas enriquecedoras.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="o-que-esperar-do-delphi-na-estrada-2025">O que esperar do Delphi na Estrada 2025?<a class="hash-link" href="#o-que-esperar-do-delphi-na-estrada-2025" title="Direct link to heading">​</a></h2><p>O Delphi na Estrada traz objetivos importantes para quem vive o desenvolvimento com Delphi:</p><ul><li><strong>Promover a linguagem Delphi:</strong> Discutir sua evolução, tendências atuais e previsões para o futuro.</li><li><strong>Compartilhar conhecimentos técnicos:</strong> Apresentar técnicas inovadoras, dicas valiosas e ferramentas que facilitam o desenvolvimento.</li><li><strong>Fortalecer a comunidade Delphi:</strong> Proporcionar networking, aprendizado e troca de experiências entre desenvolvedores de diversas regiões.</li><li><strong>Conectar especialistas e iniciantes:</strong> Criar um ambiente colaborativo para que todos possam crescer e aprender juntos.</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="programação-e-conteúdo">Programação e Conteúdo<a class="hash-link" href="#programação-e-conteúdo" title="Direct link to heading">​</a></h2><p>Das <strong>13h às 18h</strong> em cada cidade, o Delphi na Estrada vai oferecer um conteúdo técnico relevante e inspirador, incluindo:</p><ul><li><strong>A história e a evolução do Delphi:</strong> Como a linguagem se desenvolveu ao longo dos anos e o que vem pela frente.</li><li><strong>Técnicas modernas e práticas avançadas:</strong> Conhecimentos aplicáveis que facilitam o dia a dia do desenvolvedor.</li><li><strong>Casos reais de sucesso:</strong> Exemplos práticos de como o Delphi é utilizado para resolver desafios complexos.</li><li><strong>Dicas de especialistas:</strong> Truques e insights para quem quer levar suas habilidades a outro nível.</li><li><strong>Networking com a comunidade:</strong> Conheça outros desenvolvedores, compartilhe experiências e faça contatos valiosos.</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="onde-o-delphi-na-estrada-vai-passar">Onde o Delphi na Estrada vai passar?<a class="hash-link" href="#onde-o-delphi-na-estrada-vai-passar" title="Direct link to heading">​</a></h2><p>Em maio, o Delphi na Estrada percorre <strong>oito cidades brasileiras</strong>. Escolha a mais próxima de você:</p><ul><li><a href="https://www.delphinaestrada.com.br/porto-alegre" target="_blank" rel="noopener noreferrer">Porto Alegre – RS: 12 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/chapeco" target="_blank" rel="noopener noreferrer">Chapecó – SC: 14 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/curitiba" target="_blank" rel="noopener noreferrer">Curitiba – PR: 16 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/campinas" target="_blank" rel="noopener noreferrer">Campinas – SP: 19 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/rio-de-janeiro" target="_blank" rel="noopener noreferrer">Rio de Janeiro – RJ: 20 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/teresopolis" target="_blank" rel="noopener noreferrer">Teresópolis – RJ: 21 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/brasilia" target="_blank" rel="noopener noreferrer">Brasília – DF: 22 de maio</a>  </li><li><a href="https://www.delphinaestrada.com.br/fortaleza" target="_blank" rel="noopener noreferrer">Fortaleza – CE: 23 de maio</a>  </li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="por-que-participar">Por que participar?<a class="hash-link" href="#por-que-participar" title="Direct link to heading">​</a></h2><p>O Delphi na Estrada é uma oportunidade única para desenvolvedores de todas as regiões do Brasil. Além de ser <strong>totalmente gratuito</strong>, ele oferece:</p><ul><li><strong>Proximidade geográfica:</strong> Sem precisar se deslocar para longe, você pode participar do evento na sua própria cidade ou em uma cidade próxima.</li><li><strong>Palestrantes renomados:</strong> Especialistas nacionais e internacionais, muitos deles <strong>Embarcadero MVPs</strong>, trazendo conteúdos relevantes e atualizados.</li><li><strong>Networking:</strong> Interação com outros desenvolvedores, consultores e líderes do mercado.</li><li><strong>Conteúdo técnico de alto nível:</strong> Conhecimentos valiosos que podem ser aplicados imediatamente nos seus projetos.</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="inscreva-se-e-participe-do-delphi-na-estrada">Inscreva-se e participe do Delphi na Estrada!<a class="hash-link" href="#inscreva-se-e-participe-do-delphi-na-estrada" title="Direct link to heading">​</a></h2><p>As vagas são limitadas e a inscrição é gratuita! Para garantir seu lugar, acesse o site oficial e escolha a cidade mais próxima de você:</p><p><a href="https://www.delphinaestrada.com.br" target="_blank" rel="noopener noreferrer"><strong>https://www.delphinaestrada.com.br</strong></a></p><p>Não perca essa chance de aprimorar seus conhecimentos, fazer networking e se inspirar com o que há de melhor na comunidade Delphi!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing AWS S3 Support in AWS SDK for Delphi]]></title>
            <link>https://www.landgraf.dev/en/aws-s3-support-in-aws-sdk-for-delphi</link>
            <guid>aws-s3-support-in-aws-sdk-for-delphi</guid>
            <pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Support for AWS S3 has been introduced in the AWS SDK for Delphi. This new addition brings the powerful capabilities of AWS S3 directly to Delphi developers]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="aws-s3" src="/assets/images/aws-s3-f20e86a3be4820b0b6a2661416b60f8f.png" width="1950" height="656" class="img_ev3q"></p><p><a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener noreferrer">AWS Simple Storage Service (S3)</a> is one of the most popular services provided by Amazon Web Services. It offers scalable, secure, and reliable cloud storage for all kinds of data, ranging from media files to backups. AWS S3 is designed to store data in "buckets," which are containers that hold objects (files) along with metadata. S3 provides essential features like versioning, encryption, and access control, making it an excellent choice for developers who need a versatile and easy-to-use storage solution.</p><p>Support for AWS S3 has been introduced in the <a href="https://github.com/landgraf-dev/aws-sdk-delphi" target="_blank" rel="noopener noreferrer">AWS SDK for Delphi</a>. This new addition brings the powerful capabilities of AWS S3 directly to Delphi developers, allowing you to interact with your cloud storage from your Delphi applications seamlessly. Whether you need to upload files, list objects, or manage buckets, it's all now possible with the familiar Delphi syntax you know and love.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="key-features-of-aws-s3-in-aws-sdk-for-delphi">Key Features of AWS S3 in AWS SDK for Delphi<a class="hash-link" href="#key-features-of-aws-s3-in-aws-sdk-for-delphi" title="Direct link to heading">​</a></h3><ul><li><strong>Bucket Management</strong>: Easily create, delete, and list S3 buckets.</li><li><strong>Object Operations</strong>: Upload, download, delete, and list objects in your S3 buckets.</li><li><strong>Access Control</strong>: Manage permissions to control who can access your data.</li><li><strong>Advanced Configurations</strong>: Leverage options like server-side encryption and metadata tagging for your objects.</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="uploading-a-file-to-s3">Uploading a File to S3<a class="hash-link" href="#uploading-a-file-to-s3" title="Direct link to heading">​</a></h3><p>This section demonstrates how to upload a file to AWS S3 using the AWS SDK for Delphi:</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">uses</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  AWS.S3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">procedure UploadFileToS3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client: IAmazonS3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: IPutObjectRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName, Content, ObjectKey: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client := TAmazonS3Client.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName := 'my-bucket';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ObjectKey := 'TestObject';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Content := 'Sample Content';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TPutObjectRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.BucketName := BucketName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.ContentBody := Content;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Key := ObjectKey;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    S3Client.PutObject(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Writeln('File uploaded successfully!');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  except</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    on E: Exception do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Writeln('Error uploading file: ', E.Message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="getting-an-object-from-s3">Getting an Object from S3<a class="hash-link" href="#getting-an-object-from-s3" title="Direct link to heading">​</a></h3><p>This section explains how you can retrieve an object from an S3 bucket using the AWS SDK for Delphi:</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure GetObjectFromS3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client: IAmazonS3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetObjectRequest: IGetObjectRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetObjectResponse: IGetObjectResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName, ObjectKey: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Reader: TStreamReader;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client := TAmazonS3Client.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName := 'my-bucket';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ObjectKey := 'test.txt';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetObjectRequest := TGetObjectRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetObjectRequest.BucketName := BucketName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetObjectRequest.Key := ObjectKey;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    GetObjectResponse := S3Client.GetObject(GetObjectRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Reader := TStreamReader.Create(GetObjectResponse.ResponseStream);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Writeln('Object Content: ', Reader.ReadToEnd);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    finally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Reader.Free;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  except</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    on E: Exception do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Writeln('Error getting object: ', E.Message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="generating-a-pre-signed-url">Generating a Pre-Signed URL<a class="hash-link" href="#generating-a-pre-signed-url" title="Direct link to heading">​</a></h3><p>This section demonstrates how to generate a pre-signed URL for an object stored in an S3 bucket, allowing temporary access to the object:</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure GeneratePreSignedUrl;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client: IAmazonS3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: IGetPreSignedUrlRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName, ObjectKey: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Expires: TDateTime;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Url: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  S3Client := TAmazonS3Client.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  BucketName := 'my-bucket';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ObjectKey := 'test.txt';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Expires := Now + 1; // URL expires in 1 day</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TGetPreSignedUrlRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.BucketName := BucketName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Key := ObjectKey;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Expires := Expires;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Url := S3Client.GetPreSignedURL(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Writeln('Generated Pre-Signed URL: ', Url);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  except</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    on E: Exception do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Writeln('Error generating Pre-Signed URL: ', E.Message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>In this example, we create an instance of <code>TS3Client</code> and use it to upload a local file to an S3 bucket. The AWS credentials and region are automatically retrieved, similar to how it is done in the AWS SDK for .NET, from the .aws configuration file or environment variables.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="example-deleting-a-bucket-with-all-its-objects">Example: Deleting a Bucket with All Its Objects<a class="hash-link" href="#example-deleting-a-bucket-with-all-its-objects" title="Direct link to heading">​</a></h3><p>Here’s an example of how to delete an S3 bucket along with all its objects, including different versions of objects. This can be useful when you need to completely remove a bucket and ensure all of its contents are deleted first.</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">class procedure TS3TestUtils.DeleteS3BucketWithObjects(S3Client: IAmazonS3; const BucketName: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const DeleteOptions: TS3DeleteBucketWithObjectsOptions);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var listVersionsRequest: IListVersionsRequest := TListVersionsRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  listVersionsRequest.BucketName := BucketName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var listVersionsResponse: IListVersionsResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  repeat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // List all the versions of all the objects in the bucket.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    listVersionsResponse := S3Client.ListVersions(listVersionsRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    if listVersionsResponse.Versions.Count = 0 then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Break;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var keyVersionList := TObjectList&lt;TKeyVersion&gt;.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      for var index := 0 to listVersionsResponse.Versions.Count - 1 do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var keyVersion := TKeyVersion.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        keyVersionList.Add(keyVersion);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        keyVersion.Key := listVersionsResponse.Versions[index].Key;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        keyVersion.VersionId := listVersionsResponse.Versions[index].VersionId;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        // Delete the current set of objects.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var deleteObjectsRequest: IDeleteObjectsRequest := TDeleteObjectsRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        deleteObjectsRequest.BucketName := BucketName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        deleteObjectsRequest.Objects := keyVersionList;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        deleteObjectsRequest.KeepObjects := True;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        deleteObjectsRequest.Quiet := DeleteOptions.QuietMode;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var deleteObjectsResponse := S3Client.DeleteObjects(deleteObjectsRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      except</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        on DeleteObjectsException: EDeleteObjectsException do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          if DeleteOptions.ContinueOnError then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            // Continue the delete operation if an error was encountered.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            // Re-throw the exception if an error was encountered.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            raise;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    finally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      keyVersionList.Free;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // Set the markers to get next set of objects from the bucket.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    listVersionsRequest.KeyMarker := listVersionsResponse.NextKeyMarker;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    listVersionsRequest.VersionIdMarker := listVersionsResponse.NextVersionIdMarker;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  until listVersionsResponse.IsTruncated;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var maxRetries := 10;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  for var retries := 1 to maxRetries do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      // Bucket is empty, delete the bucket.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      S3Client.DeleteBucket(BucketName);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Break;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    except</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      on E: EAmazonS3Exception do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if (E.StatusCode &lt;&gt; 409) or (retries = maxRetries) then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          raise</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          TDefaultRetryPolicy.DoWaitBeforeRetry(retries, 5000);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>This example shows how to list and delete all object versions in a bucket before deleting the bucket itself, ensuring that all resources are properly removed.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a class="hash-link" href="#conclusion" title="Direct link to heading">​</a></h3><p>The addition of AWS S3 support to AWS SDK for Delphi opens up a range of possibilities for Delphi developers who want to leverage cloud storage in their applications. From managing buckets to working with objects, you can now build more versatile and cloud-connected solutions using Delphi.</p><p>We hope you're as excited as we are about this new feature. Feel free to explore the capabilities and start integrating AWS S3 into your Delphi applications today!</p><p>If you have any questions or need help getting started, let's <a href="https://forum.landgraf.dev/t/introducing-aws-s3-support-in-aws-sdk-for-delphi/369" target="_blank" rel="noopener noreferrer">discuss about this article in our forum!</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AWS SDK for Delphi: cross-platform and open-source]]></title>
            <link>https://www.landgraf.dev/en/aws-sdk-for-delphi-open-source</link>
            <guid>aws-sdk-for-delphi-open-source</guid>
            <pubDate>Wed, 27 Dec 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[AWS SDK for Delphi is now cross-platform, open-source 100% native Delphi code]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="aws-buliding" src="/assets/images/aws-buliding-3a7f87578483d6c98a06045510595a4f.jpg" width="615" height="303" class="img_ev3q"></p><p>The AWS SDK for Delphi has reached a major milestone:</p><p><strong><a href="https://github.com/landgraf-dev/aws-sdk-delphi" target="_blank" rel="noopener noreferrer">AWS SDK for Delphi GitHub Repository</a></strong></p><p><strong><a href="https://github.com/landgraf-dev/aws-sdk-delphi-samples" target="_blank" rel="noopener noreferrer">AWS SDK for Delphi Samples GitHub Repository</a></strong></p><p>It now supports <strong>Delphi 12, Delphi 11, Delphi 10.4.3 Sydney and Delphi 10.4.2 Rio</strong>, and supports all platforms available in those Delphi versions, which means <strong>Windows 32-bit and 64-bit, Linux, Android, iOS and macOS</strong>.</p><p>And more important, we removed the dependency on 3rd party libraries and it is now <strong>fully open-source written in 100% native Delphi code</strong>. It's available to everyone, for all Delphi editions.</p><p>We have <a href="/en/aws-amazon-web-services-sdk-for-delphi">written about the SDK in a previous blog post</a>, but here is a quick overview about it.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="about-amazon-web-services-and-the-sdk-for-delphi">About Amazon Web Services and the SDK for Delphi<a class="hash-link" href="#about-amazon-web-services-and-the-sdk-for-delphi" title="Direct link to heading">​</a></h2><p>The <strong>AWS SDK for Delphi</strong> enables Delphi developers to easily work with <a href="https://aws.amazon.com/" target="_blank" rel="noopener noreferrer">Amazon Web Services</a> and build scalable solutions with <a href="https://aws.amazon.com/ses/" target="_blank" rel="noopener noreferrer">Amazon SES</a>, <a href="https://aws.amazon.com/sqs/" target="_blank" rel="noopener noreferrer">Amazon SQS</a>, and more.</p><p>Each Amazon web service has its own package and unit name scheme, which is AWS &lt; service &gt; .dproj and AWS.&lt; service &gt;.*.pas, respectively. For example, for Amazon SQS (Simple Queue Service), the package name is AWSSQS.dproj and unit name is AWS.SQS.pas (and all other units in the package follow same pattern, like AWS.SQS.Client.pas or AWS.SQS.ClientIntf.pas.</p><p>Most types you need will be in the main unit, which for example is AWS.SES. So that’s the only unit you will need to use most of the functions. From there you can access all the available API operations. Each operation method receives a request interface and returns a response interface.</p><p>The following example sends an e-mail to the specified address using the specified subject and message:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// 1. Use main unit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">uses AWS.SES;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">procedure SendEmail(const Recipient, Subject, Content: string);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IAmazonSimpleEmailService;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: ISendEmailRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response: ISendEmailResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 2. Instantiate client interface</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TAmazonSimpleEmailServiceClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 3. Create and fill the request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TSendEmailRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Source := SenderEmail; </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Destination := TDestination.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Destination.ToAddresses.Add(Recipient);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Message := TMessage.Create(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    TContent.Create(Subject),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    TBody.Create(TContent.Create(Content)));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 4. Call operation method passing the request to receive the response;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response := Client.SendEmail(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 5. Process the response</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  WriteLn(Response.MessageId);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="credentials">Credentials<a class="hash-link" href="#credentials" title="Direct link to heading">​</a></h2><p>The AWS SDK for Delphi searches for credentials in a certain order and uses the first available set for the current application, and follows the AWS conventions for credentials. It can retrieve credentials in several ways:</p><ol><li>Credentials that are explicitly set on the AWS service client, as described in below</li><li>A credentials profile with the name specified by a value in TAWSConfigs.AWSProfileName.</li><li>A credentials profile with the name specified by the AWS_PROFILE environment variable.</li><li>The <!-- -->[default]<!-- --> credentials profile.</li></ol><p>AWS documentation <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html" target="_blank" rel="noopener noreferrer">explains about credentials profiles</a> and <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html" target="_blank" rel="noopener noreferrer">how AWS retrieves credentials</a>.</p><p>The SDK also supports <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html" target="_blank" rel="noopener noreferrer">EC2 instance profile credentials</a>.</p><p>Passing access and secret keys directly to client:</p><p>You can simply pass the Access key ID and Secret key directly in the client constructor:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    Client := TAmazonSQSClient.Create(myAccessKey, mySecretKey);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Although using credentials profile is recommended as it’s easier to manage and also compatible with <a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener noreferrer">AWS Command Line Interface</a>.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="enjoy-and-comment">Enjoy and comment!<a class="hash-link" href="#enjoy-and-comment" title="Direct link to heading">​</a></h2><p>Thanks for being with me so far. AWS SDK was born to grow and make the whole AWS world accessible to you. It is <a href="https://faircode.io/" target="_blank" rel="noopener noreferrer">fair-code</a> distributed under <a href="https://github.com/landgraf-dev/aws-sdk-delphi/blob/master/LICENSE" target="_blank" rel="noopener noreferrer">Apache 2.0 with Commons Clause</a> license.</p><p>(*) AWS building photo by <a href="https://www.flickr.com/photos/diversey/46600198075/in/photostream/" target="_blank" rel="noopener noreferrer">Tony Webster</a>. Original picture was cropped to better fit in the page.</p><p><a href="https://forum.landgraf.dev/t/aws-amazon-web-services-sdk-for-delphi-cross-platform-and-open-source/362" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OpenAI ChatGPT API Client For Delphi and Lazarus]]></title>
            <link>https://www.landgraf.dev/en/openai-chatgpt-api-client-delphi-lazarus</link>
            <guid>openai-chatgpt-api-client-delphi-lazarus</guid>
            <pubDate>Wed, 11 Jan 2023 08:35:00 GMT</pubDate>
            <description><![CDATA[Use ChatGPT incredible chatbot directly from your Delphi or Lazarus application.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="OpenAI ChatGPT" src="/assets/images/pexels-tara-winstead-8386434-524311a5ff173d575a4346a9de7eb0ac.jpg" width="900" height="381" class="img_ev3q"></p><p>Welcome to our blog post on using the <a href="https://beta.openai.com/docs/api-reference/chat" target="_blank" rel="noopener noreferrer">ChatGPT API</a> with Delphi!</p><p>Have you ever wanted to incorporate the power of state-of-the-art language processing into your Delphi applications? <a href="https://beta.openai.com/docs/models/chat" target="_blank" rel="noopener noreferrer">ChatGPT</a>, developed by <a href="https://openai.com/" target="_blank" rel="noopener noreferrer">OpenAI</a>, is a cutting-edge language model that allows for natural language communication with a conversational AI. And with the <a href="https://beta.openai.com/docs/api-reference/chat" target="_blank" rel="noopener noreferrer">ChatGPT API</a>, you can easily use this technology in your Delphi projects.</p><p>In this blog post, we'll explore how to use the ChatGPT API with Delphi to build chatbots and other language-based applications. We'll go over the basics of setting up the API and making requests.</p><p>So if you're a Delphi developer looking to add the power of natural language processing to your projects, this blog post is for you! We'll show you how to get started with ChatGPT and the Delphi API and provide you with the tools and knowledge you need to take your first steps in using this powerful technology.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="openai-delphi-and-lazarus-client">OpenAI Delphi and Lazarus client<a class="hash-link" href="#openai-delphi-and-lazarus-client" title="Direct link to heading">​</a></h2><p>OpenAI for Delphi is a client library to connect to the OpenAI API from Delphi and Lazarus/FPC (Free Pascal Compiler) applications. It does not require any 3rd-party library. It works on all recent Delphi versions that provides unit (<code>System.Net.HttpClient</code>) and Lazarus/FPC. Although not fully tested, it should also work on all supported platforms (Windows, Linux, macOS, etc.).</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="installation">Installation<a class="hash-link" href="#installation" title="Direct link to heading">​</a></h3><p>You can simply clone the source code from the <a href="https://github.com/landgraf-dev/openai-delphi" target="_blank" rel="noopener noreferrer"><strong>OpenAI for Delphi official repository at GitHub</strong></a> and follow the installation instructions there:</p><p>Official repository: <a href="https://github.com/landgraf-dev/openai-delphi" target="_blank" rel="noopener noreferrer">https://github.com/landgraf-dev/openai-delphi</a> </p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="usage">Usage<a class="hash-link" href="#usage" title="Direct link to heading">​</a></h3><p>The library needs to be configured with your account's secret API key, which is available on the <a href="https://beta.openai.com/account/api-keys" target="_blank" rel="noopener noreferrer">website</a>. We recommend setting it as an environment variable named <code>OPENAI_API_KEY</code>. Once you have the API key, just create the client and set the key as the following:</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IOpenAIClient;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{...}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TOpenAIClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.Config.AccessToken := GetEnvironmentVariable('OPENAI_API_KEY');</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="executing-methods">Executing methods<a class="hash-link" href="#executing-methods" title="Direct link to heading">​</a></h3><p>Most of <a href="https://beta.openai.com/docs/api-reference" target="_blank" rel="noopener noreferrer">OpenAI API endpoints</a> are available in the client, under the interface provided in the <code>OpenAI</code> property. The following example shows how to ask a question and receive an answer using the CreateCompletion endpoint:</p><div class="language-delphi codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-delphi codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">function AskQuestion(const Question: string): string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: TCreateCompletionRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response: TCreateCompletionResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response := nil;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TCreateCompletionRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Request.Prompt := Question;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Request.Model := 'text-davinci-003';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Request.MaxTokens := 2048; // Be careful as this can quickly consume your API quota.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Response := Client.OpenAI.CreateCompletion(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    if Assigned(Response.Choices) and (Response.Choices.Count &gt; 0) then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Result := Response.Choices[0].Text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Result := '';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  finally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Request.Free;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Response.Free;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-the-demo">Using the demo<a class="hash-link" href="#using-the-demo" title="Direct link to heading">​</a></h2><p>The repository provides a simple demo console application that you can use to make questions and receive answers from ChatGPT. All you need is to <a href="https://beta.openai.com/account/api-keys" target="_blank" rel="noopener noreferrer">create your access key at OpenAI</a> and provide it to the console application, either manually or by creating the <code>OPENAI_API_KEY</code> environment variable.</p><p>The demo is cross-platform, so we used ran it on Linux and asked ChatGPT a few paramount questions:</p><p><img loading="lazy" alt="OpenAI Linux Demo" src="/assets/images/openai-linuxdemo-chat-40c997f1397d582780ed36f7cb79388d.png" width="997" height="1029" class="img_ev3q"></p><p>In case you prefer the text transcription, here it is:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">user@delphi-ubuntu:~/PAServer/scratch-dir/user-Delphi-Ubuntu/ChatGPT$ ./ChatGPT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">OpenAI ChatGPT Sample Application version 1.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Using OpenAI API Client for Delphi and Lazarus/FPC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">https://github.com/landgraf.dev/openai-delphi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Copyright (c) Landgraf.dev - all rights reserved.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">API Key loaded from environment variable OPENAI_API_KEY.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: What can I use to develop software using Pascal?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">You can use a variety of development environments and compilers to develop software using Pascal. Popular choices include Embarcadero's Delphi and Free Pascal. Additionally, the Lazarus IDE is a free and open source option available for a variety of platforms. For a full list of software available, visit http://www.freepascal.org/development.html.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: Can you please tell me more about Embacadero's Delphi and Free Pascal then?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Delphi is a general-purpose programming language and software development tool created by Embarcadero Technologies. It helps developers create native Windows, macOS, iOS, and Android applications using the same source code. Delphi’s language is Object Pascal, a descendant of the procedural language Pascal. Free Pascal is a GPL-licensed compiler for the closely related Object Pascal language. It is open-source, and its latest version supports Windows, macOS, and Linux for 32-bit and 64-bit development. It is primarily used to create Pascal-based applications, making it somewhat behind the more powerful Delphi.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: Do you have any information about "Landgraf.dev"? I read somewhere they have a blog about software development with Pascal (at https://landgraf.dev) and a training courses site (https://courses.landgraf.dev). What can you tell me about it?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Landgraf.dev is a blog and educational platform that focuses on software development with Pascal. It contains tutorials and articles with step-by-step instructions on how to create programs in the language. The blog's main goal is to provide a working knowledge of Pascal in a logical and easy-to-understand way. The courses section on the platform features paid courses covering object-oriented programming, debugging, working with databases, and more. Additionally, they offer free Pascal programming practice exercises and free downloadables related to the language.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: Ok, thank you for your answers. Anything else I should know about Pascal software development in general?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Pascal software development is quite versatile and can be used for many different purposes. To get the most out of it, it is important to be familiar with the language and its features, including object-oriented programming and generics. Additionally, it is also important to have a good knowledge of data structures, algorithms, and debugging. Finally, a good understanding of the various libraries and frameworks available can help streamline your development process.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: Can you then provide the source code of a minimal application written in Pascal so I have an idea about what does it look like?</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">program HelloWorld;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Writeln('Hello World!');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: Thank you very much for your help.   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Answer: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">It was my pleasure.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Write your question: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Goodbye.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">user@delphi-ubuntu:~/PAServer/scratch-dir/user-Delphi-Ubuntu/ChatGPT$ </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Smart guy, isn't it?</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-sky-is-the-limit">The sky is the limit<a class="hash-link" href="#the-sky-is-the-limit" title="Direct link to heading">​</a></h2><p>Now, it's up to you to check if and how using ChatGPT and other OpenAI technologies will be useful in your applications. The use cases are many, and actually I'm sure that with some creativity you can find some original uses for it.</p><p>The "traditional" uses I can think of are using it to help support, answer frequently asked question, fix text typos and grammar errors, give suggestions about how to do something, among others. Take into account that you can train your model with specific information you might have.</p><p>Oh, and by the way, here is another use case: writing blog posts! Maybe you have noticed due to the different style, but in case you haven't: the first four paragraphs of this blog post were written 100% by ChatGPT. I asked it to write it for me. Even though the very first version was not exactly what I wanted, I was too lazy to fix it myself, adding hyperlinks, so I just asked it to improve it. I used the web version, and here are the screenshots of the interactions:</p><p><img loading="lazy" alt="OpenAI Chat Step 1" src="/assets/images/openai-web-chat1-335e70d98b54911dc782f35820a96869.png" width="900" height="813" class="img_ev3q"></p><p><img loading="lazy" alt="OpenAI Chat Step 2" src="/assets/images/openai-web-chat2-82d06985c8252d0090d2904a9118112d.png" width="900" height="638" class="img_ev3q"></p><p><img loading="lazy" alt="OpenAI Chat Step 3" src="/assets/images/openai-web-chat3-a360c1d824bf7ff461f1ee4c6d7039c9.png" width="900" height="581" class="img_ev3q"></p><p><img loading="lazy" alt="OpenAI Chat Step 4" src="/assets/images/openai-web-chat4-f3b404c53f7dfb049665780718aebbc1.png" width="900" height="591" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-now">What now?<a class="hash-link" href="#what-now" title="Direct link to heading">​</a></h2><p>Well, again, feel free to use the OpenAI API client in your applications, use it from the GitHib repo: <a href="https://github.com/landgraf-dev/openai-delphi" target="_blank" rel="noopener noreferrer">https://github.com/landgraf-dev/openai-delphi</a>.</p><p>You are also welcome to <a href="https://forum.landgraf.dev/t/openai-chatgpt-api-client-for-delphi-and-lazarus/346" target="_blank" rel="noopener noreferrer">discuss about this article in our forum</a>. Let us know your thoughts and ideas about this new exciting technology!</p><p>(*) Photo by <a href="https://www.pexels.com/photo/person-reaching-out-to-a-robot-8386434/" target="_blank" rel="noopener noreferrer">Tara Winstead</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[High DPI support in Delphi 11, and why it does matter]]></title>
            <link>https://www.landgraf.dev/en/high-dpi-support-in-delphi-11-and-why-does-it-matter</link>
            <guid>high-dpi-support-in-delphi-11-and-why-does-it-matter</guid>
            <pubDate>Wed, 08 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Delphi 11 (and RAD Studio 11) was released. See what I think about it.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="delphi-splash" src="/assets/images/delphi-splash-ae7d60dc6ec474fead5abaf4e2cfc78c.jpg" width="750" height="264" class="img_ev3q"></p><p><strong>Delphi 11</strong> (and <strong>RAD Studio 11</strong>) is just around the corner! It’s been <a href="https://blogs.embarcadero.com/rad-studio-11-is-coming-new-version-announcement-and-beta-invite-for-update-subscription-customers/" target="_blank" rel="noopener noreferrer">announced in July</a>, <a href="https://blogs.embarcadero.com/how-to-prepare-for-rad-studio-11/" target="_blank" rel="noopener noreferrer">promoted in August</a>, and now in September <a href="https://blogs.embarcadero.com/rad-studio-11-preview-at-desktop-first-summit/" target="_blank" rel="noopener noreferrer">Marco Cantu has provided a public preview</a> of the next major version of our beloved development tool. Well, maybe when you are reading this, it might be already released.</p><p>There are several new features and bug fixes, but if you want see all of them in more details, I suggest you <a href="https://register.gotowebinar.com/register/4959183786599768588" target="_blank" rel="noopener noreferrer">register for the Sneak Peek webinar about Delphi 11</a> to watch the replay. Here I will focus on my preferred new feature: High DPI.</p><div class="theme-admonition theme-admonition-danger alert alert--danger admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>Warning</div><div class="admonitionContent_S0QG"><p>Please note I’m discussing a pre-release product that is subject to change, and I’m doing so with special permission from Embarcadero.</p></div></div><p>Having developed with Delphi on a Mac with Retina Display for some time, I really missed this. Delphi was probably one of the few remaining apps that I use that still didn’t support the high DPI mode. Now I’m covered. It’s refreshing to use the app I use the most now in high quality.</p><p>If you look at the following screenshots comparing Delphi 10.4 and Delphi 11, you will see what I mean:</p><img loading="lazy" src="/img/landgraf/delphi10-structure.png" width="380" class="img_ev3q">  <img loading="lazy" src="/img/landgraf/delphi11-structure.png" width="350" class="img_ev3q"><p>It’s a <strong>night-and-day difference</strong>. Actually after using Delphi 11 in high DPI for a while, it feels really bad to open Delphi 10.4 again, it looks like I’m traveling back to 10 years ago.</p><p>You might not see much difference at first sight, but I believe it’s because of the natural progression that we sometimes don’t notice. As an example, maybe you didn’t even notice how your smartphone evolved regarding to software features, performance, and also, of course, screen quality. But if you try to use the same smartphone you used 5 years ago, you will ask yourself “wow, I didn’t remember how bad it was compared to this one I have now”.</p><p>And in case you think this is just personal taste, I give you a practical example of how a better screen improves your productivity, somehow.</p><p>This is a screenshot of Delphi 10.4 with a TMS Aurelius unit open.</p><p><img loading="lazy" alt="delphi10-aurelius" src="/assets/images/delphi10-aurelius-40a8e66a53b564aea21832bd3c3a5da2.png" width="2880" height="1720" class="img_ev3q"></p><p>And this is a screenshot of Delphi 11 with the same unit open. You can see how much more source code is displayed in the same area of code. This alone makes it worth it.</p><p><img loading="lazy" alt="delphi11-aurelius" src="/assets/images/delphi10-aurelius-40a8e66a53b564aea21832bd3c3a5da2.png" width="2880" height="1720" class="img_ev3q"></p><p>Delphi 11 uses a different, smaller font than Delphi 10.4. But if you try to set the same font in Delphi 10.4 editor, it will not look good at all!</p><p>And what about you? What is your favorite new Delphi 11 feature? Comment in our forum, and let us know what you think about it. And welcome, Delphi 11!</p><p><a href="https://forum.landgraf.dev/t/high-dpi-support-in-delphi-11-and-why-does-it-matter/308" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Developing Delphi libraries: behind the scenes]]></title>
            <link>https://www.landgraf.dev/en/developing-delphi-libraries-behind-the-scenes</link>
            <guid>developing-delphi-libraries-behind-the-scenes</guid>
            <pubDate>Tue, 24 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[New webinar series "TMS BIZ: Behind the scenes". Proposal is to have a casual, relaxed conversation about the recent experiences in developing with Delphi.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="joakim" src="/assets/images/joakim-0fc07c323ad40ae16faaf335a0a60993.jpg" width="700" height="418" class="img_ev3q"></p><p>A recent <a href="https://tmssoftware.com/site/default.asp" target="_blank" rel="noopener noreferrer">TMS Software</a> <a href="https://www.tmssoftware.com/site/blog.asp?post=817Also" target="_blank" rel="noopener noreferrer">blog post</a> has announced a new webinar series called <a href="https://www.tmssoftware.com/site/blog.asp?post=817Also" target="_blank" rel="noopener noreferrer">TMS BIZ: Behind the scenes with Wagner Landgraf</a>. First one will be held next <strong>August, 26th, 16h UTC</strong>, at the <a href="https://www.tmswebacademy.com/" target="_blank" rel="noopener noreferrer">TMS Web Academy</a>. You can <a href="https://www.tmssoftware.com/site/tmswebacademy.asp?id=96" target="_blank" rel="noopener noreferrer">register for free</a> to watch it!.</p><p>The proposal is to have a casual, relaxed conversation with <a href="https://www.linkedin.com/in/wagner-landgraf/" target="_blank" rel="noopener noreferrer">Wagner Landgraf</a> about the recent experiences in developing with Delphi. The main focus will be <a href="https://www.tmssoftware.com/site/tmsbizintro.asp" target="_blank" rel="noopener noreferrer">TMS Business</a> product set, which include libraries like <a href="https://tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer">TMS XData</a>, <a href="https://www.tmssoftware.com/site/aurelius.asp" target="_blank" rel="noopener noreferrer">TMS Aurelius</a>, <a href="https://www.tmssoftware.com/site/scriptstudiopro.asp" target="_blank" rel="noopener noreferrer">TMS Scripter</a> and more. But this is just because that’s Wagner’s main focus of work.</p><p>The idea is to share <strong>development anecdotes</strong>, recente researches it’s being done, <strong>features being developed</strong> that are not announced yet, <strong>hidden gems found during the process</strong>, interesting sites and blogs recently visited, and so on. It’s a <strong>free chat about Delphi, software development</strong> and, of course, TMS Business.</p><p>You are also invited to participate, <strong>raising questions that could be discussed live</strong>. And more importantly, you are invited to <a href="https://support.tmssoftware.com/t/tms-biz-behind-the-scenes-with-wagner-landgraf/14904" target="_blank" rel="noopener noreferrer">suggest topics for next sessions</a>. The intention is to have “Behind the scenes” webinars at least <strong>twice a month</strong>, and we would like you to bring your ideas and suggestions for what could be discussed in upcoming sessions. For that we created a <a href="https://support.tmssoftware.com/t/tms-biz-behind-the-scenes-with-wagner-landgraf/14904" target="_blank" rel="noopener noreferrer">specific topic at our Support Center</a> that is a permanent place, 24/7, to discuss the series and propose new topics. It’s an extension of the webinars, let’s discuss ideas there!</p><p><a href="https://www.tmssoftware.com/site/tmswebacademy.asp?id=96" target="_blank" rel="noopener noreferrer">Register for free for the next “Behind the scenes” webinar</a> on <strong>August, 26th, 4PM UTC</strong>.</p><p><a href="https://support.tmssoftware.com/t/tms-biz-behind-the-scenes-with-wagner-landgraf/14904" target="_blank" rel="noopener noreferrer">Participate in discussion about “Behind the scenes” webinar series.</a></p><p>(*) Photo by <a href="https://unsplash.com/@jhonkasalo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Joakim Honkasalo</a> on <a href="https://unsplash.com/s/photos/sneak-peek?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Unsplash</a></p><p><a href="https://forum.landgraf.dev/t/developing-delphi-libraries-behind-the-scenes/305" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Artificial Intelligence can improve UI/UX at Desktop First UX Summit]]></title>
            <link>https://www.landgraf.dev/en/how-artificial-intelligence-can-improve-ui-ux-at-desktop-first-ux-summit</link>
            <guid>how-artificial-intelligence-can-improve-ui-ux-at-desktop-first-ux-summit</guid>
            <pubDate>Mon, 23 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[You are invited to Desktop First UX Summit 2021! "The world may seem to run on mobile, but everything still begins on the desktop, and the desktop user experience deserves better!"]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="thisisengineering" src="/assets/images/thisisengineering-f29a6a63424268b3b8d7580bc13ba81c.jpg" width="700" height="357" class="img_ev3q"></p><p>You are invited to <a href="https://summit.desktopfirst.com/?sc=46W4gTxE" target="_blank" rel="noopener noreferrer">Desktop First UX Summit 2021</a>! “The world may seem to run on mobile, but everything still begins on the desktop, and the desktop user experience deserves better!”</p><p>From <strong>Monday, Aug 30th</strong> through <strong>Friday, Sept 3rd, 2021</strong> industry experts and thousands of developers and designers like you will attend this five-day open online conference and take your desktop UI/UX skills to new levels! The Desktop-First UX Summit will cover both the theory and practice of creating great desktop user experiences.</p><p>Among other renowned speakers, <a href="https://summit.desktopfirst.com/speakers/wagner-landgraf/" target="_blank" rel="noopener noreferrer">Wagner Landgraf</a> will host the session “<a href="https://summit.desktopfirst.com/talks/how-artificial-intelligence-can-improve-uiux/" target="_blank" rel="noopener noreferrer">How Artificial Intelligence can improve UI/UX</a>“, on <strong>Tuesday, August 31st, at 10pm GMT</strong>, talking about about how AI can impact your UI design, and how it can bring you insights to improve the user experience of your application. Some practical examples with Delphi will also be presented!</p><p><a href="https://summit.desktopfirst.com/talks/how-artificial-intelligence-can-improve-uiux/" target="_blank" rel="noopener noreferrer">Book your FREE ticket now</a> for and see you there!</p><p>(*) Photo by <a href="https://unsplash.com/@thisisengineering?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">ThisisEngineering RAEng</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Unsplash</a></p><p><a href="https://forum.landgraf.dev/t/how-artificial-intelligence-can-improve-ui-ux-at-desktop-first-ux-summit/304" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Amazon SNS with Delphi: Push notifications, SMS and publish-subscribe pattern]]></title>
            <link>https://www.landgraf.dev/en/amazon-sns-with-delphi-push-notifications-sms-and-publish-subscribe-pattern</link>
            <guid>amazon-sns-with-delphi-push-notifications-sms-and-publish-subscribe-pattern</guid>
            <pubDate>Tue, 17 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Delphi users developing with AWS SDK for Delphi now can use Amazon SNS to send SMS, e-mail, mobile push notifications, and implement pub/sub pattern.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="pexels-torsten" src="/assets/images/pexels-torsten-ad7730cf93aca8d58dc5c1b654ef429d.jpg" width="640" height="243" class="img_ev3q"></p><p>Delphi users developing with <a href="/en/aws-amazon-web-services-sdk-for-delphi">Amazon Web Services SDK for Delphi</a> now can use yet another service: <a href="https://aws.amazon.com/sns/?nc1=h_ls&amp;whats-new-cards.sort-by=item.additionalFields.postDateTime&amp;whats-new-cards.sort-order=desc" target="_blank" rel="noopener noreferrer">Amazon SNS</a> (Amazon Simple Notification Service).</p><p><a href="https://docs.aws.amazon.com/sns/latest/dg/welcome.html" target="_blank" rel="noopener noreferrer">Amazon SNS</a> is a managed service that provides message delivery from <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern" target="_blank" rel="noopener noreferrer">publishers to subscribers</a> (also known as <em>producers</em> and <em>consumers</em>). Publishers subscribers by sending messages to a <em>topic</em>, which is a logical access point and communication channel. Clients can subscribe to the SNS topic and receive published messages using a supported endpoint type, such as Amazon Kinesis Data Firehose, Amazon SQS, AWS Lambda, HTTP, email, <a href="https://en.wikipedia.org/wiki/Push_technology#Push_notification" target="_blank" rel="noopener noreferrer">mobile push notifications</a>, and <a href="https://en.wikipedia.org/wiki/SMS" target="_blank" rel="noopener noreferrer">mobile text messages</a> (SMS).</p><p>It’s an affordable service. At the time this article was written, sending 1 million push notifications costs $0.50, while sending 1 million notifications via HTTP(S) costs $0.60. You can find more by visiting <a href="https://aws.amazon.com/sns/pricing/" target="_blank" rel="noopener noreferrer">Amazon SNS pricing page</a>.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="use-cases">Use cases<a class="hash-link" href="#use-cases" title="Direct link to heading">​</a></h2><p>Amazon SNS can be used to implement a <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern" target="_blank" rel="noopener noreferrer"><em>publisher/subscriber</em></a> system, to send <a href="https://en.wikipedia.org/wiki/SMS" target="_blank" rel="noopener noreferrer">SMS messages</a>, to send <a href="https://en.wikipedia.org/wiki/Push_technology#Push_notification" target="_blank" rel="noopener noreferrer">mobile push notifications</a>, among others. Some <a href="https://docs.aws.amazon.com/sns/latest/dg/sns-common-scenarios.html" target="_blank" rel="noopener noreferrer">common use cases</a> are listed below:</p><ul><li><p><strong>Application integration</strong>: Applications can communicate with each other by sending message to topics and receiving messages via notifications. For example, you can develop an application that publishes a message to an SNS topic whenever an order is placed for a product. This message can be consumed by other applications for further processing.</p></li><li><p><strong>Application alerts</strong>: Applications can send notifications when something happen, for example, when a critical error occurs, or when a specific indicator value is higher than a threshold.</p></li><li><p><strong>User notifications</strong>: Amazon SNS can send push email messages and text messages (SMS messages) to individuals or groups. For example, you could send e-commerce order confirmations as user notifications.</p></li><li><p><strong>Mobile push notifications</strong>: And of course, you can send push notifications to your mobile applications to notify your users of any important information related to your app.</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-amazon-sns-with-aws-sdk-for-delphi">Using Amazon SNS with AWS SDK for Delphi<a class="hash-link" href="#using-amazon-sns-with-aws-sdk-for-delphi" title="Direct link to heading">​</a></h2><p>You can refer to our <a href="/en/">first article about AWS SDK for Delphi</a> to learn more about the concepts, credentials and basic usage. Once you learn about it, you can easily use the following code snippets to perform some specific operations using Amazon SNS.</p><p>To start with, you should of course create the SNS client:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IAmazonSimpleNotificationService;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TAmazonSimpleNotificationServiceClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The following code shows how to create a topic (which you can use to publish messages to it) and get its ARN, which will be used for further operations:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TopicArn: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TopicArn := Client.CreateTopic('test-name').TopicArn;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>You can then set or get the topic attributes, or even delete it if you need to:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TopicArn: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  SetAttrRequest: ISetTopicAttributesRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrResponse: IGetTopicAttributesResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // set topic attribute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  SetAttrRequest := TSetTopicAttributesRequest.Create(TopicArn, 'DisplayName', 'My topic');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.SetTopicAttributes(SetAttrRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // verify topic attributes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrResponse := Client.GetTopicAttributes(TopicArn);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  DisplayName := GetAttrResponse.Attributes['DisplayName']);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // delete new topic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.DeleteTopic(TopicArn);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Next step is to add subscriptions to the topic. You can setup it to send notifications to HTTP endpoints, e-mail, SMS messages, other Amazon services, etc. The following example shows how to subscribe the topic to an e-mail address, so messages published to topic will be sent via e-mail. This example waits for the subscription operation to finish to get the subscription ARN. With the subscription ARN you can later perform operations on it, like removing the subscription.</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">function SubscribeTopic(const TopicArn, EmailAddress: string): string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Latest: TDateTime;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response: IListSubscriptionsByTopicResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // subscribe an email address to the topic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.Subscribe(TSubscribeRequest.Create(TopicArn, 'email', EmailAddress));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // wait until subscription has been confirmed, wait time for two minutes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Latest := IncMinute(Now, 2);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  while Now &lt; Latest do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // get subscriptions for topic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Response := Client.ListSubscriptionsByTopic(TopicArn);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // test whether the subscription has been confirmed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    if Response.Subscriptions[0].SubscriptionArn &lt;&gt; 'PendingConfirmation' then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      Exit(Response.Subscriptions[0].SubscriptionArn);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // wait</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Sleep(15 * 1000);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And this is another example of subscription. In this case, we’re subscribing to an Amazon SQS queue. Messages will be forwarded to the queue, which can be later processed by reading the existing messages in the queue. The example also sets proper permission to the queue, otherwise the SNS topic wouldn’t have permission to send messages to it.</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">function SubscribeQueue(Client: IAmazonSimpleNotificationService;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const TopicArn: string; SQSClient: IAmazonSQS; const SQSQueueUrl: string): string; </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrResponse: IGetQueueAttributesResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrRequest: IGetQueueAttributesRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  SQSQueueArn: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Policy: TPolicy;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  PolicyStr: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TopicArn: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  SetAttrRequest: ISetQueueAttributesRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // Get the queue's existing policy and ARN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrRequest := TGetQueueAttributesRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrRequest.QueueUrl := SQSQueueUrl;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrRequest.AttributeNames.Add(TSQSConsts.ATTRIBUTE_ALL);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GetAttrResponse := SQSClient.GetQueueAttributes(GetAttrRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  SQSQueueArn :=  GetAttrResponse.Attributes['QueueArn'];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if  GetAttrResponse.Attributes.TryGetValue('Policy', PolicyStr) then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Policy := TPolicy.FromJson(PolicyStr)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Policy := TPolicy.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SetLength(Result, 0);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    if not HasSQSPermission(Policy, TopicArn, SQSQueueArn) then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      AddSQSPermission(Policy, TopicArn, SQSQueueArn);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Result := Client.Subscribe(TopicArn, 'sqs', SQSQueueArn).SubscriptionArn;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SetAttrRequest := TSetQueueAttributesRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SetAttrRequest.QueueUrl := SQSQueueUrl;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SetAttrRequest.Attributes.Add('Policy', Policy.ToJson);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    SQSClient.SetQueueAttributes(SetAttrRequest);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  finally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Policy.Free;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Once topic is created and has some subscriptions to it, all you need to do is publish messages to the topic. The messages will be then sent to the subscribers:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> // publish a message to the topic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.Publish(TPublishRequest.Create(TopicArn, 'Test message', 'Subject'));</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>If you also want to write code for the subscribers, you can read the sent message and process it. Messages are sent as JSON format, and AWS SDK for Delphi already provides high-level classes to process it. You can easily parse the message from JSON and even validate it. Each message sent from Amazon SNS has a signature generated from a certificate. The message provides an URL you can use to download a certificate and check if the signature is valid by using the certificate’s public key. AWS SDK for Delphi automatically does that for you (download the certificate and check the valid signature) with a single line of code:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// Parse an SNS message from JSON and validate signature:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function GetMessage(const Json: string): AWS.SNS.Message.TMessage;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Result := AWS.SNS.Message.TMessage.ParseMessage(Json);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if not Result.IsMessageSignatureValid then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    raise Exception.Create('Invalid message: bad signature');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And finally, a totally different example, the code used to send a mobile push notification to an arbitrary mobile number.</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IAmazonSimpleNotificationService;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: IPublishRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TAmazonSimpleNotificationServiceClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TPublishRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.PhoneNumber := '+184298765321';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Message := 'Hello from AWS SDK for Delphi!';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client.Publish(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="whats-next">What’s next<a class="hash-link" href="#whats-next" title="Direct link to heading">​</a></h2><p>Stay tuned for more services to be added to <a href="/en/">AWS SDK for Delphi</a>! You can also be part of it. Let us know what do you think and what service you want to be added, and why. Share your needs, we can listen and adjust our roadmap, depending on community feedback. And of course, also feel free to comment below with your thoughts and what did you think about Amazon SNS support.</p><p>(*) Photo by <a href="https://www.pexels.com/@tdcat/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels" target="_blank" rel="noopener noreferrer">Torsten Dettlaff</a> from <a href="https://www.pexels.com/photo/black-and-gray-digital-device-193003/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels" target="_blank" rel="noopener noreferrer">Pexels</a></p><p><a href="https://forum.landgraf.dev/t/amazon-sns-with-delphi-push-notifications-sms-and-publish-subscribe-pattern/302/2" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AWS (Amazon Web Services) SDK for Delphi]]></title>
            <link>https://www.landgraf.dev/en/aws-amazon-web-services-sdk-for-delphi</link>
            <guid>aws-amazon-web-services-sdk-for-delphi</guid>
            <pubDate>Thu, 29 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[A new relevant open source project is now available for Delphi community on GitHub: AWS SDK for Delphi.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="aws-buliding" src="/assets/images/aws-buliding-3a7f87578483d6c98a06045510595a4f.jpg" width="615" height="303" class="img_ev3q"></p><p>A new relevant open source project is now available for Delphi community on GitHub: <a href="https://github.com/landgraf-dev/aws-sdk-delphi" target="_blank" rel="noopener noreferrer">AWS SDK for Delphi</a>.</p><p>The <strong>AWS SDK for Delphi</strong> enables Delphi developers to easily work with <a href="https://aws.amazon.com/" target="_blank" rel="noopener noreferrer">Amazon Web Services</a> and build scalable solutions with <a href="https://aws.amazon.com/ses/" target="_blank" rel="noopener noreferrer">Amazon SES</a>, <a href="https://aws.amazon.com/sqs/" target="_blank" rel="noopener noreferrer">Amazon SQS</a>, and more. It is a non-official SDK based on the official<a href="https://github.com/aws/aws-sdk-net" target="_blank" rel="noopener noreferrer"> AWS SDK for .NET</a>. A few selected popular services are added, and more will be added very soon.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="about-amazon-web-services">About Amazon Web Services<a class="hash-link" href="#about-amazon-web-services" title="Direct link to heading">​</a></h2><p>From Amazon Web Services (AWS) <a href="https://aws.amazon.com/what-is-aws/" target="_blank" rel="noopener noreferrer">web site itself</a>: “AWS is the world’s most comprehensive and broadly adopted cloud platform, offering over 200 fully featured services from data centers globally. Millions of customers—including the fastest-growing startups, largest enterprises, and leading government agencies—are using AWS to lower costs, become more agile, and innovate faster.</p><p>AWS includes useful services for developers to send notifications (<a href="https://aws.amazon.com/sns/?whats-new-cards.sort-by=item.additionalFields.postDateTime&amp;whats-new-cards.sort-order=desc" target="_blank" rel="noopener noreferrer">Amazon SNS</a>), send and receive e-mails (<a href="https://aws.amazon.com/ses/" target="_blank" rel="noopener noreferrer">Amazon SES</a>), use queue systems (<a href="https://aws.amazon.com/sqs/" target="_blank" rel="noopener noreferrer">Amazon SQS</a>), several database systems (<a href="https://aws.amazon.com/rds/" target="_blank" rel="noopener noreferrer">Amazon RDS</a>, Amazon <a href="https://aws.amazon.com/dynamodb/" target="_blank" rel="noopener noreferrer">DynamoDB</a>), memory cache (Amazon ElastiCache), content delivery network (<a href="https://aws.amazon.com/cloudfront/" target="_blank" rel="noopener noreferrer">Amazon CloudFront</a>), storage (<a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener noreferrer">Amazon S3</a> and <a href="https://aws.amazon.com/s3/storage-classes/glacier/" target="_blank" rel="noopener noreferrer">Amazon Glacier</a>), and many more!.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-the-sdk">Using the SDK<a class="hash-link" href="#using-the-sdk" title="Direct link to heading">​</a></h2><p>Each Amazon web service has its own package and unit name scheme, which is AWS &lt; service &gt; .dproj and AWS.&lt; service &gt;.*.pas, respectively. For example, for Amazon SQS (Simple Queue Service), the package name is AWSSQS.dproj and unit name is AWS.SQS.pas (and all other units in the package follow same pattern, like AWS.SQS.Client.pas or AWS.SQS.ClientIntf.pas.</p><p>Most types you need will be in the main unit, which for example is AWS.SQS. So that’s the only unit you will need to use most of the functions. From there you can access all the available API operations. Each operation method receives a request interface and returns a response interface.</p><p>The following examples receive a message from an SQS queue and output the id and body of each message received:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// 1. Use main unit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">uses AWS.SQS;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">procedure WriteMessageIds(const QueueUrl: string);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IAmazonSQS;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response: IReceiveMessageResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: IReceiveMessageRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Msg: AWS.SQS.TMessage;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 2. Instantiate client interface</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TAmazonSQSClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 3. Create and fill the request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TReceiveMessageRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.QueueUrl := QueueUrl;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 4. Call operation method passing the request to receive the response;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response := Client.ReceiveMessage(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 5. Process the response</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  for Msg in Response.Messages do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    WriteLn(Msg.MessageId);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    WriteLn(Msg.Body);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    WriteLn;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The following example sends an e-mail to the specified address using the specified subject and message:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// 1. Use main unit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">uses AWS.SES;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">procedure SendEmail(const Recipient, Subject, Content: string);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client: IAmazonSimpleEmailService;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request: ISendEmailRequest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response: ISendEmailResponse;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 2. Instantiate client interface</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Client := TAmazonSimpleEmailServiceClient.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 3. Create and fill the request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request := TSendEmailRequest.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Source := SenderEmail; </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Destination := TDestination.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Destination.ToAddresses.Add(Recipient);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Request.Message := TMessage.Create(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    TContent.Create(Subject),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    TBody.Create(TContent.Create(Content)));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 4. Call operation method passing the request to receive the response;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Response := Client.SendEmail(Request);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 5. Process the response</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  WriteLn(Response.MessageId);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="credentials">Credentials<a class="hash-link" href="#credentials" title="Direct link to heading">​</a></h2><p>The AWS SDK for Delphi searches for credentials in a certain order and uses the first available set for the current application.</p><p>Credential search order</p><ol><li>Credentials that are explicitly set on the AWS service client, as described in below</li><li>A credentials profile with the name specified by a value in TAWSConfigs.AWSProfileName.</li><li>A credentials profile with the name specified by the AWS_PROFILE environment variable.</li><li>The <!-- -->[default]<!-- --> credentials profile.</li></ol><p>Passing access and secret keys directly to client</p><p>You can simply pass the Access key ID and Secret key directly in the client constructor:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    Client := TAmazonSQSClient.Create(myAccessKey, mySecretKey);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Although using credentials profile is recommended as it’s easier to manage and also compatible with <a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener noreferrer">AWS Command Line Interface</a>.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="enjoy-and-comment">Enjoy and comment!<a class="hash-link" href="#enjoy-and-comment" title="Direct link to heading">​</a></h2><p>Thanks for being with me so far. AWS SDK is was born to grow and make the whole AWS world accessible to you. It is <a href="https://faircode.io/" target="_blank" rel="noopener noreferrer">fair-code</a> distributed under <a href="https://github.com/landgraf-dev/aws-sdk-delphi/blob/master/LICENSE" target="_blank" rel="noopener noreferrer">Apache 2.0 with Commons Clause</a> license, and <strong>does not require any third-party component, it's 100% native pure Pascal code</strong>. Please leave your impressions and comments here, and if you have any suggestion for the framework, feel free to use the <a href="https://github.com/landgraf-dev/aws-sdk-delphi/issues" target="_blank" rel="noopener noreferrer">GitHub Issues</a> page of the project!</p><p>(*) AWS building photo by <a href="https://www.flickr.com/photos/diversey/46600198075/in/photostream/" target="_blank" rel="noopener noreferrer">Tony Webster</a>. Original picture was cropped to better fit in the page.</p><p><a href="https://forum.landgraf.dev/t/aws-amazon-web-services-sdk-for-delphi/301" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cloud services with Delphi: don’t make these mistakes!]]></title>
            <link>https://www.landgraf.dev/en/cloud-services-with-delphi-dont-make-these-mistakes</link>
            <guid>cloud-services-with-delphi-dont-make-these-mistakes</guid>
            <pubDate>Sun, 30 May 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Thinking about migrating your applications to the cloud? Come watch my free webinar: Cloud services with Delphi: don't make these mistakes!]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="person-is-going" src="/assets/images/person-is-going-72e3b463b975b6fdf029b993e2f3ed7e.jpg" width="600" height="331" class="img_ev3q"></p><p>So, you have been given your first project to develop a cloud service with Delphi and want to <strong>avoid all pitfalls and loopholes</strong> that can come with such project? Or, you are thinking about migrating your existing applications to the cloud, and want to make sure you are <strong>going do start it the right away</strong>?</p><div class="theme-admonition theme-admonition-info alert alert--info admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Update</div><div class="admonitionContent_S0QG"><p>The live webinar is gone, sorry if you have missed this one! Attending live is always nice, you can ask questions and get eventual prizes (in this one, we offered a 20% discount coupon). But you can watch the replay here:</p></div></div><iframe width="560" height="315" src="https://www.youtube.com/embed/ZHbDFu8Cbsg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"></iframe><h3 class="anchor anchorWithStickyNavbar_LWe7" id="original-post">Original Post:<a class="hash-link" href="#original-post" title="Direct link to heading">​</a></h3><p>Well, you are welcome to attend my next <strong>free live webinar</strong>, at TMS Software Academy: <a href="https://www.tmssoftware.com/site/tmswebacademy.asp?id=83" target="_blank" rel="noopener noreferrer">Cloud services with Delphi: don’t make these mistakes</a>. It will happen on next <strong>Wednesday, June 2nd, 3:00 PM UTC</strong></p><p>I will talk about what does it mean to “move to the cloud”, and <strong>share with you my experience with developing such applications</strong>, and mention key points you need to pay attention to a<strong>void headaches and troubles</strong> during development and after your application is running in production.</p><p>You are invited! <a href="https://www.tmssoftware.com/site/tmswebacademy.asp?id=83" target="_blank" rel="noopener noreferrer">Register for free for the webinar through this link</a>, it will be LIVE, so you also have the opportunity to ask your questions and have it answered right away. I’m waiting you there.</p><p><a href="https://forum.landgraf.dev/t/cloud-services-with-delphi-dont-make-these-mistakes/300" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Windows-native, tristate checkbox TTreeView control]]></title>
            <link>https://www.landgraf.dev/en/a-windows-native-tristate-checkbox-ttreeview-control</link>
            <guid>a-windows-native-tristate-checkbox-ttreeview-control</guid>
            <pubDate>Sun, 09 May 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[A Windows-native tri-state checkbox for Delphi.]]></description>
            <content:encoded><![CDATA[<p>Recently I had to create a Delphi VCL form with a tree-like control. It should be a piece-of-cake with Delphi: just dropped a TTreeView control on the form and I was almost there. But there was one gotcha: I wanted to have checkboxes in each node. Worse: checkboxes that could hold three different states (checked, unchecked, partial).</p><p><img loading="lazy" alt="CheckTreeViewSample" src="/assets/images/CheckTreeViewSample-420483f040b52fffcc651c631f8f5398.png" width="606" height="513" class="img_ev3q"></p><p>It’s very rare that I have to build complex GUI applications (lucky me) thus I had hope that in most recent VCL all I had to use was to enable some kind of property in the TTreeView component. To my disappointment, there is not such support for checkbox in the tree view.</p><p>Since I didn’t want to use a 3rd party control in this project, I had to find a way to do that manually. I googled for it, and all I could find was the same old way of solving things: create images for the checkbox states, and use StateIndex property to read/write the checkbox state. I just couldn’t believe this is still the way to do it in 2021.</p><p>After more research I found out that Windows Vista and on (sorry, XP folks, we have to move forward eventually) provides an extended style to the native tree view control that allows tristate checkboxes. That’s exactly what I wanted. I was really annoyed that I’d had to “create” (find somewhere) images for checked state, unchecked state, partial state… Worse, I’d have to find normal-dpi and high-dpi versions. Using a Windows-native tristate checkbox was the way to go for me.</p><p>Enabling tristate checkboxes is as simple as using one line of code to modify the extended style of the Windows control, adding the TVS_EX_PARTIALCHECKBOXES style:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    TreeView_SetExtendedStyle(TreeView1.Handle, TVS_EX_PARTIALCHECKBOXES, TVS_EX_PARTIALCHECKBOXES);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>But since I needed more of that – inspect and modify the checkbox state, automatically select child nodes, etc., I wrapped everything it under a TTreeView helper and made it available in the CheckTreeView GitHub repository.</p><p>Another advantage is that this not a different component, so no need to install any package and use a different class at runtime. Just use the regular TTreeView component.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="enabling-tristate-checkboxes">Enabling tristate checkboxes<a class="hash-link" href="#enabling-tristate-checkboxes" title="Direct link to heading">​</a></h2><p>Since it’s just a class helper, to use it, just add the unit CheckTreeView to your form unit:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    uses {...}, CheckTreeView;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then just call EnableTristateCheckboxes method:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    TreeView1.EnableTristateCheckboxes;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>This is enough to add checkboxes to all nodes, and change the node check state when users clicks the checkbox or press space key when a node is selected.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="reading-or-changing-the-check-state-of-a-node">Reading or changing the check state of a node<a class="hash-link" href="#reading-or-changing-the-check-state-of-a-node" title="Direct link to heading">​</a></h2><p>You can use CheckState property of a node to read or modify the state of the checkbox:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    Node := TreeView1.Selected;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    case Node.CheckState of</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csUnchecked: Node.Text := 'unchecked';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csChecked: Node.Text := 'checked';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csPartial: Node.Text := 'partial';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    TreeView1.Selected.CheckState := csChecked;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="automatic-check-states">Automatic check states<a class="hash-link" href="#automatic-check-states" title="Direct link to heading">​</a></h2><p>If you want the TTreeView control to perform these operations automatically when the user checks/unchecked a node:</p><ol><li>Check/uncheck all child items of the modified node;</li><li>Update the parent node check state based on the check state of the child nodes;</li><li>Allow just check/uncheck state for the modified node.</li></ol><p>Then add OnMouseDown and OnKeyPress event handlers for your TTreeView component and add the following code:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure TForm6.TreeView1KeyPress(Sender: TObject; var Key: Char);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TreeView1.HandleKeyPress(Key);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">procedure TForm6.TreeView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  TreeView1.HandleMouseDown(Button, Shift, X, Y);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>That’s it, I hope you enjoy it. Grab it from GitHub: <a href="https://github.com/landgraf-dev/CheckTreeView" target="_blank" rel="noopener noreferrer">https://github.com/landgraf-dev/CheckTreeView</a> and let me know what you think by adding your comment below!</p><p><a href="https://forum.landgraf.dev/t/a-windows-native-tristate-checkbox-ttreeview-control/296" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[20 Years of Delphi and TMS in ERP software: an interview]]></title>
            <link>https://www.landgraf.dev/en/delphi-sydney-10-4-2-interview</link>
            <guid>delphi-sydney-10-4-2-interview</guid>
            <pubDate>Fri, 26 Mar 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[In this interview, you will see how Delphi and TMS Software helped Viasoft Korp, started as a 1-man company, to become part of a conglomerate with more than 500 collaborators.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="krakenimages" src="/assets/images/krakenimages-05ae3846b9c52ced0280399a4cdb9e56.jpg" width="600" height="444" class="img_ev3q"></p><p>We have recently published in our <a href="https://www.youtube.com/landgrafdev" target="_blank" rel="noopener noreferrer">Youtube channel</a> (subscribe!) an <strong>interview with</strong> <a href="https://www.linkedin.com/in/alexandre-henzen-1b234130" target="_blank" rel="noopener noreferrer">Alexandre Henzen</a>, Technical Director of <a href="https://korp.com.br/" target="_blank" rel="noopener noreferrer">Viasoft Korp</a>. The interview (in Portuguese) is available <a href="https://www.youtube.com/watch?v=IfNRciJJ6x4&amp;feature=youtu.be" target="_blank" rel="noopener noreferrer">through this link</a>, and in this article right below as well.</p><p>In this interview, Alexandre talks about <strong>Viasoft Korp</strong>, how the company started with just <strong>one person</strong> and a <strong>desktop software built with C++Builder 6</strong> and became an ERP software provider to <strong>big Brazilian industries</strong>, being part of conglomerate of companies with <strong>more than 500 employees</strong>.</p><p>Among those who took part of this journey, there are me, <strong>Wagner Landgraf</strong>, <a href="https://tmssoftware.com/site/default.asp" target="_blank" rel="noopener noreferrer">TMS Software</a>, and <a href="https://www.embarcadero.com/?aldSet=en-GB" target="_blank" rel="noopener noreferrer">Embarcadero</a>, with <a href="https://www.embarcadero.com/br/products/delphi" target="_blank" rel="noopener noreferrer">Delphi</a>, a development tool used by the company <strong>for almost 20 years</strong>. This interview is about such journey.</p><p>For those who can’t understand Portuguese, or just don’t like videos, we have transcribed below, in English, the best moments of the interview.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/IfNRciJJ6x4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"></iframe><h3 class="anchor anchorWithStickyNavbar_LWe7" id="146---about-viasoft-korp"><a href="https://youtu.be/IfNRciJJ6x4?t=106" target="_blank" rel="noopener noreferrer">1:46</a> - About Viasoft Korp<a class="hash-link" href="#146---about-viasoft-korp" title="Direct link to heading">​</a></h3><p><em><strong>Wagner Landgraf:</strong></em> <em>What is Viasoft Korp?</em></p><p><strong>Alexandre Henzen:</strong> Viasoft Korp is a business unit of <a href="https://viasoft.com.br/" target="_blank" rel="noopener noreferrer">Viasoft</a> group. Viasoft is a company that provides ERP software for <strong>several different types of business</strong>: agribusiness, supermarkets, construction material, among others. And Viasoft Korp provides <strong>ERP software for industries</strong>. Korp started officially in 2000. (…) At that time software was developed in <strong>C++ Builder</strong>.</p><blockquote><p><strong><em>Viasoft Korp provides</em></strong> <br>
<strong><em>ERP software for</em></strong> <br>
<strong><em>industries</em></strong>.</p><p>Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="300---migrating-from-c-builder-to-delphi"><a href="https://youtu.be/IfNRciJJ6x4?t=180" target="_blank" rel="noopener noreferrer">3:00</a> - Migrating from C++ Builder to Delphi<a class="hash-link" href="#300---migrating-from-c-builder-to-delphi" title="Direct link to heading">​</a></h3><p><strong><em>WL:</em></strong> <em>I think it was one of the few ERP software I’ve seen that was built with C++</em>.</p><p><strong>AH:</strong> Indeed. At some point, around 2004, 2005, it took <strong>6 hours</strong> to compile. We then developed a <strong>C++ to Pascal transpiler</strong> – with your help by the way, I’m not sure if you remember – so we could migrate the full source code to Delphi.</p><blockquote><p><strong><em>We developed a C++ to</em></strong> <br>
<strong><em>Pascal transpiler to</em></strong> <br>
<strong><em>migrate from C++</em></strong> <br>
<strong><em>Builder to Delphi</em></strong>. <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="647---key-moments-that-helped-the-company"><a href="https://youtu.be/IfNRciJJ6x4?t=408" target="_blank" rel="noopener noreferrer">6:47</a> - Key moments that helped the company<a class="hash-link" href="#647---key-moments-that-helped-the-company" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>What helped Korp to grow? What were the key moments?</em></p><p><strong>AH:</strong> <a href="https://www.tmssoftware.com/site/scriptstudiopro.asp" target="_blank" rel="noopener noreferrer">TMS Scripter</a> (TMS Software product for editing and executing scripts at runtime) <strong>was a big differential for us</strong>. (…) It’s typical that ERP must be customizable, thus <strong>such flexibility allowed by TMS Scripter</strong>, (it’s something that) in 2001, 2002, you didn’t see many things like that. (…) The customers themselves could create forms, even full modules inside the system. That helped us to grow.</p><blockquote><p><strong><em>Runtime software</em></strong> <br>
<strong><em>customization using</em></strong> <br>
<strong><em><a href="https://www.tmssoftware.com/site/scriptstudiopro.asp" target="_blank" rel="noopener noreferrer">TMS Scripter</a> helped us</em></strong> <br>
<strong><em>to grow.</em></strong> <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="917---using-workflow-tool"><a href="https://youtu.be/IfNRciJJ6x4?t=556" target="_blank" rel="noopener noreferrer">9:17</a> - Using workflow tool<a class="hash-link" href="#917---using-workflow-tool" title="Direct link to heading">​</a></h3><p><strong>AH:</strong> (Another important decision): At a time BPM was not very popular, (to use <a href="https://www.tmssoftware.com/site/workflow.asp" target="_blank" rel="noopener noreferrer">TMS Workflow</a> in 2007) was also a <strong>big plus</strong>. The fact you could draw a flowchart and say: “Joe approves the invoice, if the invoice value is higher than X, send it to Jack, etc.” (…) <strong>That made our system even more flexible.</strong></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="1720---the-origin-of-tms-aurelius"><a href="https://youtu.be/IfNRciJJ6x4?t=1040" target="_blank" rel="noopener noreferrer">17:20</a> - The origin of TMS Aurelius<a class="hash-link" href="#1720---the-origin-of-tms-aurelius" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>Well, we have been friends and technical partners all these years, we (at TMS) have helped you a lot, you have helped us a lot. We helped you as you said: Korp had technical needs and we developed and improved solutions to give ERP flexibility, I believe you have reduced support a lot. But one thing I never said in public, I will talk about it here for the first time: if you (who are watching us) use <a href="https://tmssoftware.com/site/aurelius.asp" target="_blank" rel="noopener noreferrer">TMS Aurelius</a> (Delphi ORM framework from TMS Software), be grateful to this person I’m talking to: he he was not only the first customer of the TMS Aurelius, <strong>he was the driving force that led TMS Aurelius to be developed</strong>. We are in 2021 and there are many people who still do not know why or how to use an ORM in their software. <strong>Alexandre, in 2010, already had such vision, that an ORM would help him and his company</strong>. He contacted us and we partnered to develop TMS Aurelius, <strong>as the existing ORM libraries for Delphi did not fully serve him.</strong></em></p><blockquote><p><strong><em>Alexandre was the</em></strong> <br>
<strong><em>driving force that led</em></strong> <br>
<strong><em>TMS Aurelius to be</em></strong> <br>
<strong><em>developed.</em></strong> <br>
Wagner Landgraf</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="2043---scalability-and-technologies"><a href="https://youtu.be/IfNRciJJ6x4?t=1244" target="_blank" rel="noopener noreferrer">20:43</a> - Scalability and technologies<a class="hash-link" href="#2043---scalability-and-technologies" title="Direct link to heading">​</a></h3><p><strong>AH:</strong> All these developments that we seek, have always been thinking about scalability. Always wanting to expand, improve code, the ERP was always growing very, very complex. <strong>So all of these technologies came to make the system flexible and scalable.</strong> The system is huge. Today we don’t just use Delphi.</p><p><strong>WL:</strong> <em>Yes, let’s talk about the other technologies used by Viasoft Korp. The software started as a client/server Windows desktop application. Today, of course, you have many other services, web applications, mobile application, integrations, microservices. What other technologies are also helping Korp today?</em></p><p><strong>AH:</strong> For web development, for example, we chose to use C # on the backend and Angular on the frontend. We also use Golang in some microservices. Each language has its purpose.</p><p><strong>WL:</strong> <em>And you also have take into account the current needs of the company. One might ask, for example: “why didn’t you use <a href="https://www.tmssoftware.com/site/tmswebcoreintro.asp" target="_blank" rel="noopener noreferrer">TMS Web Core</a> (TMS Software product to create web applications with Delphi)?”. Simply because TMS Web Core didn’t exist at that time! When it was first released, Korp already had all his web applications fully developed in Angular.</em></p><p><strong>AH:</strong> Exactly. And we also wanted to run on Linux, Docker, lots of things to take into account.</p><p><strong>WL:</strong> Yes, all of these tools as well. I learn a lot from Korp when I go there. Not only about programming, but also devops. They are always dealing with Kubernetes, Docker, Consul, Traefik …</p><p><strong>AH:</strong> About the tests we developed an internal framework, named Flow, that was a big change for us. With such tool we write the <a href="https://pt.wikipedia.org/wiki/Behavior_Driven_Development" target="_blank" rel="noopener noreferrer">BDD</a> code and it executes everything. Currently we have around twelve virtual machines in three different servers running tests 24/7.</p><blockquote><p><strong><em>We also use C#,</em></strong> <br>
<strong><em>Angular, Golang. Each</em></strong> <br>
<strong><em>programming language</em></strong> <br>
<strong><em>has its purpose.</em></strong> <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="2344---tests-and-software-quality"><a href="https://youtu.be/IfNRciJJ6x4?t=1424" target="_blank" rel="noopener noreferrer">23:44</a> - Tests and software quality<a class="hash-link" href="#2344---tests-and-software-quality" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>You mentioned how TMS Software contributed with ORM (<a href="https://tmssoftware.com/site/aurelius.asp" target="_blank" rel="noopener noreferrer">TMS Aurelius</a>), multitier REST (<a href="https://tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer">TMS XData</a>), etc. But speaking about tests: I remember how, many years ago, you (and everyone I knew at the time), suffered from tests and to keep software quality. You had people just to do manual tests, UI tests, etc. I believe that these technical improvements over time helped a lot in this.</em></p><p><strong>AH:</strong> Yes, ERP is a very complex software. Without these new development paradigms, this would not be possible. <strong>Today we have servers running tests 24 hours a day, using continuous integration</strong>. We use Jenkins, it retrieves changes from Bitbucket (Git repository for source code version control), immediately runs all test scenarios, <strong>all 100% automated.</strong></p><p><strong>WL:</strong> <em>I believe then that TMS helped you, a little bit, isn’t it?</em></p><p><strong>AH:</strong> Absolutely, <strong>all the architecture of our Delphi-made software is built around TMS Business.</strong></p><blockquote><p><strong><em>All the architecture of</em></strong> <br>
<strong><em>our software written in</em></strong> <br>
<strong><em>Delphi is built around</em></strong> <br>
<strong><em><a href="https://www.tmssoftware.com/site/tmsbizintro.asp" target="_blank" rel="noopener noreferrer">TMS Business</a>.</em></strong> <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="2740---size-of-customers"><a href="https://youtu.be/IfNRciJJ6x4?t=1660" target="_blank" rel="noopener noreferrer">27:40</a> - Size of customers<a class="hash-link" href="#2740---size-of-customers" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>You mentioned that XData services are processing a large number of requests, please tell us more about the size of your customers.</em></p><p><strong>AH:</strong> There are the most varied sizes. Companies range from 20 to <strong>500 users accessing the system simultaneously</strong>. And those are companies with a high volume of logistics handling and issuing fiscal notes (Brazilian legal invoices), they are complex and heavy systems. The amount of information that travels through the system is huge.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="2850---about-recent-delphi-versions"><a href="https://youtu.be/IfNRciJJ6x4?t=1730" target="_blank" rel="noopener noreferrer">28:50</a> - About recent Delphi versions<a class="hash-link" href="#2850---about-recent-delphi-versions" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>Speaking about Delphi. You were using Berlin (10.1), how was this evolution?</em></p><p><strong>AH:</strong> We were using Delphi Berlin (10.1) and tried to update to new versions. (The problem is that) our application works like this: it is not just a single executable. There is the main executable and <strong>each module in the system is a runtime package, a BPL</strong>, (they are modules) that are loaded dynamically as the user keeps using the software. So it’s a <strong>huge package structure</strong>, and we’ve always suffered from it (runtime packages) in several ways: detecting memory leaks is more complicated, recompiling packages is cumbersome because of package dependencies, etc.</p><p><strong>WL:</strong> <em>There was even a problem with a Windows update, not related to Delphi, that cause you a big problem, right?</em></p><p><strong>AH:</strong> Yes, there was a Windows update that simply screw everything up (Alexandre is referring to <a href="https://blog.marcocantu.com/blog/2017-june-delphi-packages-creators-update.html" target="_blank" rel="noopener noreferrer">this problem reported in Marco Cantu’s blog</a>). <strong>It took almost five minutes to just start the application.</strong> We even went so far as to revert to previous Windows version and block Windows updates on all the company’s machines. (…) Then, we migrated to Delphi 10.4 Sydney, the first version (10.4.0). At the beginning we still had problems with the tool, the LSP (Language Server Protocol, Delphi’s new system for code completion) itself had some problems. And now with (the update to) 10.4.2, for which we recently upgraded, we felt the difference – <strong>it’s much more stable, the IDE is compiling much faster and the feedback I’m receiving from the developers is very positive.</strong></p><blockquote><p><strong><em>Since XE2, the most</em></strong> <br>
<strong><em>stable release that I’ve</em></strong> <br>
<strong><em>seen, of all of them, is</em></strong> <br>
<strong><em>this one, Sydney 10.4.2.</em></strong> <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="3139---upgrading-to-delphi-1042-sydney"><a href="https://youtu.be/IfNRciJJ6x4?t=1899" target="_blank" rel="noopener noreferrer">31:39</a> - Upgrading to Delphi 10.4.2 Sydney<a class="hash-link" href="#3139---upgrading-to-delphi-1042-sydney" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>So, this 10.4.2 release, compared to 10.4, is much better?</em></p><p><strong>AH:</strong> Yes, <strong>much more stable, not even close</strong>. If we take all the Delphi versions that we’ve used all those years, since Delphi XE2, <strong>the most stable released I’ve seen, of all of them, is this one, 10.4.2</strong>.</p><p><strong>WL:</strong> <em>I remember that a big problem you had was the compilation time. The time to run the tests, for example, how long did it take?</em></p><p><strong>AH:</strong> It took <strong>58 minutes, almost an hour</strong>. It dropped to about 28 minutes with just the upgrade to 10.4.2. Then we made a few more changes to the package settings, and it dropped to <strong>12 to 14 minutes</strong>, depending on the machine. <strong>The runtime packages feature is also much more stable now, it is a big difference</strong>.</p><blockquote><p><strong><em>Compilation time was</em></strong> <br>
<strong><em>58 minutes, it dropped</em></strong> <br>
<strong><em>to 12 minutes.</em></strong> <br>
Alexandre Henzen</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="3810---closure-and-contacts"><a href="https://youtu.be/IfNRciJJ6x4?t=2220" target="_blank" rel="noopener noreferrer">38:10</a> - Closure and contacts<a class="hash-link" href="#3810---closure-and-contacts" title="Direct link to heading">​</a></h3><p><strong>WL:</strong> <em>Alexandre, thank you very much for being available for this interview.</em></p><p><strong>AH:</strong> Thank you, I am available for anyone who wants to contact me, just go to (my profile on) LinkedIn, search for Alexandre Henzen (<a href="https://www.linkedin.com/in/alexandre-henzen-1b234130" target="_blank" rel="noopener noreferrer">link here</a>) and we can exchange ideas, I always like to discuss new technologies.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="comment">Comment!<a class="hash-link" href="#comment" title="Direct link to heading">​</a></h2><p>Did you like this interview? What to share your experiences using Delphi, TMS Software products, how your company is going? Leave your comment!</p><p>(*) Photo by <a href="https://unsplash.com/@krakenimages?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">krakenimages</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Unsplash</a></p><p><a href="https://forum.landgraf.dev/t/20-years-of-delphi-and-tms-in-erp-software-an-interview/292" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Two webinars in two days: what’s happening in the Delphi world!]]></title>
            <link>https://www.landgraf.dev/en/webinars-whats-new-in-delphi-world</link>
            <guid>webinars-whats-new-in-delphi-world</guid>
            <pubDate>Mon, 22 Feb 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Amazing things are going to happen in the Delphi world. This week, two different, important webinars are going to announce great things coming in the Delphi world!]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="pexels-andrea" src="/assets/images/pexels-andrea-55fcadde35782b6586c69e612311346b.jpg" width="640" height="368" class="img_ev3q"></p><p>Amazing things are happening in the Delphi world! Amazing things are going to happen in the Delphi world. This week, two different, important webinars are going to announce great things coming in the Delphi world!</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="graphql-for-delphi-bpm-workflow-and-more-whats-coming-for-delphi-in-2021">GraphQL for Delphi, BPM Workflow, and more: what’s coming for Delphi in 2021:<a class="hash-link" href="#graphql-for-delphi-bpm-workflow-and-more-whats-coming-for-delphi-in-2021" title="Direct link to heading">​</a></h2><p>TMS Software is going to host a <strong>free, live, interactive webinar</strong> to show some recent technologies, features and products being developed to be released in 2021, many of them in a few weeks or months. <strong>GraphQL for Delphi, BPM Workflow, multitenancy, authorization and authentication, user management</strong>, and more!</p><p>The webinar happens:</p><p><strong>Tuesday, Feb 23, 2021, 3:00 PM – 4:00 PM GMT</strong></p><p><a href="https://www.tmssoftware.com/site/tmswebacademy.asp?id=63" target="_blank" rel="noopener noreferrer">Follow this link for more information and to register to attend for free!</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="whats-coming-in-delphi-cbuilder-and-rad-studio-1042-sydney">What’s Coming in Delphi, C++Builder, and RAD Studio 10.4.2 Sydney<a class="hash-link" href="#whats-coming-in-delphi-cbuilder-and-rad-studio-1042-sydney" title="Direct link to heading">​</a></h2><p>Find out what is coming in the next major release 10.4.2 Sydney of your favorite developer tool: RAD Studio, Delphi, &amp; C++Builder. This is your opportunity to join product management to understand how your productivity will be improved.</p><p>The <strong>What’s Coming in Delphi, C++Builder, and RAD Studio 10.4.2 Sydney</strong><a href="https://attendee.gotowebinar.com/notAvailable.tmpl" target="_blank" rel="noopener noreferrer"> webinar is offered at 3 <em>times</em></a> so you can find the one that best suites your schedule.</p><p>Wed, Feb 24, 2021 3:00 PM – 4:00 PM GMT <br>
Wed, Feb 24, 2021 6:00 PM – 7:00 PM GMT <br>
Thu, Feb 25, 2021 1:00 AM – 2:00 AM GMT <br></p><p>Register for free for both webinars and learn all the exciting next things coming to Delphi world!</p><p>(Photo by <a href="https://www.pexels.com/@olly/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels" target="_blank" rel="noopener noreferrer">Andrea Piacquadio</a> from <a href="https://www.pexels.com/photo/cheerful-ethnic-woman-against-vivid-red-painted-wall-3768892/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels" target="_blank" rel="noopener noreferrer">Pexels</a>)</p><p><a href="https://forum.landgraf.dev/t/two-webinars-in-two-days-whats-happening-in-the-delphi-world/289" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A new Embarcadero MVP has arrived!]]></title>
            <link>https://www.landgraf.dev/en/new-embarcadero-mvp-arrived</link>
            <guid>new-embarcadero-mvp-arrived</guid>
            <pubDate>Tue, 29 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[I'm honored to say that I've been recently regarded as an Embarcadero MVP and joined the team - the MVP directory now includes Wagner Landgraf as an MVP.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="laula" src="/assets/images/laula-288a637a187bbf897f037901993fdcf1.jpg" width="796" height="421" class="img_ev3q"></p><p><a href="https://www.embarcadero.com/" target="_blank" rel="noopener noreferrer">Embarcadero</a> has been running, for quite some time already, an <a href="https://www.embarcadero.com/embarcadero-mvp-program" target="_blank" rel="noopener noreferrer">MVP (Most Valuable Professional) program</a>, which, according to the official web site, “<em>chooses the “best of the best” Embarcadero community members to be trusted assets for our customers and prospects</em>”.</p><p>In other words, Embarcadero MVPs are professionals who are well regarded by being technically skilled in Delphi (and other Embarcadero products) and helping in its evangelization, by writing articles, speaking at conferences, webinars, among others.</p><p><strong>I’m honored to say that I’ve been recently regarded as an Embarcadero MVP and joined the team</strong> – the MVP directory now includes <a href="https://www.embarcadero.com/resources/partners/mvp-directory?limitstart=180" target="_blank" rel="noopener noreferrer">Wagner Landgraf as an MVP</a>. I’ve also been kindly mentioned by <a href="https://www.ideasawakened.com/" target="_blank" rel="noopener noreferrer">Darian Miller</a> in <a href="https://www.ideasawakened.com/post/now-an-embarcadero-mvp-for-delphi" target="_blank" rel="noopener noreferrer">his post where he announces</a> that he’s also become an Embarcadero MVP (his post has interesting and detailed information about the Embarcadero MVP program, check it if you want to know more).</p><p>Well, I just wanted to share with you this great news! I hope Delphi community continues to increase and gets stronger, and I will keep doing my part for that to happen. Thank you for your support and feel free to leave your comment below.</p><ul><li>Photo by <a href="https://unsplash.com/@laulaco?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Laula</a> Co on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener noreferrer">Unsplash</a></li></ul><p><a href="https://forum.landgraf.dev/t/a-new-embarcadero-mvp-has-arrived/285/5" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Top content and free prizes at DelphiCon 2020]]></title>
            <link>https://www.landgraf.dev/en/delphicon-2020-free-prizes</link>
            <guid>delphicon-2020-free-prizes</guid>
            <pubDate>Tue, 27 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[DelphiCon2020 will take place on Nov 17-19. Get free or discounted training course and from Landgraf.Dev at the event. Register for free!]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="DelphiCon-2020" src="/assets/images/DelphiCon-2020-cfff11777bc848693ae46213a3058296.png" width="2016" height="480" class="img_ev3q"></p><p>In this unusual year, Embarcadero is bringing us the <a href="https://delphicon.embarcadero.com/" target="_blank" rel="noopener noreferrer">DelphiCon 2020 Worldwide</a> event: the official online conference about Delphi. It’s a <strong>free, 3-day online</strong> event, which will happen on <strong>November 17h through 19th</strong> with extraordinary content and expert speakers.</p><p>We at <strong>Landgraf.dev</strong> are happy to offer to all DelphiCon 2020 attendees <strong>30% discount</strong> on our training courses <a href="https://courses.landgraf.dev/p/web-applications-with-delphi-tms-web-core" target="_blank" rel="noopener noreferrer">Introduction to TMS Web Core</a> and <a href="https://courses.landgraf.dev/p/tms-business-masterclass-bundle" target="_blank" rel="noopener noreferrer">TMS Business Masterclass</a>. In addition to that, we will also offer <strong>3 (three) free enrollments</strong> to our <a href="https://courses.landgraf.dev/p/web-applications-with-delphi-tms-web-core" target="_blank" rel="noopener noreferrer">Introduction to TMS Web Core</a> training course for winners to be selected during the event!</p><p>You will attend sessions from a stellar team of speakers like <a href="https://blog.marcocantu.com/" target="_blank" rel="noopener noreferrer">Marco Cantu</a>, <a href="https://parnassus.co/about/" target="_blank" rel="noopener noreferrer">David Millington</a>, <a href="https://tmssoftware.com/site/default.asp" target="_blank" rel="noopener noreferrer">Bruno Fierens</a>, <a href="https://flixengineering.com/" target="_blank" rel="noopener noreferrer">Dr. Holger Flick</a>, <a href="https://www.drbob42.com/" target="_blank" rel="noopener noreferrer">Bob Swart</a>, <a href="https://www.andreamagni.eu/" target="_blank" rel="noopener noreferrer">Andrea Magni</a>, <a href="https://github.com/NickHodges" target="_blank" rel="noopener noreferrer">Nick Hodges</a>, <a href="https://delphisorcery.blogspot.com/" target="_blank" rel="noopener noreferrer">Stefan Glienke</a>, <a href="https://github.com/gabr42" target="_blank" rel="noopener noreferrer">Primož Gabrijelcic</a>, <a href="https://delphibydesign.com/" target="_blank" rel="noopener noreferrer">Ray Konopka</a>, <a href="https://www.kudzuworld.com/" target="_blank" rel="noopener noreferrer">Chad Hower</a>, <a href="https://caryjensen.blogspot.com/" target="_blank" rel="noopener noreferrer">Cary Jensen</a>, <a href="https://learndelphi.tv/" target="_blank" rel="noopener noreferrer">Alister Christie</a> and <a href="http://www.danieleteti.it/" target="_blank" rel="noopener noreferrer">Daniele Teti</a>.</p><a href="https://delphicon.embarcadero.com/" target="_blank" rel="noopener noreferrer"><img loading="lazy" src="/img/landgraf/Delphicon_2020_Social_Banner.png" class="img_ev3q"></a><p>Don’t miss this opportunity and save your free seat at <a href="https://delphicon.embarcadero.com/" target="_blank" rel="noopener noreferrer">DelphiCon 2020 official site</a>!</p><p><a href="https://forum.landgraf.dev/t/top-content-and-free-prizes-at-delphicon-2020/275/3" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GraphQL from the perspective of a Delphi developer]]></title>
            <link>https://www.landgraf.dev/en/graphql-from-the-perspective-of-a-delphi-developer</link>
            <guid>graphql-from-the-perspective-of-a-delphi-developer</guid>
            <pubDate>Tue, 21 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[GraphQL is taking over APIs. In this article we will try to explain what GraphQL is and why it's being adopted, all from the perspective of a Delphi developer.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="timothe-courtier" src="/assets/images/timothe-courtier-b6d66119981dafc02d4ac926c29d01ac.jpg" width="640" height="424" class="img_ev3q"></p><p>By now you probably know about <a href="https://graphql.org/" target="_blank" rel="noopener noreferrer">GraphQL</a>, or at least you might have read or heard that name somewhere. <a href="https://en.wikipedia.org/wiki/GraphQL" target="_blank" rel="noopener noreferrer">Wikipedia says</a> that “GraphQL is an open-source data <a href="https://en.wikipedia.org/wiki/Query_language" target="_blank" rel="noopener noreferrer">query</a> and <a href="https://en.wikipedia.org/wiki/Data_manipulation_language" target="_blank" rel="noopener noreferrer">manipulation</a> language for <a href="https://en.wikipedia.org/wiki/API" target="_blank" rel="noopener noreferrer">APIs</a>, and a runtime for fulfilling queries with existing data”. If that doesn’t say much to you, or if you have read about GraphQL before but <strong>didn’t understand very well what it is</strong>, then this article is for you. Its purpose is to try – again – to explain what GraphQL is, its advantages and disadvantages, <strong>from the perspective of a Delphi</strong> (and also <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer">TMS XData</a>) developer.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-graphql">What is GraphQL?<a class="hash-link" href="#what-is-graphql" title="Direct link to heading">​</a></h2><p>There are <strong>plenty</strong> of articles explaining what GraphQL is. The <a href="https://graphql.org/" target="_blank" rel="noopener noreferrer">official site</a>, the <a href="https://en.wikipedia.org/wiki/GraphQL" target="_blank" rel="noopener noreferrer">Wikipedia article</a>, tutorials <a href="https://www.howtographql.com/" target="_blank" rel="noopener noreferrer">here</a> and <a href="https://medium.com/devgorilla/what-is-graphql-f0902a959e4" target="_blank" rel="noopener noreferrer">there</a>. So I will try to not be repetitive here and <strong>explain it my way</strong>, converting the points I was most confused of when I was learning about it. First of all:</p><p><img loading="lazy" alt="GraphQL-logo-horizontal" src="data:image/png;base64,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" width="494" height="156" class="img_ev3q"></p><ul><li>GraphQL is <strong>NOT</strong> a framework or programming library;</li><li>GraphQL is <strong>NOT</strong> a server application;</li><li>GraphQL is <strong>NOT</strong> a database server or wrapper.</li></ul><p>When I first started learning about it, this got me confused as well. What is GraphQL then?</p><ul><li>GraphQL is a <strong>SPECIFICATION</strong> for a language and a runtime (<a href="http://spec.graphql.org/" target="_blank" rel="noopener noreferrer">available here</a>).</li></ul><p>In other words, GraphQL strictly specifies a <strong>query language</strong> and how runtime libraries should “execute” that language. Using a <strong>very rough</strong> analogy, it’s like a strict specification of SQL, explaining how SQL statements should be parsed and validated, and providing algorithms defining how database servers should execute those SQL and return data.</p><p>Of course, then you have several GraphQL implementations, which are indeed <strong>libraries in several different languages</strong> like <a href="https://www.graphql-java.com/" target="_blank" rel="noopener noreferrer">Java</a>, <a href="https://graphql-dotnet.github.io/" target="_blank" rel="noopener noreferrer">.NET</a>, <a href="https://graphql.org/graphql-js/" target="_blank" rel="noopener noreferrer">JavaScript</a>, that allow you to create, for example, servers that, through HTTP, allow clients to <strong>send such queries and get the response of such query execution in JSON format</strong>. But note that even that HTTP communication is not <strong>strictly specified</strong>. It’s just one of many ways to “execute” a GraphQL document (a text file containing a query in valid GraphQL language).</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="graphql-is-strongly-typed">GraphQL is “strongly typed”<a class="hash-link" href="#graphql-is-strongly-typed" title="Direct link to heading">​</a></h2><p>This is one key point of GraphQL. Whatever you are going to do with it, you always start from a “schema”, which specifies all the types available in the language. The following is an example of a GraphQL schema:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">type Query {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  human(id: Int): Human</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">type Human  {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  id: Int!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: String!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  homePlanet: String</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  height: Float</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  mass: Float</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The above schema specifies two object types: Query and Human, the fields in each object type, and the type returned for each field. Fields can also have arguments (parameters), also with its specified type (human field in Query type has the parameter id of type Int).</p><p>If you are implementing a server that supports GraphQL, <strong>you have to define a schema first</strong>. If you are writing a client that sends GraphQL queries to a server, those queries <strong>must comply with the specified schema</strong>. You cannot create a query asking for a type that doesn’t exist. Or a field that doesn’t exist. Or even using a field parameter (argument) of a different data type than the specified.</p><p>It’s interesting to note that in a world that <strong>dynamic languages are so popular today</strong> (JavaScript being the main one), one of the key claims about GraphQL advantages is that it’s strongly typed – something I personally like, as a Delphi developer.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="graphql-documents-queries">GraphQL documents (queries)<a class="hash-link" href="#graphql-documents-queries" title="Direct link to heading">​</a></h2><p>Once the GraphQL schema is defined, clients can write GraphQL documents (queries). In summary, a GraphQL query just lists the fields that we want the values for, and if such fields return objects, the fields of those objects, and on and on at any depth level you want. For example:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    human(id: 1000)  {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        height</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The query above asks for the value of human field of type Query (implicit by default because of its name), passing the value 1000 for the id parameter. Since human field returns an object of type Human, the query also asks for just fields name and height for such object.</p><p>If such query is sent via HTTP to a server implementing GraphQL, the server will execute the query and return its results. A possible result could be something like this:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "data": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "human": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "name": "Luke Skywalker",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "height": 1.72</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>You get what you asked for: the result of human field, and the name and height fields of the Human object. It’s also important to explain two things:</p><ol><li>The GraphQL query is <strong>NOT</strong> a JSON document, even though it looks like one. It’s a GraphQL query, with its specific syntax as defined by the specification.</li><li>The result data <strong>IS a JSON document</strong>, and the way it is formatted is <strong>NOT</strong> in the specification. It could be a XML document or whatever other format.</li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="graphql-and-http">GraphQL and HTTP<a class="hash-link" href="#graphql-and-http" title="Direct link to heading">​</a></h2><p>GraphQL specification <strong>doesn’t say anything about HTTP</strong>. That really got me confused at the beginning, because I was reading all around about how GraphQL compares to REST, how it’s a new way to build APIs, etc. But the thing is GraphQL is not about HTTP, but only about the query language and how it should return results, in any format.</p><p>But of course, <strong>HTTP became the main way of executing GraphQL queries</strong>, and JSON end up being the most used format to return the results of such execution. The closest to a standard we have are some guidelines about <a href="https://graphql.org/learn/serving-over-http/" target="_blank" rel="noopener noreferrer">how to serve GraphQL over HTTP</a>, in the official GraphQL site.</p><p>In summary, there are no multiple endpoints: GraphQL queries are sent to a single URL endpoint, either directly in the URL in GET requests:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://myapi/graphql?query={{human(id:1000){name,height}}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>or in a JSON document, in POST requests:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">POST /graphql HTTP/1.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Host: myapi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "query": "{{human(id:1000){name,height}}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And the server response is usually a JSON document, as we saw above. It’s also interesting to note that if the GraphQL query is accepted, it always return an HTTP status code 200, even if the query execution fails. If there are errors, they appear inside the JSON document (error format is included in GraphQL specification):</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "errors": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      "message": "Cannot query field \"homePlanet\" on type \"Human\"."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-graphql">Why GraphQL?<a class="hash-link" href="#why-graphql" title="Direct link to heading">​</a></h2><p>Now that we have quickly covered what GraphQL is, it’s time to learn <strong>why</strong> it’s beginning to be so widely used.</p><p>There are several articles mentioning <a href="https://www.altexsoft.com/blog/engineering/graphql-core-features-architecture-pros-and-cons/" target="_blank" rel="noopener noreferrer">GraphQL advantages and disadvantages compared to REST</a>, explaining why <a href="https://webapplog.com/graphql/" target="_blank" rel="noopener noreferrer">it’s taking over APIs</a>, directly <a href="https://www.imaginarycloud.com/blog/graphql-vs-rest/" target="_blank" rel="noopener noreferrer">comparing GraphQL and REST</a>, listing reasons <a href="https://www.mediacurrent.com/blog/5-reasons-why-you-should-consider-graphql-server/" target="_blank" rel="noopener noreferrer">why you should consider a GraphQL server</a> and even <a href="https://xalitech.com/graphql-how-to-convince-your-boss/" target="_blank" rel="noopener noreferrer">how to convince your boss to use GraphQL</a> (despite the awful name – in my opinion – this last one is a nice article about GraphQL).</p><p>I will try to summarize what I think are the key reasons. I will also try to compare it with REST and specially with <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer">TMS XData</a> – a framework to build REST API servers using Delphi.</p><p>Clients choose what to fetch
In REST, if you want to retrieve an invoice, you will do something like this:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET /invoice/10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "id": 10,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "total": 152</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then, if you want to retrieve the customer of the invoice, you need another request:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET /invoice/10/customer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "id": 115,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "name": "Joe Doe"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Of course, your REST server could return customer data inline in the first request, when invoice is requested. But then, what if the client <strong>does not</strong> want customer data? The server will return more data than the client needs. In GraphQL you write the query <strong>asking just the data you want</strong>. Either without customer data:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  invoice(10)  {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    total</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>or with customer data:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  invoice(10) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    total</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    customer {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And you don’t have to modify your server for that, or create multiple endpoints. Developing <strong>client applications with GraphQL</strong>, specially mobile and web applications, is a breeze, and performs very well – in a single request you get all the data you need, and only the data you need.</p><p>On the other hand, your REST server could implement mechanisms to allow your client to better choose what it needs. That’s what XData does, for example, with the <a href="https://doc.tmssoftware.com/biz/xdata/guide/aurelius.html#expand" target="_blank" rel="noopener noreferrer">$expand query filter mechanism</a>. When an invoice is request from the client, it comes with minimum customer information (just the id):</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET /invoice/10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "id": 10,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "total": 152,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "customer@xdata.ref": "customer(115)"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>but if the client wants full customer information, he can just ask for it using $expand:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET /invoice/10?$expand=customer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "id": 10,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "total": 152,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "customer": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "id": 115,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "name": "John Doe"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>When I first learned about GraphQL, my thought was: this might be an advantage in some situations, but if you are using a XData REST server, it’s not a big difference. With XData, there are ways to get all related (associated) information in one single server request (roundtrip).</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="schema-introspection-self-documentation">Schema introspection (Self-documentation)<a class="hash-link" href="#schema-introspection-self-documentation" title="Direct link to heading">​</a></h2><p>As I’ve mentioned above, GraphQL is based on a schema which holds information about all types, fields and other elements needed to describe and validate a GraphQL query. Not only that, GraphQL itself provides a way for clients to retrieve information about such schema, a process called introspection. For example, this query will ask for all fields of the type Human, with their respective types:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  __type(name: "Human") {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fields {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      type {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        kind</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The query would return something like this:</p><div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"data"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"__type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “Human”</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"fields"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token null keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kind"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"NON_NULL"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token null keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kind"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"NON_NULL"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “homePlanet”</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"String"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kind"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SCALAR"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “height</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “Float</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kind"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SCALAR"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “mass”</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> “Float”</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kind"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SCALAR"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>This brings a lot of benefits. First, all clients <strong>know in advance what information they can retrieve</strong>. That also means that a GraphQL server is <strong>self-documented</strong>, since the schema already describes what it can offer.</p><p>For a client to use a REST server, for example it necessarily needs to go to some kind of documentation, because the client simply don’t know that an invoice resource is available at some endpoint /invoice/:id, or what is the type of the id, or any other ways to create an invoice, different ways to query, etc. That’s not the case with GraphQL: everything is there.</p><p>Also, this makes it possible to create lots of <strong>tools around GraphQL</strong>. I will mention it later in this article.</p><p>Again, REST servers also have a nice way to provide meta information about it: <a href="https://swagger.io/" target="_blank" rel="noopener noreferrer">Swagger</a>. The problem, of course, is that not all REST servers implement Swagger. Also, some REST implementations require you to do lots of extra coding to properly build the Swagger file, like adding lots of attributes just to flag each endpoint, which parameters are available in each endpoint, etc.</p><p>Luckily, TMS XData <a href="https://doc.tmssoftware.com/biz/xdata/guide/tasks.html#openapiswagger-support" target="_blank" rel="noopener noreferrer">does support Swagger</a> beautifully, and in a <strong>very automatic way</strong>. Basically, after you created your REST API with XData, your <strong>Swagger document is provided automatically</strong>, because it has all metadata information from the interfaces declared as endpoints, thanks to the way XData is built. So the “introspection” of a XData REST server can be enabled with just a few lines to code, and available at a single endpoint:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://myapi/openapi/swagger.json</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Again, this is a nice GraphQL feature, but with proper tool or effort, REST servers can also provide such mechanism.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="versioning">Versioning<a class="hash-link" href="#versioning" title="Direct link to heading">​</a></h2><p>From <a href="https://graphql.org/learn/best-practices/" target="_blank" rel="noopener noreferrer">GraphQL website</a>: Why do most APIs version? When there’s limited control over the data that’s returned from an API endpoint, <strong>any change</strong> can be considered a breaking change, and breaking changes require a new version. If adding new features to an API requires a new version, then a tradeoff emerges between releasing often and having many incremental versions versus the understandability and maintainability of the API.</p><p>In other words: it’s easier to expand and evolve a GraphQL API because clients <strong>never receive information that they didn’t ask for</strong>. Adding a new field to Human type, for example, will be harmless because existing clients will never receive that new field, because they never asked for it. On the other hand, adding a new field to a REST endpoint will result in a different response for existing clients (the new added field).</p><p>Thus, GraphQL makes it easier to evolve your API and, in theory – in most cases, not all – you don’t need to version your API.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="tools-and-libraries">Tools and libraries<a class="hash-link" href="#tools-and-libraries" title="Direct link to heading">​</a></h2><p>In my opinion, <strong>this is where GraphQL really shines</strong>, and which justifies using it. As you saw, when you use nice libraries for building REST servers, like TMS XData, you kind of minimize the advantages of GraphQL over REST. The limitations of REST are known, and a good framework, over time, will add features to solve the problems. That’s what XData does.</p><p>But when you have an ecosystem, it’s a different thing. You can’t control or define everything that will be built around a tool or a specification. Since the release of GraphQL, lots of tools and libraries have been built, and once you have your GraphQL API, you can benefit from all of them.</p><p>This nice article describes <a href="https://nordicapis.com/10-awesome-tools-and-extensions-for-graphql-apis/" target="_blank" rel="noopener noreferrer">10 awesome tools and extensions for GraphQL APIs</a>, and I will explicitly mention some of them here.</p><p><a href="https://github.com/graphql/graphiql" target="_blank" rel="noopener noreferrer">GraphiQL</a> and/or <a href="https://github.com/graphql/graphql-playground" target="_blank" rel="noopener noreferrer">GraphQL Playground</a></p><p>Simply put, <a href="https://github.com/graphql/graphiql" target="_blank" rel="noopener noreferrer">GraphiQL</a> and <a href="https://github.com/graphql/graphql-playground" target="_blank" rel="noopener noreferrer">GraphQL Playground</a> are a “GraphQL IDE”. From there you can write your queries with full code completion (GraphQL has a schema, remember?), syntax check, detailed error messages and positions, among others. It really makes it easy to write and run GraphQL queries.</p><p><img loading="lazy" alt="GraphQL-playground" src="/assets/images/GraphQL-playground-c3abf700c007427b820fc303b2468b62.png" width="768" height="442" class="img_ev3q"></p><p><a href="https://github.com/IvanGoncharov/graphql-voyager" target="_blank" rel="noopener noreferrer">GraphQL Voyager</a></p><p><a href="https://github.com/IvanGoncharov/graphql-voyager" target="_blank" rel="noopener noreferrer">GraphQL voyager</a> is a tool to represent any GraphQL API as an interactive graph. Another tool that takes advantage of the GraphQL schema introspection. You quickly see all your schema in a graphical way, and how each field and type relates to each other. It has a nice <a href="https://ivangoncharov.github.io/graphql-voyager/" target="_blank" rel="noopener noreferrer">live demo</a> you can try.</p><p><img loading="lazy" alt="GraphQL-Voyager" src="/assets/images/GraphQL-Voyager-a20f0325a9646e53f5fdeaab0089bbfa.png" width="1536" height="836" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="browser-extensions">Browser extensions<a class="hash-link" href="#browser-extensions" title="Direct link to heading">​</a></h2><p>There are several GraphQL extensions to your favorite browser that helps you out when building web applications that connect to GraphQL. Both <a href="https://chrome.google.com/webstore/detail/graphql-network/igbmhmnkobkjalekgiehijefpkdemocm?hl=en-GB" target="_blank" rel="noopener noreferrer">Chrome GraphQL Network</a> and <a href="https://addons.mozilla.org/en-US/firefox/addon/graphql-developer-tools/" target="_blank" rel="noopener noreferrer">Firefox GraphQL DevTools</a> extensions are valuable tools that allows you to debug, inspect requests, track errors and increase your productivity when working with GraphQL. They are just honorable mentions, of course there are much more extensions for different purposes that you can use.</p><p><img loading="lazy" alt="GraphQL-browser-extension" src="/assets/images/GraphQL-browser-extension-a3a01905b0afd209ddef798198d39c1e.png" width="1278" height="722" class="img_ev3q"></p><p>Clients all around</p><p>This deserves a separated article, but of course it’s worth mention that libraries for GraphQL clients are all around. If you want to build web or mobile applications, there are clients for <a href="https://relay.dev/" target="_blank" rel="noopener noreferrer">React</a>, <a href="https://apollo.vuejs.org/" target="_blank" rel="noopener noreferrer">VueJs</a>, <a href="https://apollo-angular.com/docs/" target="_blank" rel="noopener noreferrer">Angular</a>, <a href="https://www.apollographql.com/docs/ios/" target="_blank" rel="noopener noreferrer">Swift</a>, <a href="https://github.com/ExpediaGroup/graphql-kotlin/" target="_blank" rel="noopener noreferrer">Kotlin</a>, <a href="https://github.com/zino-hofmann/graphql-flutter" target="_blank" rel="noopener noreferrer">Flutter</a> and many other development tools and libraries.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="final-notes">Final notes<a class="hash-link" href="#final-notes" title="Direct link to heading">​</a></h2><p>I didn’t name this section “Conclusion” because there is nothing to conclude. This article also didn’t have an intention to compare GraphQL with anything – even when I do comparisons with REST here and there.</p><p>The idea was to introduce this technology to newcomers, and also for those who already knew about it, talk about it from the perspective of a Delphi developer, hoping that I had clarified some points that might be obscure for those used to Delphi – I tried to focused exactly on the points that got me confused when I started learning about GraphQL.</p><p>What about you? Do you have any experience with GraphQL? Are you using it in production? What do you think about it? Are you a newcomer and want to ask something or share your doubts? Please leave your comment following the link below and let’s discuss about it!</p><p><a href="https://forum.landgraf.dev/t/graphql-from-the-perspective-of-a-delphi-developer/245" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Catching memory leaks in Delphi: a definitive list of tools]]></title>
            <link>https://www.landgraf.dev/en/catching-memory-leaks-in-delphi</link>
            <guid>catching-memory-leaks-in-delphi</guid>
            <pubDate>Fri, 15 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[A definitive list of tools to detect memory leaks in Delphi: FastMM, AQTime, EurekaLog, among others.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="hunter-haley" src="/assets/images/hunter-haley-5440aae529cfa3221cb9a03a3434663c.jpg" width="640" height="427" class="img_ev3q"></p><p>Detecting memory leaks is a very important task in Delphi development. I’ve <a href="/en/delphi-memory-leaks/">written about it before</a>, stressing that in server applications like the ones built with <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer">TMS XData</a> this is even more important.</p><p>With the <a href="https://blogs.embarcadero.com/get-ready-for-the-10-4-beta-with-update-subscription/" target="_blank" rel="noopener noreferrer">upcoming release of Delphi 10.4 version</a>, this will become even more relevant. A unified memory management <a href="https://blogs.embarcadero.com/august-2019-delphi-android-beta-plans-august-roadmap-update/" target="_blank" rel="noopener noreferrer">has been promised since last year</a>, and <a href="https://github.com/ideasawakened/DelphiKB/wiki/D27.Next.10.4.0.0" target="_blank" rel="noopener noreferrer">it looks it has arrived</a>.</p><p>As everything in life, this change isn’t 100% good or bad, <a href="https://dalijap.blogspot.com/2020/03/unified-memory-management-coming-with.html" target="_blank" rel="noopener noreferrer">there are pros and cons in it</a>, but one thing is clear: the way memory is managed now is the same in all platforms, so the <strong>ways to detect memory leaks in different platforms are more similar now</strong>. I personally think this is a good thing. It’s also important to note that this doesn’t necessarily mean “more leaks” in mobile platforms. The “old” (still valid when I’m writing this article) <a href="https://docwiki.embarcadero.com/RADStudio/Rio/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers" target="_blank" rel="noopener noreferrer">ARC mechanism</a> also had its problems, and in my opinion harder to detect, like <a href="https://en.wikipedia.org/wiki/Reference_counting#Dealing_with_reference_cycles" target="_blank" rel="noopener noreferrer">dealing with cyclical references</a>.</p><p>But well, enough with this too long introduction. The humble purpose of this article is to be a <strong>definitive and up-to-date list of all tools that you can use to detect memory leaks in Delphi applications</strong>. Pointing out that memory management is unified just means that these tools are even more relevant than ever. Detecting and fixing a memory leak in Windows will help even more that your non-Windows applications will not have memory leaks as well.</p><p>So, to the list!</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="fastmm-free"><a href="https://github.com/pleriche/FastMM4" target="_blank" rel="noopener noreferrer">FastMM</a> (Free)<a class="hash-link" href="#fastmm-free" title="Direct link to heading">​</a></h2><p><img loading="lazy" alt="fastmm-logo" src="/assets/images/fastmm-logo-5551d22b18647a5456c9e54f472db6f6.jpg" width="300" height="150" class="img_ev3q"></p><p>FastMM (or to be more specific, FastMM4) is the <em>de-facto</em> <strong>standard tool to detect memory leaks in Delphi</strong>. The reason is simple: it’s the <a href="https://docwiki.embarcadero.com/RADStudio/Rio/en/Memory_Management#The_FastMM_Memory_Manager_.28Win32_and_Win64.29" target="_blank" rel="noopener noreferrer">default memory manager in Delphi</a>, so it’s already built-in and ready to use.</p><p>Since FastMM allocates and deallocates memory in your application, who else is better to report the blocks that have not been deallocated? All that it takes to start working with it is add a single line in your project:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ReportMemoryLeaksOnShutdown := True;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And <em>voilà</em>, your application will report all memory leaks when it shuts down. If there are leaks at the end of your application, a dialog will be displayed showing all the leaks.</p><p><img loading="lazy" alt="fastmm-leak-report" src="/assets/images/fastmm-leak-report-7f2fb3fe2bd3c1a2e8f7551a5e0e8a16.png" width="768" height="334" class="img_ev3q"></p><p><strong>All Delphi developers should add that line to their applications. Really</strong>. I don’t even know why this is not added by default in Delphi, at least wrapped by a {$IFDEF DEBUG} directive. Maybe for historical reasons.</p><p>So, if it’s built-in, default, works, why don’t we finish this article here, then? Well, there are some gotchas.</p><p>Delphi includes a <strong>stripped out version of FastMM4</strong>. It doesn’t have all the nice debugging tools you need (to know, for example, where in the code your leaked memory was allocated). To do that, you have to use the full FastMM4 version, available on the <a href="https://github.com/pleriche/FastMM4" target="_blank" rel="noopener noreferrer">FASTMM4 public GitHub repository</a>.</p><p>You also have to use a DLL for the full debugging features; it’s not cross-platform: it only officially works for Windows (it looks like a macOS version is available in the official repo, but I never tried it). And even though it has lots of features, to use it <a href="https://delphiprogrammingdiary.blogspot.com/2018/09/fastmm-and-how-to-use-in-delphi-project.html" target="_blank" rel="noopener noreferrer">you have to deal with .INC files and manual configurations to make it work</a>, which might not be comfortable for some users.</p><p>But all in all, is a great tool, the “standard” tool to get memory leaks in Delphi. <em>(Side note: <a href="https://github.com/pleriche/FastMM5" target="_blank" rel="noopener noreferrer">FASTMM5 has just been released</a>. We haven’t tested it yet but it looks like it brings a <a href="https://en.delphipraxis.net/topic/2751-fastmm5-now-released-by-pierre-le-riche-small-background-story/" target="_blank" rel="noopener noreferrer"><strong>great</strong> improvement in performance for multithreaded applications</a>, we can’t wait to test it in TMS XData.)</em></p><p><strong>Pros</strong></p><ul><li>Free;</li><li>Full source code;</li><li>Built-in in Delphi;</li><li>Easy to setup;</li><li>Lots of advanced features.</li></ul><p><strong>Cons</strong></p><ul><li>Windows only;</li><li>Needs external DLL for debugging features;</li><li>Not user-friendly to setup and use advanced features (no built-in GUI);</li><li>Only reports leaks on memory allocated by FASTMM itself.</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="leakcheck-free"><a href="https://bitbucket.org/shadow_cs/delphi-leakcheck" target="_blank" rel="noopener noreferrer">LeakCheck</a> (Free)<a class="hash-link" href="#leakcheck-free" title="Direct link to heading">​</a></h2><p>Delphi LeakCheck is another great option for detecting memory leaks. It’s also <strong>free, open source</strong>, and has some advantages over FastMM: it’s <strong>cross-platform</strong>, meaning you can check leaks directly in mobile and Linux applications; and it <strong>integrates very well with unit test frameworks</strong> (namely <a href="https://docwiki.embarcadero.com/RADStudio/Rio/en/DUnit_Overview" target="_blank" rel="noopener noreferrer">DUnit</a> and <a href="https://docwiki.embarcadero.com/RADStudio/Rio/en/DUnitX_Overview" target="_blank" rel="noopener noreferrer">DUnitX</a>).</p><p>The way to get started with it is <strong>similar to FastMM</strong>: add LeakCheck unit as the first used unit in your dpr uses clause, and it will plugin and be ready to use. Setting up for unit testing is a little more complicated though, but that’s part of the game.</p><p>One small disadvantage is that to use it you are almost on your own: the project <strong>hasn’t received updates</strong> for a while (which isn’t necessarily bad since it’s working). But that means probably you won’t get much help directly from the author (I never tried, to be fair). There is also <strong>not much information about it in the web</strong>, I just found one <a href="https://medium.com/@Zawuza/delphi-leakcheck-2242270ef42d" target="_blank" rel="noopener noreferrer">single article that explains how to use</a> it besides the detailed description in the official Bitbucket repository itself.</p><p><strong>Pros</strong></p><ul><li>Free;</li><li>Full source code;</li><li>Cross-plataform;</li><li>Integrates well with unit testing (DUnit and DUnitX).</li></ul><p><strong>Cons</strong></p><ul><li>Not much information around about how to use it;</li><li>No recent updates, no official support;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="deleaker-commercial"><a href="https://www.deleaker.com/" target="_blank" rel="noopener noreferrer">Deleaker</a> (Commercial)<a class="hash-link" href="#deleaker-commercial" title="Direct link to heading">​</a></h2><img loading="lazy" src="/img/landgraf/deleaker-logo.png" class="img_ev3q"><p>Delaker is the only commercial tool in the list that is <strong>exclusively dedicated to catch memory leaks</strong>. That reflects in the product, which provides you with really nice features for detecting memory leaks.</p><img loading="lazy" src="/img/landgraf/deleaker-screenshot.png" class="img_ev3q"><p>Differently from the previous two tools, and similar to the following ones, it has a <strong>friendly GUI for you to setup the environment</strong> and see the results, which can be used standalone or integrated to Delphi IDE. It can also detect way <strong>more types of memory leaks</strong>: GDI leaks, leaks of Windows USER objects and handles, leaks in Windows APIs, in 3rd party DLLs, etc. Exactly because of that, it provides options for you to <strong>easily ignore several types of leaks</strong> – if you don’t, you will get a gazillion of leaks in just a regular app execution.</p><p>Another nice feature is the ability to take <strong>snapshots of the memory allocation</strong>. This allows you to detect leaks not only during the lifetime of your whole application, but in some specific operations of it.</p><p><strong>Pros</strong></p><ul><li>Friendly GUI that can be used standalone or integrated into Delphi IDE;</li><li>Detects all types of leaks;</li><li>Command-line tool for CI integration;</li><li>Memory usage snapshots;</li><li>Official support;</li></ul><p><strong>Cons</strong></p><ul><li>Paid license ($99 for Home License, $399 for Single Developer License);</li><li>Windows only;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="eurekalog-commercial"><a href="https://www.eurekalog.com/" target="_blank" rel="noopener noreferrer">EurekaLog</a> (Commercial)<a class="hash-link" href="#eurekalog-commercial" title="Direct link to heading">​</a></h2><p><img loading="lazy" alt="eurekalog-demo" src="/assets/images/eurekalog-demo-5826397e7cbdecb489d4a8787c1e2df0.png" width="300" height="65" class="img_ev3q"></p><p>EurekaLog is an old player. It’s <strong>been around for decades</strong> – I couldn’t find any info on their web site about when first version was released, the <a href="https://www.eurekalog.com/showchangelog_delphi.php" target="_blank" rel="noopener noreferrer">oldest information is for EurekaLog 4.0</a> which was released in 2002, simply 18 years ago.</p><p><img loading="lazy" alt="eurekalog-leaks" src="/assets/images/eurekalog-leaks-c1307dfb3e519d8ea6a72dbc3d0006bc.png" width="608" height="435" class="img_ev3q"></p><p>It’s not a tool dedicated exclusively to catch memory leaks. Instead, it has a <strong>full range of features</strong>, memory leak detection being just a “side” feature. The purpose of EurekaLog is to <strong>detect any problems in your application</strong> – exceptions, leaks, etc. – at the customer side, and report it back to you.</p><p>Thus, it’s a great tool to help you improve the quality of your software and provide good support to your customers, since you will get error and leak reports from all your customers, in different environments, doing different operations. It also helps you to find tricky bugs that only happen at customer side (you know it well, those “cannot reproduce it here” situations).</p><p><strong>Pros</strong></p><ul><li>Detects both memory and resource leaks;</li><li>Leaks and errors detected at customer side can be sent automatically to you;</li><li>Lots of other features: bug reporting, integration with bug tracking systems, among others;</li><li>Official support;</li></ul><p><strong>Cons</strong></p><ul><li>Paid license ($149 for Professional License, $249 for Enterprise License);</li><li>Windows only;</li><li>Not many advanced features for memory leak detection;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="madexcept-commercial"><a href="http://madexcept.com/" target="_blank" rel="noopener noreferrer">madExcept</a> (Commercial)<a class="hash-link" href="#madexcept-commercial" title="Direct link to heading">​</a></h2><p><img loading="lazy" alt="madexcept-logo" src="/assets/images/madexcept-logo-7f56803d90a0f8c8fbb7c29ae28e4f62.gif" width="200" height="105" class="img_ev3q"></p><p>I use to say that MadExcept is the “cousin” of EurekaLog. They are both available for about the same time (around 20 years or more). They share similar features. They have more or less the same purpose. And so on.</p><p>And, funny enough, there isn’t a “winner”. If you look around the web about comparisons between the two, you will <strong>never come to a conclusion on which is “better”</strong>. Customers of both products are <strong>usually satisfied</strong>, and also usually they can’t comment on the competitor because they never used it. That’s my case, actually. I’m a <strong>happy EurekaLog customer</strong> (although I don’t use it to catch memory leaks), and I never used madExcept. But it could simply be the opposite. I believe I could be well served with madExcept as well.</p><p>Thus, I considered madExcept pros and cons equal to EurekaLog’s. Maybe the only visible different is that while <strong>madExcept is cheaper</strong> (there is even a free version for non-commercial use), <strong>EurekaLog seems to be much more active</strong> and frequently updated.</p><p><strong>Pros</strong></p><ul><li>Free for non-commercial support;</li><li>Detects both memory and resource leaks;</li><li>Leaks and errors detected at customer side can be sent automatically to you;</li><li>Lots of other features: bug reporting, integration with bug tracking systems, among others;</li><li>Official support;</li></ul><p><strong>Cons</strong></p><ul><li>Paid license (€ 159 for full source license);</li><li>Windows only;</li><li>Not many advanced features for memory leak detection;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="aqtime-pro-commercial"><a href="https://smartbear.com/product/aqtime-pro/overview/" target="_blank" rel="noopener noreferrer">AQTime Pro</a> (Commercial)<a class="hash-link" href="#aqtime-pro-commercial" title="Direct link to heading">​</a></h2><p><img loading="lazy" alt="aqtime-logo" src="/assets/images/aqtime-logo-1d7d7859f946ef85de5436d8a77083d8.png" width="300" height="81" class="img_ev3q"></p><p>AQTime is a top-<strong>notch tool to make your code better</strong>. It’s really <strong>high standard</strong>, offering not only an <strong>advanced memory leak detection tool</strong> (with a nice GUI, snapshots, memory tracking, resource leaks detection, among others) but also <strong>performance profiling</strong> (with both instrumenting and sampling profilers), <strong>code coverage</strong>, code analysis, among others.</p><p><img loading="lazy" alt="aqtime-memory-leaks" src="data:image/png;base64,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" width="500" height="249" class="img_ev3q"></p><p>It’s really awesome tool, but it has its downsides: it’s <strong>pretty expensive</strong>, and it looks in “maintenance” mode – it receives updates more or less once a year, and the news are mostly “support for the new Delphi version”. Handful bug fixes over the years, and <strong>virtually no new features</strong>. But well, it still has no equivalent tool in Delphi world when it comes to many and powerful features.</p><p><strong>Pros</strong></p><ul><li>Detects memory, resource, GDI, handle leaks among others;</li><li>Real-time allocation monitor;</li><li>Snapshots;</li><li>Lots of other tools in the bundle (performance profiler, code coverage, etc.).</li></ul><p><strong>Cons</strong></p><ul><li>Pretty expensive ($719 for a node-locked license, $2279 for node-floating license);</li><li>Windows only;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="nexus-quality-suite-commercial"><a href="https://www.nexusdb.com/support/index.php?q=qualitysuite" target="_blank" rel="noopener noreferrer">Nexus Quality Suite</a> (Commercial)<a class="hash-link" href="#nexus-quality-suite-commercial" title="Direct link to heading">​</a></h2><p><img loading="lazy" alt="nexus-quality-logo" src="/assets/images/nexus-quality-logo-bae04c35ba70636526fcc73eee43b57f.png" width="150" height="150" class="img_ev3q"></p><p>In a similar way of EurekaLog and madExcept, I believe Nexus Quality Suite is somewhat related to AQTime. Both provide lots of tools to improve the quality of your software, and there are intersections between them.</p><p><img loading="lazy" alt="nexus-quality-screenshot" src="/assets/images/nexus-quality-screenshot-77a0e957b2a2fb0c624c400ff7398581.png" width="768" height="553" class="img_ev3q"></p><p>Nexus Quality suite provides memory and resource leak detectors, but also performance profilers, line timers, code coverage, and even a GUI automatic tester, among other things.</p><p>I haven’t tried the memory check tool myself, thus the pros and cons will be just based on what I see from web site:</p><p><strong>Pros</strong></p><ul><li>Detects memory and resource leaks;</li><li>Official support, active support forums;</li><li>Lots of other tools in the package</li></ul><p><strong>Cons</strong></p><ul><li>Paid license (AUD 490, around $300);</li><li>Windows 32-bit only.</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="tms-meminsight-commercial"><a href="https://ddobjects.de/dddebug" target="_blank" rel="noopener noreferrer">TMS MemInsight</a> (Commercial)<a class="hash-link" href="#tms-meminsight-commercial" title="Direct link to heading">​</a></h2><p>TMS MemInsight is an evolution from the former DDDebug. It’s a collection of debugging tools which contains several modules: a memory profiler, a thread viewer, a module viewer and an enhanced exception handler.</p><p><img loading="lazy" alt="dddebug-screenshot" src="/assets/images/dddebug-screenshot-d228e50392ae3d22144856eca3c52c86.png" width="480" height="379" class="img_ev3q"></p><p>One interesting thing I noted from TMS MemInsight is that it has a slightly different approach, by providing memory usage and statistics instantly from inside the application. I didn’t use it but it looks like it makes it easier to find bugs in the app, by interacting with it at the same time you analyze it.</p><p>It also works with packages which is a plus, provides more functions besides just memory leak detection, and even though it’s commercial, the license price is really accessible.</p><p><strong>Pros</strong></p><ul><li>Provides results directly inside your application, from a GUI;</li><li>Supports packages;</li><li>Official support;</li><li>Accessible license price (from €115).</li></ul><p><strong>Cons</strong></p><ul><li>Windows only;</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="spider-free"><a href="https://yavfast.github.io/dbg-spider/" target="_blank" rel="noopener noreferrer">Spider</a> (Free)<a class="hash-link" href="#spider-free" title="Direct link to heading">​</a></h2><p>Spider websites lists a lot of interesting features: analysis of exceptions, analysis of the realtime use of memory, analysis of memory leaks, analysis of the call stack, among others.</p><p><img loading="lazy" alt="spider-screenshot" src="/assets/images/spider-screenshot-e7f74a34bef4e73d0ccc3abd7bea51a1.png" width="768" height="519" class="img_ev3q"></p><p>The thing is, from the list, is a tool I just tried once, and I got confused with the user interface and the results themselves. I couldn’t use it, but maybe that’s just me. So it’s listed here, but I can’t make any fair judgement over it.</p><p><strong>Pros</strong></p><ul><li>Free;</li><li>Source code available;</li></ul><p><strong>Cons</strong></p><ul><li>Confusing user interface (personal opinion);</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="non-delphi-tools">Non-Delphi Tools<a class="hash-link" href="#non-delphi-tools" title="Direct link to heading">​</a></h2><p>In addition to the above tools that are Delphi specific, or at least also cover Delphi specifically – either integrating into source code, or in the IDE, etc. – there are also <strong>general-purpose tools</strong> for memory leak detection which can be helpful in some situations.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="valgrind-free"><a href="https://valgrind.org/" target="_blank" rel="noopener noreferrer">Valgrind</a> (Free)<a class="hash-link" href="#valgrind-free" title="Direct link to heading">​</a></h3><p><img loading="lazy" alt="valgrind-logo-small" src="/assets/images/valgrind-logo-small-4f9917700ea88ef13f83d141049d9240.png" width="256" height="136" class="img_ev3q"></p><p>Valgrind is an instrumentation framework. It provides many tools in it, and you can add your own. One of the tools is <strong>memcheck</strong>, which will <strong>help you to get leaks in your application</strong>.</p><p><img loading="lazy" alt="valgrind-screenshot" src="/assets/images/valgrind-screenshot-eacf4ee6e5ca8664edbdcc59e105fa2f.png" width="768" height="533" class="img_ev3q"></p><p>I use it myself a lot to <strong>detect memory leaks in Linux applications</strong>, and it’s actually very simple to use: just execute valgrind and pass the application you want to test in the command-line parameter. Valgrind will launch the application and at the end of it will give you a <strong>report with detailed information including possible leaks</strong>. There are of course many command-line options you can use to log to a file, choose the size of call stack, detection level, among others.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="instruments-free">Instruments (Free)<a class="hash-link" href="#instruments-free" title="Direct link to heading">​</a></h3><p>Apple Instruments is a powerful and flexible performance-analysis and testing tool that’s part of the Xcode tool set. Among other things, it can be used to <strong>detect leak in iOS and macOS applications</strong>. Adrian Gallero has made a <a href="https://www.tmssoftware.com/site/blog.asp?post=288" target="_blank" rel="noopener noreferrer">nice article at TMS Software blog about how to use Instruments</a> to detect leaks in iOS applications. It’s a little bit old post already, but I believe it’s still valid.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a class="hash-link" href="#conclusion" title="Direct link to heading">​</a></h2><p><strong>There is no winner</strong>. Each tool has their own pros and cons, and it’s interesting to note that the they are <strong>not mutually exclusive</strong>. Actually, I use several of them myself, for different purposes.</p><p>I use FastMM for my <strong>“daily” memory leak detection</strong>, LeakCheck in <strong>unit tests</strong>, Deleaker when I want to <strong>check for other types of leaks</strong> and use <strong>snapshots</strong>, EurekaLog for <strong>bug reporting</strong> in my end-user applications, AQTime for <strong>performance profiling</strong> and Valgrind for <strong>detecting leaks in Linux</strong>. As you can see, all of them are useful!</p><p>The important thing is: <strong>don’t let your application leak memory</strong>! If you are just starting with this subject, you know what to have to do now:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ReportMemoryLeaksOnShutdown := True;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Add the above line to your application and start catching leaks!</p><p>(Do you know of <strong>any other tool</strong> that should be on the list? Do you have a d<strong>ifferent opinion on the liste</strong>d tools? Please <strong>comment below</strong> and share your knowledge to help make this list the definitive one. I will update it frequently as I receive new info.)</p><p><a href="https://forum.landgraf.dev/t/catching-memory-leaks-in-delphi-a-definitive-list-of-tools/244" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An interesting discussion about data replication with TMS Echo]]></title>
            <link>https://www.landgraf.dev/en/data-replication-with-tms-echo-discussion</link>
            <guid>data-replication-with-tms-echo-discussion</guid>
            <pubDate>Sun, 08 Mar 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[An interesting discussion about data replication at TMS Business Mastercall]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="sheep_replication" src="/assets/images/sheep_replication-3d430d81820493f7f76ed482fc6b8c3c.jpg" width="1496" height="840" class="img_ev3q"></p><p>This one is another one of many interesting discussions we had at the <a href="/en/en/biggest-tms-software-delphi-event-ever">TMS Business Masterclass in Düsseldorf</a>. One of then was the <a href="/en/en/tms-business-masterclass-is-now-online">funny discussion about class fields having their name prefixed with uppercase “F” or not</a>.</p><p>Now this one is a little bit more technical, but also interesting nonetheless. The session was about <a href="https://www.tmssoftware.com/site/tmsecho.asp" target="_blank" rel="noopener noreferrer">database data replication using TMS Echo</a>, and the topic was how the changes are send (moved) from one peer (node) to another.</p><p>The question raised was about the “server”, or the “controller” which orchestrates all this “moving” of data changes. I replied that there is no “server” (in the sense that there is no central orchestrator, the system is distributed and can operate independent from other nodes), only to contradict myself few minutes later saying that a “server” is needed (but then I – hopefully – explained the contradiction).</p><p>A funny and interesting discussion, I hope you enjoy it, and, after you watch the video, I raise the question to you: Is there a server, or not? Leave your comment!</p><p>By the way, the complete content of the event is available here: <a href="https://courses.landgraf.dev/p/tms-business-masterclass-bundle" target="_blank" rel="noopener noreferrer">TMS Business Masterclass Online</a>.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/Yku6bbBhj_w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"></iframe><p><a href="https://forum.landgraf.dev/t/an-interesting-discussion-about-data-replication-with-tms-echo/243" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TMS Business Masterclass is now online]]></title>
            <link>https://www.landgraf.dev/en/tms-business-masterclass-is-now-online</link>
            <guid>tms-business-masterclass-is-now-online</guid>
            <pubDate>Thu, 23 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[TMS Business Masterclass is now available as online course! Content includes Delphi ORM (Aurelius), REST Server (XData), Data replication and more!]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="biz_masterclass_instructor2" src="/assets/images/biz_masterclass_instructor2-0107512f19b8252f9e325969a44e4cd6.png" width="1920" height="1080" class="img_ev3q"></p><p>In November 2019, TMS Days 2019 took place in both Düsseldorf, Germany and Wevelgem, Belgium. It was the <a href="/en/en/biggest-tms-software-delphi-event-ever">biggest TMS event ever</a>, with three full days in two different cities, and team members from nine different countries around the world.</p><p>It was a <strong>face-to-face event</strong> and even before the event started many people from all over the world approached us asking if they could <strong>watch the content online</strong> since they could not travel to attend the event in person.</p><p><img loading="lazy" alt="biz_masterclass_dusseldorf2" src="/assets/images/biz_masterclass_dusseldorf2-2e3cf111e2292ad3a1d166fbb5b90bcd.jpg" width="1536" height="746" class="img_ev3q"></p><p><img loading="lazy" alt="biz_masterclass_wevelgem" src="/assets/images/biz_masterclass_wevelgem-4801eddc90430422b79cdb33751dc11b.jpg" width="1536" height="746" class="img_ev3q"></p><p>Well, now also for the first time, parts of a TMS event is now <strong>available to be watched online</strong>. You will have the opportunity to watch both TMS Business Masterclass days in full length – the one which took place in Düsseldorf and the one which took place in Wevelgem.</p><p>We also tried to provide you with high quality material:</p><ul><li>Audio from instructor is clear and the screen recordings are there, of course;</li><li>A second camera showing the instructor is also present to give you a more immersive feeling and get a more personal touch;</li></ul><p><img loading="lazy" alt="biz_masterclass_instructor" src="/assets/images/biz_masterclass_instructor-758b98cce069a65637154be08d535fdc.png" width="1536" height="864" class="img_ev3q"></p><ul><li>Questions from the audience was not very audible, but we took the effort to subtitle most of the questions so you could follow and understand everything that was being discussed!</li></ul><p><img loading="lazy" alt="biz_masterclass_subtitle" src="/assets/images/biz_masterclass_subtitle-4d304e5ed2c5d465908357f6dde97514.png" width="1462" height="671" class="img_ev3q"></p><ul><li>The full recording was fully reviewed and broken into several smaller pieces, so you don’t have a big chunk of 8 hours recording, but very small “lessons” and the subject discussed in each of them. Some are broken to the length of only a few minutes. So you can get really focused and productive on finding content!</li></ul><p><img loading="lazy" alt="biz_masterclass_curriculum" src="/assets/images/biz_masterclass_curriculum-bb5e828e46beba5ef026eb4c4dab5187.png" width="1520" height="734" class="img_ev3q"></p><p>Follow the links below to get more info about the course, pictures of the event, view the full course content structure, <strong>watch some preview videos for free</strong>, and of course, enroll to the course!</p><p><a href="https://courses.landgraf.dev/p/tms-business-masterclass-dusseldorf" target="_blank" rel="noopener noreferrer">TMS Business Masterclass in Düsseldorf ($49)</a></p><p><a href="https://courses.landgraf.dev/p/tms-business-masterclass-wevelgem" target="_blank" rel="noopener noreferrer">TMS Business Masterclass in Wevelgem ($49)</a></p><p><a href="https://courses.landgraf.dev/p/tms-business-masterclass-bundle" target="_blank" rel="noopener noreferrer">TMS Business Masterclass Bundle ($79)</a></p><p>Enrollment is $79 for both courses, or just $49 for one course. Of course, all the attendants of the event in Düsseldorf or Wevelgem <strong>get free access</strong>, not only for the attended day, but for both. Another perk for the attendants!</p><p>To finish this post, watch this personal-favorite excerpt below. When building Delphi classes, do you prefix field names with an uppercase “F”? 😉</p><iframe width="560" height="315" src="https://www.youtube.com/embed/R4ZQIGWzjAc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"></iframe><p><a href="https://forum.landgraf.dev/t/tms-business-masterclass-is-now-online/242" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Biggest TMS Software Event Ever]]></title>
            <link>https://www.landgraf.dev/en/biggest-tms-software-delphi-event-ever</link>
            <guid>biggest-tms-software-delphi-event-ever</guid>
            <pubDate>Wed, 30 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[The biggest TMS Software event ever: TMS TRAINING DAYS 2019. TMS team members from 9 different countries will be speaking about Delphi and TMS development.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="tms-training-days-2019" src="/assets/images/tms-training-days-2019-4e618fbbd279c7de5a264d42f7495501.jpg" width="800" height="572" class="img_ev3q"></p><p>Be prepared! Coming in November 2019, the biggest TMS Software event ever: <strong>TMS TRAINING DAYS 2019</strong>. TMS team members from <strong>nine different countries</strong> will be speaking about Delphi development, TMS libraries and components, in sessions that target from the beginner developer to the advance one.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="dusseldorf-training-days-germany">Dusseldorf Training Days (Germany)<a class="hash-link" href="#dusseldorf-training-days-germany" title="Direct link to heading">​</a></h2><p>On November 14th and 15th, there will be two training days in Dusseldorf, Germany. The event will take place at <a href="https://www.lindner.de/en/duesseldorf-hotel-airport/welcome.html" target="_blank" rel="noopener noreferrer">Lindner Hotel Airport</a>, Unterrather Str. 108, 40468 Düsseldorf, Germany.</p><p><img loading="lazy" alt="lindnerhotel" src="/assets/images/lindnerhotel-58e6b4af8fcd78f03c5cfdbd672711d1.png" width="800" height="400" class="img_ev3q"></p><p>Check below the full agenda for the two training days</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="tms-business-masterclass">TMS Business Masterclass<a class="hash-link" href="#tms-business-masterclass" title="Direct link to heading">​</a></h2><p>November, 14th – Dusseldorf, Germany</p><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" width="100" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Bruno Fierens</p></div><div><div style="margin-left:10px"><strong>9h00: Welcome &amp; introduction (Bruno Fierens)</strong><p>A welcome to all attendees and introduction to the agenda.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="380" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>9h15: Session 1 – The TMS Business World (Wagner R. Landgraf) </strong><p>A journey into the world of TMS Business bundle. We will learn about the solutions provided by TMS Business: the components included, the libraries, the use cases, all in the historical context and the motivations behind it. Products covered: TMS Scripter, TMS Diagram Studio, TMS Workflow Studio, TMS Data Modeler, TMS Aurelius, TMS Sparkle, TMS RemoteDB, TMS XData, TMS Echo, TMS Logging. <br>*BIZ level: Beginner </p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">            10h00: Coffee break</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/empty-horizontal.png" width="120" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px"></p></div><div><div style="margin-left:10px"><strong>10h30: The TMS Aurelius book , experiences &amp; book writing process (John Kouraklis) </strong><p>John Kouraklis book author talks about his most recent book dedicated to TMS Aurelius and the experiences related to writing it.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="380" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>11h00: Session 2 – The Joy of Databases (Wagner R. Landgraf)  </strong><p>A talk about new ways to deal with databases. What is an ORM (Object-Relational Mapping) framework, and are the most recent news and features about it. What you can use to help you out in modeling your databases, easily use existing databases with the new paradigm, and advanced techniques on ORM and database modeling. Products covered: TMS Aurelius, TMS Data Modeler <br>*BIZ level: Intermediate </p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="320" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>12h00: Session 3 – Your App Everywhere: REST API (Wagner R. Landgraf) </strong><p>Multitier, cloud, REST, HTTP, SSL: What, Why, When, and mainly, How? Learn what a REST API server means, why you will benefit from moving your app to the cloud, and learn how to do it in a very RAD way. The latest features will be covered, even for the experienced users. Products covered: TMS XData, TMS Sparkle <br>*BIZ level: Intermediate </p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">             13h00: Warm lunch</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="350" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>14h00: Session 4 – The Unsung Heroes (Wagner R. Landgraf) </strong><p>A deeper look into three awesome TMS Business libraries that bring you solutions for problems you never thought you had. User-customized application, modifying business rules at runtime, offline data synchronization, and moving your app to the cloud in a couple of days. Products covered: TMS Scripter, TMS Echo, TMS RemoteDB <br>*BIZ level: Beginner </p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="200" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>15h00: Session 5 – Getting serious in the Cloud (Wagner R. Landgraf) </strong><p>Advanced techniques and concepts when building REST API servers: Security, background processing and deep analysis of real-world source code. Products covered: TMS XData, TMS Sparkle <br>*BIZ level: Advanced </p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">             16h00: Coffee break</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="280" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Wagner Landgraf</p></div><div><div style="margin-left:10px"><strong>16h30: Session 6: All Together Now (Wagner R. Landgraf) </strong><p>A very interactive session where all the current ideas from the TMS team for the future of TMS Business will be presented. We will discuss the existing roadmap, brainstorming about all the possibilities, have all questions answered and summarize the whole day.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">             17h30: Wrap-up, Q&amp;A &amp; open discussion </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><h2 class="anchor anchorWithStickyNavbar_LWe7" id="tms-dev-intensive">TMS DEV Intensive<a class="hash-link" href="#tms-dev-intensive" title="Direct link to heading">​</a></h2><p>November, 15th – Dusseldorf, Germany</p><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" width="100" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Bruno Fierens</p></div><div><div style="margin-left:10px"><strong>9h00: Welcome &amp; introduction (Bruno Fierens)</strong><p>A welcome to all attendees and introduction to the agenda.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" width="100" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Bruno Fierens</p></div><div><div style="margin-left:10px"><strong>9h15: TMS VCL UI Pack modernization (Bruno Fierens)</strong><p>What is new &amp; upcoming for ensuring VCL UI controls look as good as possible: high-DPI, VCL styles, Office styles, images</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/marion-candau.jpg" width="280" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Marion Candau</p></div><div><div style="margin-left:10px"><strong>10h15: Using TMS Cryptography Pack for secure signing of PDF files (Marion Candau)</strong><p>Marion will show how to sign a PDF document with a cryptographic USB token using TMS Cryptography Pack. She will briefly explain the cryptographic mechanisms of an electronic signature, then she will present how a cryptographic USB token works and finally, how to use a TMS Cryptography Pack component to sign a PDF document with the token.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">           10h45: Coffee break</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/adrian-gallero.jpg" width="200" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Adrian Gallero</p></div><div><div style="margin-left:10px"><strong>11h15: TMS Flexcel 7.0 introduction: taking advantage of the new Flexcel 7.0 for VCL/FMX features (Adrian Gallero</strong><p>Adrian will show how to create a Delphi app that exports data to Excel and Pdf files, using some of the newest stuff introduced in FlexCel 7. If time allows, he will also have a glimpse at the future and look at the stuff currently in development.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">           12h15: Warm lunch</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/jose-leon.jpg" width="550" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">José León Serna</p></div><div><div style="margin-left:10px"><strong>13h15: Taking the wraps of our upcoming groundbreaking product for Delphi developers (José León Serna / Bruno Fierens)</strong><p>Be there, be amazed, receive the first product beta and start playing with it! It has been more than one year that Bruno Fierens, CEO of tmssoftware.com got in touch with José Leon Serna and discussed about future opportunities for Delphi development. Surprisingly our visions for future directions and possibilities perfectly aligned. From there, an intense collaboration started and in this session they will present the first fruits of this collaboration. TMS clearly could not have achieved the amazing technical break-throughs without José Leon. The TMS team is honoured, happy and proud to have one of the brightest minds in the Delphi world in our family.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/roman-yankovsky.png" width="100" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Roman Yankovsky</p></div><div><div style="margin-left:10px"><strong>14h15: Using TMS FixInsight to bring your code to a higher &amp; more secure level (Roman Yankovsky)</strong><p>Roman will show how to use FixInsight’s static code analysis in Delphi to find bugs in your code before your customers do.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">           15h15: Coffee break</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/roman-kassebaum.jpg" width="150" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Roman Kassebaum</p></div><div><div style="margin-left:10px"><strong>15h45: TMS WEB Core v1.3 for web, cross platform Electron and mobile PWA apps new features &amp; capabilities (Bruno Fierens / Roman Kassebaum)</strong><p>Together with Bruno Fierens, Roman Kassebaum, architect of the TMS WEB Core IDE integration, will present and demonstrate TMS WEB Core v1.3 new features &amp; capabilities.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex"><div style="display:flex;flex-direction:column"><img loading="lazy" src="/img/landgraf/holger-flick.png" width="550" class="img_ev3q"><p style="font-size:14px;text-align:center;margin-top:5px">Holger Flick</p></div><div><div style="margin-left:10px"><strong>16h45: Putting it all together : Using TMS XData back-end and TMS WEB Core + TMS FNC UI as front-end (Holger Flick)</strong><p>Many frameworks, many platforms: TMS has all the tools. You’ve read it many times, but how do all these technologies fit together? This session will give an example of how you can build a database application for multiple desktop and mobile platforms as well as for the web using the same database backend. It will also provide an overview of the technologies that are available from the TMS toolbox and will hand you key pointers how to pick the right one for the task you need to achieve. In short, you will get a hands-on example that covers TMS XData, TMS RemoteDB, TMS Web Core, VCL, and FNC.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">           17h45: Q&amp;A &amp; open discussions </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><p>Meet the experts</p><p>During the whole TMS Dev Intensive Day, the opportunity will be given, as alternative to the regular sessions, to meet each of the many TMS experts available during the full day in person or in very small group to discuss and learn from. </p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="wevelgem-training-day-belgium">Wevelgem Training Day (Belgium)<a class="hash-link" href="#wevelgem-training-day-belgium" title="Direct link to heading">​</a></h2><p>On November 18th, there will be a second TMS Business Masterclass day in Wevelgem, Belgium. The event will take place at the TMS Software Main Office. It’s an opportunity to meet other members of the team as well!</p><p><img loading="lazy" alt="tms-office" src="/assets/images/tms-office-b8437c743d5d2a69e650f6502781836a.png" width="522" height="358" class="img_ev3q"></p><p>The sessions will very similar to the TMS Business Masterclass in Dusseldorf on November 14th, thus this is a simplified agenda. All sessions will be presented by Wagner Landgraf and Bruno Fierens.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="tms-business-masterclass-1">TMS Business Masterclass<a class="hash-link" href="#tms-business-masterclass-1" title="Direct link to heading">​</a></h2><p>November, 18th – Wevelgem, Belgium</p><p>Speakers:</p><div style="display:flex"><div><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" width="100" class="img_ev3q"><p style="font-size:12px;text-align:center">Bruno Fierens</p></div><div style="margin-left:15px"><img loading="lazy" src="/img/landgraf/wagner-landgraf-2.png" width="100" class="img_ev3q"><p style="font-size:12px;text-align:center">Wagner Landgraf</p></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>9h00: Welcome &amp; introduction (Bruno Fierens)</strong></p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>9h15: Session 1 – The TMS Business World (Wagner R. Landgraf)</strong></p><hr style="border-top:1px solid;border-bottom:1px solid"><p>10h00: Coffee break</p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>10h30: Session 2 – The Joy of Databases (Wagner R. Landgraf)</strong> </p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>11h30: Session 3 – Your App Everywhere: REST API (Wagner R. Landgraf)</strong> </p><hr style="border-top:1px solid;border-bottom:1px solid"><p>12h30: Warm lunch</p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>13h30: Session 4 – The Unsung Heroes (Wagner R. Landgraf)</strong> </p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>14h30: Session 5 – Getting serious in the Cloud (Wagner R. Landgraf)</strong></p><hr style="border-top:1px solid;border-bottom:1px solid"><p>15h30: Coffee break</p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>16h00: Session 6: All Together Now (Wagner R. Landgraf)</strong></p><hr style="border-top:1px solid;border-bottom:1px solid"><p><strong>17h00: Wrap-up, Q&amp;A &amp; open discussion</strong> </p><hr style="border-top:1px solid;border-bottom:1px solid"><h2 class="anchor anchorWithStickyNavbar_LWe7" id="speakers">SPEAKERS<a class="hash-link" href="#speakers" title="Direct link to heading">​</a></h2><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/adrian-gallero.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Adrian Gallero</strong> (Uruguay)<br>Adrian Gallero is an Electrical Engineer who has been working in Delphi since it was Turbo pascal 4. He has worked in calculating power line distribution for electrical companies in Montevideo and Rio de Janeiro. He lived for over 3 years in Madrid working the development of a telecommunication management system, and over a year in Sydney developing custom solutions for a freight-forwarding company.</p><img loading="lazy" src="/img/landgraf/flag-of-Uruguay.png" style="height:20px;width:40px" class="img_ev3q"></div><div><div style="margin-left:10px"><p style="font-size:18px">He has been working with spreadsheets for longer than he can remember, and in 1996 he developed a small component that allowed his Delphi 1 apps to export the results to a spreadsheet (to his surprise, he learned a year ago that the electrical company is still using those unmodified apps today). This component grew over the years: It was renamed FlexCel in 2001 and it joined the TMS family in 2002. Today <a href="https://www.tmssoftware.com/site/flexcel.asp" target="_blank" rel="noopener noreferrer">FlexCel</a> exists in both Delphi and <a href="https://www.tmssoftware.com/site/flexcel.asp" target="_blank" rel="noopener noreferrer"> .NET</a> versions, and they contain over a million lines of code.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Bruno Fierens</strong> (Belgium)<br></p><ul><li>Studied civil electronic engineering at university of Ghent, Belgium (1987-1992).</li><li>Started a career as R&amp;D digital hardware engineer at Barco Graphics Belgium designing with FPGA, VHDL, graphic      processors, PCI, Silicon Graphics add-on boards, high-end printer controllers,…</li><li>Began writing software in Turbo Pascal 3.0 since 1987 and used all Borland Pascal and all Delphi versions since then.</li></ul><p></p><img loading="lazy" src="/img/landgraf/flag-of-Belgium.png" style="height:20px;width:40px" class="img_ev3q"></div><div><div style="margin-left:10px"><ul style="font-size:18px"><li>Founded TMS software in 1996, developing VCL components starting with Delphi 1.</li><li>TMS software became Borland Technology Partner in 1998 and developed Delphi Informant award-winning grid &amp; scheduling components.</li><li>From 2011 FireMonkey cross platform components,targetting Windows, macOS, Android, iOS.</li><li>In 2016, TMS software launched FNC, a framework neutral component architecture enabling to use UI controls in VCL,FMX &amp; LCL apps</li><li>In 2018, TMS software launched TMS WEB Core, a framework for creating rich web clients using ObjectPascal</li><li>Currently doing and managing VCL, FMX, Web, .NET, IoT, LCL, REST, node.js development.</li><li>Is a regular speaker at conferences (Be-Delphi, DelphiTage, ITDevCon, CodeWay Tour, EKON, DevTracks, SDN, ..).</li><li>Available for consulting &amp; custom project development.</li><li>Bruno Fierens was titled Embarcadero MVP since 2012.</li><li>Special area of interest are user interfaces design, UX, RAD software development, hardware/electronics.</li></ul></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/holger-flick.png" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Holger Flick</strong> (United States)<br>Since 1996, Dr. Holger Flick has been using Delphi as his primary development platform and has been an active member of the community. He studied computer science at Dortmund University and later wrote his dissertation at the Ruhr University in Bochum in the field of telemedicine. For his passion for and knowledge of Delphi he was awarded the “Delphi MVP” moniker in 2016.</p><img loading="lazy" src="/img/landgraf/flag-of-United-States-of-America.png" style="height:20px;width:40px" class="img_ev3q"></div><div><div style="margin-left:10px;margin-top:15px"><p style="font-size:18px">In 2019, Holger moved to the United States of America (USA) and established his new company FlixEngineering LLC. The company focuses on consulting, training and software development using object-oriented programming languages like Delphi, C#, Swift, and Objective-C. Holger is part of the TMS Software family providing his expertise as Evangelist and QA Engineer.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/empty.png" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>John Kouraklis</strong> (United Kingdom)<br>John Kouraklis started exploring computers when he was 16 and since then has followed all the way from Turbo Pascal to the latest Delphi versions as a hobby initially and as a profession for most of his adult life. He has developed a wide range of applications, from financial software to reverse engineering tools. The last years he has been teaching business and programming in undergraduate and postgraduate courses. He is the author of MVVM in Delphi (Apress, 2016) and</p><img loading="lazy" src="/img/landgraf/flag-of-United-Kingdom.png" style="height:20px;width:40px" class="img_ev3q"></div><div><div style="margin-left:10px"><p style="font-size:18px">Introducing Delphi ORM – ORM with TMS Aurelius (July 2019)</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/jose-leon.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>José León Serna</strong> (Spain)<br>JCreator of Delphi for PHP/HTML5 Builder and former Director of Engineering and Branch Manager at Embarcadero Technologies between 2010 and 2016. Although the Embarcadero’s branch participated in the development of several products, he was mainly responsible for RAD Studio, and more specifically, the IDE. He participated actively on the conception and implementation of multitude of new features, specially focused on multi-device development. He has been</p><img loading="lazy" src="/img/landgraf/flag-of-Spain.png" style="height:20px;width:40px" class="img_ev3q"></div><div><div style="margin-left:10px"><p style="font-size:18px">involved in IDE development for most part of his career and is the area where he enjoys most.</p></div></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/marion-candau.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Marion Candau</strong> (France)<br></p><ul><li>PhD in cryptography</li><li>Developer at Cyberens, a French cybersecurity company located in Bordeaux.</li><li><a href="https://www.tmssoftware.com/site/tmscrypto.asp" target="_blank" rel="noopener noreferrer"> TMS Cryptography Pack</a> architect</li><li>MVP Embarcadero</li><li>She mainly develops cybersecurity-related applications and advises clients to use cryptographic tools and libraries in their own applications.</li></ul><p></p><img loading="lazy" src="/img/landgraf/flag-of-France.png" style="height:20px;width:40px" class="img_ev3q"></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/roman-kassebaum.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Roman Kassebaum</strong> (Germany)<br>Roman Kassebaum is a freelancer. He started to work with Delphi in 1996 after he graduated with a Master degree from the University of Paderborn. He is a Delphi MVP and an Embarcadero Technology Partner. During the last years he became a member of the great TMS team and he is also a <a href="https://www.tmssoftware.com/site/partners.asp" target="_blank" rel="noopener noreferrer">TMS certified consulting partner</a>. Roman is a specialist in all kinds of Delphi projects including TMS Business components and the cutting edge <a href="https://www.tmssoftware.com/site/tmswebcoreintro.asp" target="_blank" rel="noopener noreferrer">TMS WEB Core</a> library for which he created the Delphi IDE integration.</p><img loading="lazy" src="/img/landgraf/flag-of-Germany.png" style="height:20px;width:40px" class="img_ev3q"></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/roman-yankovsky.png" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Roman Yankovsky</strong> (Russia)<br>Roman Yankovsky is an Embarcadero MVP who has been working with Delphi since Delphi 2. He joined the TMS team in 2016. He is the product manager and architect of the <a href="https://www.tmssoftware.com/site/fixinsight.asp" target="_blank" rel="noopener noreferrer" tareg="_blank">FixInsight static analysis tool</a> for Delphi. <br>Roman Yankovsky is also author of the Delphi AST, an open source Abstract syntax tree builder for Delphi you can find here: <a href="https://github.com/RomanYankovsky/DelphiAST" target="_blank" rel="noopener noreferrer" tareg="_blank">https://github.com/RomanYankovsky/DelphiAST</a> and this library is used in the great <a href="https://code.visualstudio.com/" target="_blank" rel="noopener noreferrer" tareg="_blank">OmniPascal Visual Studio Code</a> extension.</p><img loading="lazy" src="/img/landgraf/flag-of-Russia.png" style="height:20px;width:40px" class="img_ev3q"></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/wagner-landgraf-back.jpg" style="height:200px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px"><strong>Wagner Landgraf</strong> (Brazil)<br></p><ul><li>Graduated in Electronic Engineering and M.Sc in Industrial IT at Federal Technological University of Parana-Brazil.</li><li>More than 24 years experience (since 1995) in Delphi development – since Delphi 1.</li><li>Architect and main developer of Delphi libraries like <a href="https://www.tmssoftware.com/site/aurelius.asp" target="_blank" rel="noopener noreferrer"> TMS Aurelius</a>, <a href="https://www.tmssoftware.com/site/xdata.asp" target="_blank" rel="noopener noreferrer"> TMS XData</a>, <a href="https://www.tmssoftware.com/site/remotedb.asp" target="_blank" rel="noopener noreferrer">TMS RemoteDB</a>, <a href="https://www.tmssoftware.com/site/scriptstudiopro.asp" target="_blank" rel="noopener noreferrer">TMS Scripter</a>, among others.</li><li>Product Manager at TMS Software for more than 20 years. Founder of landgraf.dev online school.</li></ul><p></p><img loading="lazy" src="/img/landgraf/flag-of-Brazil.png" style="height:20px;width:40px" class="img_ev3q"></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><h2 class="anchor anchorWithStickyNavbar_LWe7" id="special-bonuses">Special Bonuses<a class="hash-link" href="#special-bonuses" title="Direct link to heading">​</a></h2><p>There are also special bonuses for attendees of TMS Training Days!</p><p>Book: Introducing Delphi ORM</p><div style="display:flex;flex-direction:column"><div style="display:flex"><img loading="lazy" src="/img/landgraf/introducing-delphi-orm.jpg" style="height:300px" width="200" class="img_ev3q"><p style="font-size:18px;margin-left:15px">All attendees of TMS Business Masterclass day in Dusseldorf (Nov 14th) will receive a free copy of the book <strong>“Introducing Delphi ORM: Object-Relational Mapping using TMS Aurelius“</strong>, written by John Kouraklis. Wagner Landgraf, author of TMS Aurelius was a technical reviewer of the book.</p></div></div><hr style="border-top:1px solid;border-bottom:1px solid"><p>“Introduction to TMS Web Core” Training Course: 50% off</p><p>All attendees of TMS Training Days will get a <strong>50% off discount</strong> coupon for the online training course <a href="https://courses.landgraf.dev/p/web-applications-with-delphi-tms-web-core" target="_blank" rel="noopener noreferrer">“Introduction to TMS Web Core”</a>, from l<a href="https://courses.landgraf.dev/" target="_blank" rel="noopener noreferrer">andgraf.dev</a>. The instructor is Wagner Landgraf and you will learn concepts <strong>from the beginning</strong>: what is a web application, how does it work, what are single page application (SPA) and multiple page application (MPA), what is HTML, JavaScript, CSS, Bootstrap, AJAX. Full source code of the examples are provided.</p><hr style="border-top:1px solid;border-bottom:1px solid"><p>Special discounts for TMS Products</p><p>TMS Software will also offer special discounts for new license on several TMS products, for all attendees. Stay tuned for the offers!</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="registration-and-more-info">Registration and More Info<a class="hash-link" href="#registration-and-more-info" title="Direct link to heading">​</a></h2><p>You can find more info from TMS Software web site directly: <a href="https://www.tmssoftware.com/site/tmsdaynov2019.asp" target="_blank" rel="noopener noreferrer">TMS Training Days in Dusseldorf</a> and <a href="https://www.tmssoftware.com/site/tmsdaynov182019.asp" target="_blank" rel="noopener noreferrer">TMS Business Masterclass in Wevelgen</a>.</p><p>Registration fees:</p><p>Nov 14th, TMS Business MasterClass: 295 EUR
Nov 15th, TMS Dev Intensive: 295 EUR
Nov 18th, TMS Business Masterclass: 295 EUR</p><p>Special offer for the two TMS Training Days in Dusseldorf (Nov 14th and 15h): 495 EUR </p><p><a href="https://www.tmssoftware.com/site/regform.asp" target="_blank" rel="noopener noreferrer">Register Now for Dusseldorf Days!</a></p><p><a href="https://www.tmssoftware.com/site/error404.asp" target="_blank" rel="noopener noreferrer">Register Now for Wevelgem Day!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Embarcadero Conference 2019 in Brazil: Meet the NFC-e Issuer in the Cloud]]></title>
            <link>https://www.landgraf.dev/en/embarcadero-conference-2019-in-brazil-meet-the-nfc-e-issuer-in-the-cloud</link>
            <guid>embarcadero-conference-2019-in-brazil-meet-the-nfc-e-issuer-in-the-cloud</guid>
            <pubDate>Mon, 14 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[On October 22nd, 2019, there will be another Embarcadero Conference. Don't miss the session that shows how to issue brazilian NFC-e document from the cloud.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="embarcadero-conference-call-2019" src="/assets/images/embarcadero-conference-call-2019-efb0718beef855ecba3fce7590686e2a.jpeg" width="800" height="450" class="img_ev3q"></p><p>On October 22nd, 2019, there will be another edition of <a href="https://embarcaderoconference.com.br/" target="_blank" rel="noopener noreferrer">Embarcadero Conference</a>. It’s the <strong>biggest Delphi event of the world</strong>, and happens in São Paulo, Brazil. There are hundreds of attendants.</p><p>In this edition, just like last year, there will be <strong>seven simultaneous sessions</strong>, all happening in the same auditorium. You choose which session to listen by selecting the audio channel in your headphones. The full session list is available <a href="https://embarcaderoconference.com.br/" target="_blank" rel="noopener noreferrer">in the site of the event</a>.</p><p>With som many sessions happening at the same time, you get even anxious to choose which want to watch. But if there is one that you <strong>cannot miss</strong>, it’s this one we are going to suggest.</p><p>Our speaker, <strong>Wagner Landgraf</strong>, will be presenting the session “<strong><em>Anatomy NFC-e issuer in the cloud (REST API)</em></strong>. Oh yes, we are recommending our own session. But that’s just a coincidence, it will be very interesting, believe me!</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-in-the-cloud">Why “in the cloud”?<a class="hash-link" href="#why-in-the-cloud" title="Direct link to heading">​</a></h2><p>Ok, let’s anticipate some content here. Why of the reasons is the “why”. NFC-e is a legal digital document in Brazil, and when we issue such a document in the cloud, we have several advantages compared to the issuing of the same document in a local desktop or mobile application:</p><ul><li><p>One single and centralized place to configure the issuing environment (installation of certificates, configuration of cryptography libraries, etc.)</p></li><li><p>Minimizes the risk of problems and support cost: you don’t have to configure hundreds of client environments</p></li><li><p>Much easier system update and maintenance: you also don’t need to manage all the clients.</p></li><li><p>You can issue NFC-e from any platform (from a desktop Windows application, a mobile application, or even from Rasperry PI, who knows?). All you need is internet connection and HTTP communication on that platform.</p></li><li><p>t’s easier to develop clients in other platforms. No need to be able to port and compile several 3rd party libraries needed for the document issuing, like ACBr, FastReport, FortesReport, cryptography dlls or tools, etc.</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="can-you-disclose-a-little-bit-more">Can you disclose a little bit more?<a class="hash-link" href="#can-you-disclose-a-little-bit-more" title="Direct link to heading">​</a></h2><p>To give you a tease of what will be presented, here are some screenshots of the API that we will show and explain at the event:</p><p><img loading="lazy" alt="nuvem-fiscal-nfce-api-swagger" src="/assets/images/nuvem-fiscal-nfce-api-swagger-61b0c32171df153d138d40f5d08affdd.png" width="1024" height="756" class="img_ev3q"></p><p><img loading="lazy" alt="nuvem-fiscal-nfce-api-swagger-emissao" src="/assets/images/nuvem-fiscal-nfce-api-swagger-emissao-8d35ab133517024ef1ec0c1cae1ee600.png" width="1024" height="756" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="not-only-that">Not only that…<a class="hash-link" href="#not-only-that" title="Direct link to heading">​</a></h2><p>This will be a really interesting session. There will be an additional surprise that for sure will be very interesting for all those who work with Delphi and deal with such legal Brazilian documents. That’s just the beginning… See you at Embarcadero Conference 2019!</p><p><a href="https://forum.landgraf.dev/t/embarcadero-conference-2019-in-brazil-meet-the-nfc-e-issuer-in-the-cloud/240" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Databases inside Delphi Ecosystem: Webinar]]></title>
            <link>https://www.landgraf.dev/en/databases-inside-delphi-ecosystem</link>
            <guid>databases-inside-delphi-ecosystem</guid>
            <pubDate>Mon, 07 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[On October 10, 2019, Softacom is holding a webinar on "Databases inside RAD Studio & Delphi ecosystem". Wagner Landgraf from landgraf.dev will be a speaker.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="softacom-webinar" src="/assets/images/softacom-webinar-89f81b2f3d713f6e09e2e7cdc64ffc23.jpeg" width="1536" height="864" class="img_ev3q"></p><p>On October 10, <a href="https://www.softacom.com/" target="_blank" rel="noopener noreferrer">Softacom</a> is holding a webinar on “<em>Databases inside RAD Studio &amp; Delphi ecosystem. Migration process (legacy to up-to-date, to another RDBMS), data layer architecture (ORM), data access architecture (REST API)</em>”.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="speakers">Speakers<a class="hash-link" href="#speakers" title="Direct link to heading">​</a></h2><p>Wagner Landgraf from <em>landgraf.dev</em> will be one of the speakers, talking about how database access evolved in Delphi since the early versions, Object Relational Mapping (TMS Aurelius), REST API Servers (TMS XData) and remote database access (TMS RemoteDB).</p><div style="display:flex;margin-bottom:20px"><div><img loading="lazy" src="/img/landgraf/serge-pilko.jpg" style="border-radius:50%" width="150" class="img_ev3q"></div><div style="display:flex;flex-direction:column;margin-left:15px"><strong style="font-size:30px;margin-bottom:40px">Serge Pilko</strong><p>Embarcadero MVP &amp; CEO of Softacom – Enterprise digital transformation &amp; software modernization services expert</p></div></div><div style="display:flex;margin-bottom:40px"><div><img loading="lazy" src="/img/landgraf/wagner-landgraf.png" style="border-radius:50%" width="150" class="img_ev3q"></div><div style="display:flex;flex-direction:column;margin-left:15px"><strong style="font-size:30px;margin-bottom:40px">Wagner Landgraf</strong><p>CEO of landgraf.dev / TMS Software Partner / TMS Business Product Manager</p></div></div><div style="display:flex"><div><img loading="lazy" src="/img/landgraf/bruno-fierens-2.jpg" style="border-radius:50%" width="150" class="img_ev3q"></div><div style="display:flex;flex-direction:column;margin-left:15px"><strong style="font-size:30px;margin-bottom:40px">Bruno Fierens</strong><p>Embarcadero MVP &amp; CEO of TMS Software – Main player as Delphi and C ++ Builder 3rd party vendor</p></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-will-you-learn">What will you learn<a class="hash-link" href="#what-will-you-learn" title="Direct link to heading">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="for-ceo--owner--cto--it--director--product-manager">For CEO / Owner / CTO / IT – Director / Product Manager:<a class="hash-link" href="#for-ceo--owner--cto--it--director--product-manager" title="Direct link to heading">​</a></h3><ul><li><p>Up-to-date Delphi solutions and frameworks for communications with databases;</p></li><li><p>Pitfalls of migration to up-to date or another type of RDBMS;</p></li><li><p>Cutting-edge Delphi-related solutions and practices for RDBMS;</p></li><li><p>Arguments, why you have to use Delphi and RAD Studio for your multi-tier applications;</p></li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="for-developers-and-technical-specialists">For Developers and Technical Specialists:<a class="hash-link" href="#for-developers-and-technical-specialists" title="Direct link to heading">​</a></h3><ul><li><p>Best practices for developing data access layers for RAD Studio projects;</p></li><li><p>How to use ORM for Delphi projects;</p></li><li><p>How to develop REST API server as DB facade;</p></li><li><p>Insights, tips and tricks for the app developers from the first mouth of DB tools and DB framework developers;</p></li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="webinar-highlights">Webinar Highlights:<a class="hash-link" href="#webinar-highlights" title="Direct link to heading">​</a></h3><ul><li><p>Most popular data access patterns 10-15 years ago and today;</p></li><li><p>Pitfalls of migration legacy versions of RDBMS to up-to-date versions;</p></li><li><p>Pros and cons of migration to ORM instead of using regular practices;</p></li><li><p>ORM for Delphi ─ TMS Aurelius from TMS Software. Pros and cons of the solution;</p></li><li><p>Pros and cons of migration to REST API instead of using “classic” DB access;</p></li><li><p>REST API server for Delphi ─ RAD server. Pros and cons.</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="for-webinar-attendees-only">For webinar attendees only!<a class="hash-link" href="#for-webinar-attendees-only" title="Direct link to heading">​</a></h2><p>Don’t miss a chance to get your discount promo code for EKON conference and 10% discount for new TMS Software licenses.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="registration">Registration<a class="hash-link" href="#registration" title="Direct link to heading">​</a></h2><p>Registration link: <a href="https://www.softacom.com/en_softacom_october_webinar" target="_blank" rel="noopener noreferrer">https://www.softacom.com/en_softacom_october_webinar</a></p><p><a href="https://forum.landgraf.dev/t/databases-inside-delphi-ecosystem-webinar/239" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[5 Reasons to Use Inline Variables in Delphi]]></title>
            <link>https://www.landgraf.dev/en/5-reasons-to-use-inline-variables-in-delphi</link>
            <guid>5-reasons-to-use-inline-variables-in-delphi</guid>
            <pubDate>Mon, 05 Aug 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Inline variables declaration is a feature introduced in Delphi Rio 10.3. There are many good reasons to use it in your code, and here are listed five of them.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="blur-boxes-containers" src="/assets/images/blur-boxes-containers-9d34b9d4f4cd0952450a956026dd62e4.jpg" width="800" height="534" class="img_ev3q"></p><p>Inline variables declaration is a feature introduced in Delphi Rio 10.3. What is it?</p><p>In short, it is the possibility to <strong>declare a variable</strong> in <strong>any line</strong> of your code. That is, you can declare a variable this way, within the begin..end block:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var I: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  I := 22;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ShowMessage (I.ToString);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>A lot of people already understood <strong>how</strong> this feature works, but did not understand <strong>why</strong> it is interesting. In this article, I will show you this new feature with a focus on the advantages it brings.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="1-organizes-your-code">1. Organizes your code<a class="hash-link" href="#1-organizes-your-code" title="Direct link to heading">​</a></h2><p>The variable is only accessible from the point it is declared. For many people this better organizes the code in a large method, because it is possible to know better <strong>where</strong> that variable is being used. Consider the following code:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  A, B, C, D, E: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Found, Done, Excluded: Boolean;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Text: string;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   // many</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   // lines</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   // of</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   // code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Update (May 18, 2020): <a href="https://www.ideasawakened.com/" target="_blank" rel="noopener noreferrer">Darian Miller</a> provides even more benefits of using inline variables in his nice blog post “<a href="https://www.ideasawakened.com/post/newly-discovered-hidden-benefits-of-inline-variables-in-delphi" target="_blank" rel="noopener noreferrer">Newly discovered hidden benefits of inline variables in Delphi</a>“.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="2-minimizes-bugs">2. Minimizes bugs<a class="hash-link" href="#2-minimizes-bugs" title="Direct link to heading">​</a></h2><p>Have you ever done something like this:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var I: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  for I := 0 to Count - 1 do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Process;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  DoSomethingWithI(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>That is, using the for variable <strong>after the loop</strong> is finished. This is not safe, and although the compiler raises a warning for this, many people ignore it. By declaring the for variable inline, it will be only valid inside the for, and using it outside the block will result in a <strong>compilation error</strong>:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  for var I: Integer := 0 to Count - 1 do</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Process;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  DoSomethingWithI(I); // Compile error!!!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The benefit from the code above comes from the fact that the scope of the variable is <strong>limited to the block in which they are declared</strong>. That minimizes the chance of errors. For example, suppose you have a code like this:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var I: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  I := CalculateSomething;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Persist(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // many lines below...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Log(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then you eventually need to refactor the code in a way that the first part only executes under a specified condition. You <strong>think</strong> that variable I is only being used there, and do something like this:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var I: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if Condition then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    I := CalculateSomething;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Persist(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // many lines below...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Log(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>There, you forgot the last line and maybe the value of I is <strong>not what you expect</strong>. Changing the scope of your variable to the block will generate compilation errors if the variable is used outside the block, which will show you the problem <strong>immediately</strong>, so you can make a decision:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if Condition then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var I: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    I := CalculateSomething;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Persist(I);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // many lines below...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Log(I); // Compile error!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="3-less-typing">3. Less typing<a class="hash-link" href="#3-less-typing" title="Direct link to heading">​</a></h2><p>Who does not want more <strong>productivity</strong>? If you can type a bit less to declare a variable, why not? You can now declare and initialize a variable at the same time:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure Test;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var I: Integer := 22; </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ShowMessage (I.ToString);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>But not only that. There is also <strong>type inference</strong>, which means that in most cases you <strong>do not need to include the variable type</strong> when declaring it. Just initialize the variable with a value and Delphi will know the variable type.</p><p>Looks like it’s not a big deal? Imagine a case where the variable type is using heavy generics:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure NewTest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  MyDictionary: TObjectDictionary&lt;string, TObjectList&lt;TMyAmazingClass&gt;&gt;;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Pair: TPair&lt;string, TObjectList&lt;TMyAmazingClass&gt;&gt;;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  List: TObjectList&lt;TMyAmazingClass&gt;;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  MyDictionary := TObjectDictionary&lt;string, TObjectList&lt;TMyAmazingClass&gt;&gt;.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  MyDictionary.Add('one', CreateList);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Pair := MyDictionary.ExtractPair('one');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  List := Pair.Value;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ShowMessage(List.Count.ToString);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Using inline variable and type inference, you can rewrite the code this way:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure NewTest;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var MyDictionary := TObjectDictionary&lt;string, TObjectList&lt;TMyAmazingClass&gt;&gt;.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  MyDictionary.Add('one', CreateList);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var Pair := MyDictionary.ExtractPair('one');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var List := Pair.Value;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ShowMessage(List.Count.ToString);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Better, isn’t it?</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="4-increases-performance">4. Increases performance<a class="hash-link" href="#4-increases-performance" title="Direct link to heading">​</a></h2><p>The fact that the variables belong to a more limited scope (within a begin..end block) can even <strong>increase code performance</strong>!</p><p>You can see more details in this excelent article: <a href="https://blog.grijjy.com/2018/11/02/inline-variables-can-increase-performance/" target="_blank" rel="noopener noreferrer">Inline Variables can increase performance</a>. In summary: the variable will <strong>initialized only</strong> if the code execution <strong>enters the block</strong>, and <strong>finalized only upon block exit</strong>. In this code, for example:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure TestInlineVars(const ACondition: Boolean);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // BEFORE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if (ACondition) then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var S := 'Inline String';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var I: IInterface := TInterfacedObject.Create;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var F: TFoo;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    F.S := 'Managed Record';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // AFTER</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Variables S, I and F are managed types (string, interface and record). The compiler automatically adds initialization and finalization code for them.</p><p>If you call TestInlineVars procedure a million times, it will have a big impact. However with the code above, the variables <strong>will only be initialized</strong> if ACondition is true and the block is actually executed. Less unnecessary code being executed.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="5-makes-it-easier-to-use-conditional-directives">5. Makes it easier to use conditional directives<a class="hash-link" href="#5-makes-it-easier-to-use-conditional-directives" title="Direct link to heading">​</a></h2><p>This feature can help even in small things. This article brought my attention: <a href="http://delphi.org/2018/10/unexpected-benefit-of-inline-variables-compiler-directives/" target="_blank" rel="noopener noreferrer">Unexpected Benefit of Inline Variables: Conditional Blocks</a>.</p><p>If you use compiler directives where you declare and use different variables for each situation, you have also wrap variable declarations around a compiler directive as well:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure DoesSomething;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE1}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var1: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE2}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var2: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE1}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // use var1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE2}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // use var2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Boring, huh? In my opinion this is easier:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">procedure DoesSomething;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE1}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var1: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // use var1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$IFDEF CASE2}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  var2: Integer;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // use var2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {$ENDIF}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>I believe that inline variables still brings other subtle benefits in specific situations that are not listed here. If you can think of any other benefit, <strong>leave your comment</strong>. If you do not agree and you think inline variables are not good news for Delphi, leave your comment as well. Just do not forget one thing: <strong>if you didn’t like it, simply don’t use it</strong>!</p><p><a href="https://forum.landgraf.dev/t/5-reasons-to-use-inline-variables-in-delphi/238" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I liked about Delphi/Rad Studio 10.3.2 update]]></title>
            <link>https://www.landgraf.dev/en/what-i-liked-about-delphi-rad-studio-10-3-2-update</link>
            <guid>what-i-liked-about-delphi-rad-studio-10-3-2-update</guid>
            <pubDate>Sun, 21 Jul 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[The new Delphi/Rad Studio Rio 10.3.2 update has just been released. Here I list the new features that got my attention.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="actor-adult-businessman" src="data:image/jpeg;base64,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" width="600" height="376" class="img_ev3q"></p><p>Embarcadero has just released a new Delphi Rio version (which means also Rad Studio and C++ Builder): version 10.3.2. It’s a minor update – meaning product codename is still “Rio”, and it’s binary (DCU) compatible with previous 10.3.1 version.</p><p>Why then, an article about a minor update? Usually minor updates contain just bug fixes and small improvements, however it’s not the case of this one. I will mention here two improvements that I personally found significant. It’s also worth mentioning that Embarcadero has recently published the updated <a href="https://blogs.embarcadero.com/rad-studio-roadmap-may-2019/" target="_blank" rel="noopener noreferrer">Rad Studio roadmap – May 2019</a> which lists the probable new improvements in upcoming major versions. Now to the improvements in this release:</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="macos-64-bit-support">macOS 64-bit support<a class="hash-link" href="#macos-64-bit-support" title="Direct link to heading">​</a></h2><p>Delphi 10.3.2 finally supports macOS 64-bit platform. If you develop macOS applications with Delphi, you know how much this is important.</p><p>Apple has been planning moving all macOS applications to 64-bit for years and informing users and developers that <a href="https://support.apple.com/pt-br/HT208436" target="_blank" rel="noopener noreferrer">32-bit macOS applications will be deprecated soon</a>. And the time has come. When using macOS Mojave and trying to run 32-bit applications (the ones built with Delphi until 10.3.1, for example), users are receiving the following message:</p><p><img loading="lazy" alt="macos-mojave-32-bit-app-alert" src="/assets/images/macos-mojave-32-bit-app-alert-3ca08f7dc081299354067113f41b89c9.jpg" width="840" height="340" class="img_ev3q"></p><p>I would say that your application wouldn’t give much confidence to the user, do you agree? And, finally, in next macOS release, <a href="https://www.macrumors.com/roundup/macos-10-15/" target="_blank" rel="noopener noreferrer">macOS Catalina (10.15)</a>, 32-bit applications will simply <a href="https://www.macrumors.com/roundup/macos-10-15/#no_more_32-bit_apps" target="_blank" rel="noopener noreferrer">not work anymore</a>.</p><p>Thus, if you develop (or intend to develop) macOS applications with Delphi, it’s a relief to know that you won’t have those problems anymore: just build 64-bit macOS applications and everything is going to be alright!</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="ide-stability-and-performance">IDE stability and performance<a class="hash-link" href="#ide-stability-and-performance" title="Direct link to heading">​</a></h2><p>Another improvement that is worth mentioning is the significant improvement in IDE stability and performance. First Delphi Rio version brought us a refreshed IDE, with dark theme and more modern visual. But there were also issues: flickering, strange visual “effects”, slowness, the screen was redrawn several times when you resized the window, among other things.</p><p>That was significantly improved in this new update. If you are already using previous Delphi Rio 10.3 or 10.3.1, I believe you will notice the difference, with a smoother and snappier IDE in this regard.</p><p>Delphi Rio 10.3.2 is already available! There are many other improvements besides the ones mentioned above. If you are already using this new update, leave a comment telling what do you think about it!</p><p><a href="https://forum.landgraf.dev/t/what-i-liked-about-delphi-rad-studio-10-3-2-update/237" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What are memory leaks and what are their consequences?]]></title>
            <link>https://www.landgraf.dev/en/delphi-memory-leaks</link>
            <guid>delphi-memory-leaks</guid>
            <pubDate>Mon, 08 Jul 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[A discussion about memory leaks in Delphi and why it is important to avoid them.]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="delphi-memory-leaks" src="/assets/images/delphi-memory-leaks-8c4929ba8ecf27a5eb8cc009060fde4a.jpg" width="640" height="428" class="img_ev3q"></p><p>A <a href="https://en.wikipedia.org/wiki/Memory_leak" target="_blank" rel="noopener noreferrer">memory leak</a> happens when your application allocates a space in memory and never frees it again.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-memory-leaks-happen-in-delphi">How memory leaks happen in Delphi<a class="hash-link" href="#how-memory-leaks-happen-in-delphi" title="Direct link to heading">​</a></h2><p>In a Delphi application, memory spaces are allocated and released all the time. This is often done automatically by the compiler or by the RTL – for example, when allocating variables of primitive types, parameters that are passed to functions, etc. – and we usually do not have to worry about it.</p><p>However, there are many cases where we allocate memory manually. Instantiating an object is one of them:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Obj: = TMyObject.Create;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The code above will allocate a memory space and the Obj variable will point to the address of allocated memory space. To release it, you can use the Free method:</p><div class="language-C# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-C# codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Obj.Free;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>If the developer forgets to call the Free method, the memory space associated with the object is never released. We have a memory leak.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="consequences-of-memory-leaks">Consequences of memory leaks<a class="hash-link" href="#consequences-of-memory-leaks" title="Direct link to heading">​</a></h2><p>You may wonder: <em>"What’s the problem? I’ve seen this happen in my application and I’ve never had any practical problems!"</em></p><p>Actually, we often don’t see issues when there are just a few memory leaks, and the consequences also depend on the type of applications:</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="client-applications-desktop-or-mobile">Client applications (desktop or mobile)<a class="hash-link" href="#client-applications-desktop-or-mobile" title="Direct link to heading">​</a></h3><p>In general, in desktop or mobile applications, the consequences are not very serious. In current operating systems, the memory allocated by the application is generally released when the application is terminated, so as not to cause system instability.</p><p>Even so, if your application generates a lot of memory leaks, there may be some problems, caused by excessive memory usage:</p><ul><li><p><strong>Slow application and/or system</strong>: Disk access may be necessary to get around the memory shortage.</p></li><li><p><strong>Application abrupt closing</strong>: The operating system may force application termination due to excessive memory usage. This is more common in mobile applications.</p></li><li><p><strong>Application bugs</strong>: Application code that allocates memory can start to crash and cause bugs.</p></li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="server-applications">Server Applications<a class="hash-link" href="#server-applications" title="Direct link to heading">​</a></h3><p>When talking about server applications, the problem gets worse. That’s because ideally you will never close the server – you want it to run "forever" without needing to restart it.</p><p>The consequences are almost the same as the ones for desktop applications, but since the server application theoretically should never close, any small memory leak will have consequences in the future. The memory will slowly be consumed over days, weeks, months, and your server performance will degrade, bugs will appear and eventually the server will stop working.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="avoiding-memory-leaks">Avoiding Memory Leaks<a class="hash-link" href="#avoiding-memory-leaks" title="Direct link to heading">​</a></h3><p>There are techniques and tools to help you detect and remove memory leaks from your application. This will be covered in future posts.</p><p>What about you? Have you ever had issues in your application caused by memory leaks? Do you consider detecting and removing memory leaks a low-priority task compared to other more important things to worry about in your code? Leave a comment!</p><p><a href="https://forum.landgraf.dev/t/what-are-memory-leaks-and-what-are-their-consequences/236" target="_blank" rel="noopener noreferrer">Discuss about this article in our forum!</a></p>]]></content:encoded>
        </item>
    </channel>
</rss>