<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dariusz Suchojad</title>
    <description>The latest articles on DEV Community by Dariusz Suchojad (@dsuch).</description>
    <link>https://dev.to/dsuch</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F523223%2F1fe5f7bf-32d7-48ec-8cb7-33f00564bc3e.jpeg</url>
      <title>DEV Community: Dariusz Suchojad</title>
      <link>https://dev.to/dsuch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dsuch"/>
    <language>en</language>
    <item>
      <title>Modern REST API Tutorial in Python</title>
      <dc:creator>Dariusz Suchojad</dc:creator>
      <pubDate>Thu, 18 Dec 2025 11:00:50 +0000</pubDate>
      <link>https://dev.to/dsuch/modern-rest-api-tutorial-in-python-18m3</link>
      <guid>https://dev.to/dsuch/modern-rest-api-tutorial-in-python-18m3</guid>
      <description>&lt;p&gt;I wrote a &lt;strong&gt;&lt;a href="https://zato.io/en/tutorials/rest-api/python.html" rel="noopener noreferrer"&gt;hands-on Python REST API tutorial&lt;/a&gt;&lt;/strong&gt; focused on what actually works in production.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bfpr1qfa3sln2z21ha9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bfpr1qfa3sln2z21ha9.png" alt=" " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it skips&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Theory from 20 years ago that nobody uses anymore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it covers&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production-ready code in 60 minutes&lt;/li&gt;
&lt;li&gt;Clear conventions that eliminate team debates&lt;/li&gt;
&lt;li&gt;Maintainable APIs built on proven patterns&lt;/li&gt;
&lt;li&gt;Focused entirely on real-world B2B integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Link: &lt;a href="https://zato.io/en/tutorials/rest-api/python.html" rel="noopener noreferrer"&gt;https://zato.io/en/tutorials/rest-api/python.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>rest</category>
      <category>backend</category>
    </item>
    <item>
      <title>Complex cloud API integrations made easy with Zato and Python</title>
      <dc:creator>Dariusz Suchojad</dc:creator>
      <pubDate>Mon, 30 Nov 2020 15:28:13 +0000</pubDate>
      <link>https://dev.to/dsuch/complex-cloud-api-integrations-made-easy-with-zato-and-python-4ph4</link>
      <guid>https://dev.to/dsuch/complex-cloud-api-integrations-made-easy-with-zato-and-python-4ph4</guid>
      <description>&lt;p&gt;Cloud-based connections are a staple of modern API integrations - this article shows how, in just a few lines of Python code,  &lt;a href="https://zato.io?devto" rel="noopener noreferrer"&gt;Zato&lt;/a&gt; makes their usage easy, convenient and extremely effective.&lt;/p&gt;

&lt;center&gt;
![Architecture](https://zato.io/blog/images/cloud-made-easy/arch.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;As per the diagram, in this article we will integrate REST and FTP based resources with Dropbox but it needs to be emphasised that the exactly same code would work with other protocols.&lt;/p&gt;

&lt;p&gt;REST and FTP are just the most popular ways to upload data that can be delivered to Dropbox but if the source files were made available via AMQP, SAP or other applications - everything would the same.&lt;/p&gt;

&lt;p&gt;Similarly, in place of Dropbox we could use services based on AWS, Azure, OpenStack or other cloud providers - the same logic, approach and patterns would continue to work.&lt;/p&gt;

&lt;p&gt;Also, speaking of Dropbox, for simplicity we will focus on file uploads here but the full Drobpox API is available to Zato services, so anything that Dropbox offers is available in Zato too.&lt;/p&gt;

&lt;h1&gt;
  
  
  Layers
&lt;/h1&gt;

&lt;p&gt;We will use two layers in the solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Channel services&lt;/strong&gt; - receive data from external resources (here, REST and FTP) and deliver it to the Dropbox connector&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dropbox connector&lt;/strong&gt; - receives data from channels and uploads it to Dropbox&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The separation of concerns lets us easily add new channels as needs arise without having to modify the layer that connects to Dropbox.&lt;/p&gt;

&lt;p&gt;In this way, the solution can be extended at any time - if at one day we need to add SFTP, no changes to any already existing part will be required.&lt;/p&gt;

&lt;p&gt;First, we will create a Dropbox connector into which we can plug channel services.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dropbox connector - web-admin
&lt;/h1&gt;

&lt;p&gt;Let's create a new Dropbox connection definition in Zato web-admin first. Click Cloud -&amp;gt; Dropbox and fill out the form as below, remembering to click "Change token" afterwards.&lt;/p&gt;

&lt;p&gt;Note that the "User agent field" is required - this is part of metadata that Dropbox will accept. You can use it, for instance, to indicate whether you are connecting to Dropbox from a test vs. production environment.&lt;/p&gt;

&lt;center&gt;
![Web-admin menu - Dropbox](https://zato.io/blog/images/cloud-made-easy/webadmin-dropbox-menu.png)
&lt;/center&gt;

&lt;center&gt;
![Web-admin - Dropbox connection creation form](https://zato.io/blog/images/cloud-made-easy/webadmin-dropbox-form.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  Dropbox connector - Python
&lt;/h1&gt;

&lt;p&gt;And Here is the Python code that acts as the Dropbox connector. Note a few interesting points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It does not care where its input comes from. It just receives data. This is crucial because it means we can add any kind of a channel and the actual connector will continue to work without any interruptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The connector focuses on business functionality only - it is only&lt;br&gt;
&lt;a href="https://zato.io/docs/web-admin/intro.html" rel="noopener noreferrer"&gt;web-admin&lt;/a&gt; that specifies what the connection details are, e.g. the connector itself just sends data and does not even deals with details as low-level as security tokens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The underlying client object is an instance of dropbox.Dropbox from the official &lt;a href="https://dropbox-sdk-python.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Python SDK&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After hot-deploying the file, the service will be available as &lt;strong&gt;api.connector.dropbox&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-
&lt;/span&gt;
&lt;span class="c1"&gt;# Zato
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zato.server.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;

&lt;span class="c1"&gt;# Code completion imports
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dropbox&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dropbox&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zato.server.generic.api.cloud_dropbox&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CloudDropbox&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DropboxConnector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; Receives data to be uploaded to Dropbox.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api.connector.dropbox&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleIO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;input_required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="c1"&gt;# Connection to use
&lt;/span&gt;        &lt;span class="n"&gt;conn_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;My Connection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

        &lt;span class="c1"&gt;# Get the connection object
&lt;/span&gt;        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropbox&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;conn_name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="c1"&gt;# type: CloudDropbox
&lt;/span&gt;
        &lt;span class="c1"&gt;# Get the underlying Dropbox client
&lt;/span&gt;        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="c1"&gt;# type: Dropbox
&lt;/span&gt;
        &lt;span class="c1"&gt;# Upload the file received
&lt;/span&gt;        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;files_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  REST channel
&lt;/h1&gt;

&lt;p&gt;Now, let's add a service to accept REST-based file transfers - it will be a thin layer that will extract data from the HTTP payload to hand it over to the already existing connector service. The service can be added in the same file as the connector or it can be a separate file, it is up to you, it will work in the same. Below, it is in its own file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-
&lt;/span&gt;
&lt;span class="c1"&gt;# Zato
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zato.server.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;APIChannelRESTUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; Receives data to be uploaded to Dropbox.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api.channel.rest.upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="c1"&gt;# File name as a query parameter
&lt;/span&gt;        &lt;span class="n"&gt;file_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;

        &lt;span class="c1"&gt;# The whole data uploaded
&lt;/span&gt;        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_request&lt;/span&gt;

        &lt;span class="c1"&gt;# Invoke the Dropbox connector with our input
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api.connector.dropbox&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having uploaded the REST channel service, we need to create an actual REST channel for it. In web-admin, go to Connections -&amp;gt; Channels -&amp;gt; REST and fill out the form.&lt;/p&gt;

&lt;center&gt;
![Web-admin menu - Dropbox](https://zato.io/blog/images/cloud-made-easy/webadmin-rest-menu.png)
&lt;/center&gt;

&lt;center&gt;
![Web-admin - Dropbox connection creation form](https://zato.io/blog/images/cloud-made-easy/webadmin-rest-form.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  First tests
&lt;/h1&gt;

&lt;p&gt;At this point, we can already test it all out. Let's use curl to POST data to Zato. Afterwards, we confirm in Dropbox that a new file was created as expected.&lt;/p&gt;

&lt;p&gt;Note that we use POST to send the input file which is why we need the file_name query parameter too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    @/path/to/my-file.txt &lt;span class="se"&gt;\&lt;/span&gt;
    http://localhost:11223/api/upload?file_name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-file.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
![Web-admin - Dropbox listing with a file uploaded](https://zato.io/blog/images/cloud-made-easy/dropbox-listing-1.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  FTP connection definition
&lt;/h1&gt;

&lt;p&gt;Having made sure that the connector delivers its files through REST, let's focus on FTP, first creating a new FTP connection definition in web-admin.&lt;/p&gt;

&lt;center&gt;
![Web-admin menu - FTP](https://zato.io/blog/images/cloud-made-easy/webadmin-ftp-menu.png)
&lt;/center&gt;

&lt;center&gt;
![Web-admin - FTP connection creation form](https://zato.io/blog/images/cloud-made-easy/webadmin-ftp-form.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  FTP service
&lt;/h1&gt;

&lt;p&gt;We need some Python code now - it will connect to the FTP server, list all files in a specific directory and send them all to the Dropbox connector.&lt;/p&gt;

&lt;p&gt;The connector will not even notice that the files do not come from REST this time, it will simply accept them on input like previously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-
&lt;/span&gt;
&lt;span class="c1"&gt;# Zato
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zato.server.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;APIChannelSchedulerUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; Receives data to be uploaded to Dropbox.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api.channel.scheduler.upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="c1"&gt;# Get a handle to an FTP connection
&lt;/span&gt;        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outgoing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;My FTP Connection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Directory to find the files in
&lt;/span&gt;        &lt;span class="n"&gt;file_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

        &lt;span class="c1"&gt;# List all files ..
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="c1"&gt;# .. construct the full path ..
&lt;/span&gt;            &lt;span class="n"&gt;full_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# .. download each file ..
&lt;/span&gt;            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getbytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# .. send it to the Dropbox connector ..
&lt;/span&gt;            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api.connector.dropbox&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="c1"&gt;# .. and delete the file from the FTP server.
&lt;/span&gt;            &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  More tests
&lt;/h1&gt;

&lt;p&gt;We have an FTP connection and we have a service - after uploading some files to the FTP server, we can test the new service now using web-admin. Navigate to Services -&amp;gt; List services -&amp;gt; Choose "api.channel.scheduler.upload" -&amp;gt; Invoker and click Submit.&lt;/p&gt;

&lt;p&gt;This will invoke the service directly, without a need for any channel. Next, we can list recent additions in Dropbox to confirm that the file we were uploaded which means that the service connected to FTP, the files were downloaded and the Dropbox connector delivered them successfully.&lt;/p&gt;

&lt;center&gt;
![Web-admin - Dropbox listing with several files uploaded](https://zato.io/blog/images/cloud-made-easy/dropbox-listing-2.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  Scheduler
&lt;/h1&gt;

&lt;p&gt;Invoker the service from web-admin is good but we would like to automate the process of transferring data from FTP to Dropbox - for that, we will create a new job in the scheduler. In web-admin, go to Scheduler and create a new interval-based job.&lt;/p&gt;

&lt;p&gt;Note, however, that the job's start date will be sent to the scheduler using your user's preferred timezone. By default it is set to UTC so make sure that you set it to another one if your current timezone is not UTC - go to Settings and pick the correct timezone.&lt;/p&gt;

&lt;center&gt;
![Web-admin - User settings](https://zato.io/blog/images/cloud-made-easy/webadmin-settings.png)
&lt;/center&gt;

&lt;center&gt;
![Web-admin - Change one's timezone](https://zato.io/blog/images/cloud-made-easy/webadmin-settings-form.png)
&lt;/center&gt;

&lt;p&gt;Now, on to the creation of a new job. Note that if this was a real integration project, the interval would be probably set to a more realistic one, e.g. if you batch transfer PDF invoices then doing it once a minute or twice an hour would probably suffice.&lt;/p&gt;

&lt;center&gt;
![Web-admin menu - Scheduler](https://zato.io/blog/images/cloud-made-easy/webadmin-scheduler-menu.png)
&lt;/center&gt;

&lt;center&gt;
![Web-admin - Scheduler, creating a new job](https://zato.io/blog/images/cloud-made-easy/webadmin-scheduler-form.png)
&lt;/center&gt;

&lt;h1&gt;
  
  
  Going even further
&lt;/h1&gt;

&lt;p&gt;We have just concluded the process - in a few steps we connected REST, FTP and Dropbox. Moreover, it was done in an extensible way. Should a business need arise, there is nothing preventing us from adding more data sources.&lt;/p&gt;

&lt;p&gt;Not only that, if one day we need to add more data sinks, e.g. S3 or SQL, we could it in the same easy way. Or we could publish messages to topics and guaranteed delivery queues for Zato to manage the whole delivery life-cycle, there are no limits whatsoever.&lt;/p&gt;

&lt;p&gt;This integration example was but a tiny part of what Zato is capable of. To learn more about the platform - do visit the &lt;a href="https://zato.io/docs/?devto" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and read the friendly &lt;a href="https://zato.io/docs/tutorial/01.html?devto" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; that will get you started in no time.&lt;/p&gt;

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