{"componentChunkName":"component---src-templates-tag-jsx","path":"/blog/tag/developer-relations/","result":{"data":{"prismic":{"allFeaturedblogs":{"edges":[{"node":{"featured_blogs_enabled":true,"heading":[{"type":"paragraph","text":"Featured posts","spans":[]}],"featured_blog_1":{"__typename":"PRISMIC_Blog","_linkType":"Link.document","blog_header_image":{"dimensions":{"width":790,"height":395},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/6d8d81b1-971a-4313-b033-b4e125cb14a0_MondoDB-blog-header-790x395.PNG?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Introducing DigitalOcean Managed MongoDB – a fully managed, database as a service for modern apps","spans":[]}],"blog_post_date":"2021-06-29","blog_post_content":[{"type":"paragraph","text":"MongoDB is one of the most popular databases, and it’s ideal for apps that evolve rapidly and need to handle huge volumes of data and traffic. It offers advantages like flexible document schemas, code-native data access, change-friendly design, and easy horizontal scale-out.","spans":[{"start":22,"end":44,"type":"hyperlink","data":{"link_type":"Web","url":"https://db-engines.com/en/ranking","target":"_blank"}}]},{"type":"paragraph","text":"However, building and maintaining MongoDB clusters from the ground up can be a huge undertaking. Developers often complain that they have to spend their valuable time and resources on database management. Well, we’ve been listening and have some great news: accessing and managing MongoDB on DigitalOcean just got a lot simpler!","spans":[]},{"type":"paragraph","text":"We are excited to announce that DigitalOcean Managed MongoDB is now in General Availability. Managed MongoDB is a fully managed, database as a service (DBaaS) offering from DigitalOcean, built in partnership with and certified by MongoDB Inc. It provides you all the technical capabilities that make MongoDB so beloved in the developer community. Together we have ensured that you will get access to all the latest releases of the MongoDB document database as they become available.","spans":[{"start":32,"end":91,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/managed-databases-mongodb/"}},{"start":230,"end":241,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.mongodb.com/","target":"_blank"}}]},{"type":"paragraph","text":"Managed MongoDB simplifies the MongoDB administration. Developers of all skill levels, even those who do not have prior experience in databases, can spin up MongoDB clusters in just a few minutes. We handle the provisioning, managing, scaling, updates, backups, and security of your MongoDB clusters, allowing you to offload the complex, time consuming –yet critical – database administration tasks to us. This empowers you to focus on what really matters: building awesome apps.","spans":[]},{"type":"embed","oembed":{"height":113,"width":200,"embed_url":"https://www.youtube.com/watch?v=NvHQSV7jnKA","type":"video","version":"1.0","title":"Create a MongoDB Database on DigitalOcean","author_name":"DigitalOcean","author_url":"https://www.youtube.com/c/Digitalocean","provider_name":"YouTube","provider_url":"https://www.youtube.com/","cache_age":null,"thumbnail_url":"https://i.ytimg.com/vi/NvHQSV7jnKA/hqdefault.jpg","thumbnail_width":480,"thumbnail_height":360,"html":"<iframe width=\"200\" height=\"113\" src=\"https://www.youtube.com/embed/NvHQSV7jnKA?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>"}},{"type":"heading2","text":"Benefits of Managed MongoDB","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"list-item","text":"Easy set up and maintenance: We create the database clusters for you. Simply choose the cluster configuration (e.g., memory, disk size, number of nodes, etc.), and the data center in which you want to host the database. Follow a few simple steps and your database cluster will be up and running in a matter of minutes. You can spin up clusters using the cloud control panel, CLI, or API.\n\n","spans":[{"start":0,"end":28,"type":"strong"}]},{"type":"list-item","text":"Automatic daily backups with point in time recovery: Data is one of the most important assets of an app, so it’s critical to backup your database. We take backups of your entire clusters automatically on a daily basis, for free. We also provide a point in time recovery for 7 days, that way if things go wrong due to human error, machine error, or some combination of both, you can easily restore the database as it was at any point in the previous 7 days. \n\n","spans":[{"start":0,"end":52,"type":"strong"}]},{"type":"list-item","text":"Automatic updates and access to latest MongoDB releases: You get access to MongoDB 4.4. This is the latest release of MongoDB and comes packed with numerous enhancements like hedged reads, rust, and swift drivers. Since we have developed Managed MongoDB in partnership with MongoDB Inc, you will always get access to new releases as they become available. With Managed MongoDB, the updates happen automatically. Just select a date and time for the updates and we take care of the rest. This makes it easy to stay up to date with MongoDB releases without disrupting your business.\n\n","spans":[{"start":0,"end":56,"type":"strong"},{"start":148,"end":169,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.mongodb.com/new","target":"_blank"}}]},{"type":"list-item","text":"High availability with automated failover: If your database goes down, it can take down the entire app, leading to bad customer experiences. With Managed MongoDB, you can easily minimize the downtime for your database and make it highly available with standby nodes. Standby nodes add redundancy, so if for example the primary node fails, the standby node is immediately promoted to primary and begins serving requests while we provision a replacement standby node in the background.\n\n","spans":[{"start":0,"end":42,"type":"strong"}]},{"type":"list-item","text":"Scale up easily to handle traffic spikes: As your app gains traction and the usage grows, it’s important to have a database that can keep up with the increased demand. With Managed MongoDB, you can easily scale up the size of database nodes when needed.\n\n","spans":[{"start":0,"end":41,"type":"strong"}]},{"type":"list-item","text":"Secure by default: Since data is critical, it also needs to be secure. We encrypt data at rest with LUKS and in transit with SSL. When you create a new cluster, it’s placed in a VPC network by default that provides a more secure connection between resources. You can also restrict access to your nodes to prevent brute-force password and denial-of-service attacks.","spans":[{"start":0,"end":18,"type":"strong"},{"start":178,"end":189,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/networking/vpc/"}}]},{"type":"heading2","text":"The need for Managed Databases","spans":[]},{"type":"paragraph","text":"DigitalOcean’s mission is to simplify cloud computing so developers, startups, and SMBs can spend more time building software that changes the world. While databases are a critical component to any application, building, maintaining, and scaling them can be complex and time consuming. For developers that are building apps for their business, database administration is often not a core focus area. But it’s quite common to find developers that write the code and then also roll up their sleeves to maintain databases. Such users would rather offload the tedious database administration and focus their limited time and energy on building and enhancing their apps. ","spans":[]},{"type":"paragraph","text":"With this in mind, we introduced Managed Databases a couple of years ago and are excited to add Managed MongoDB to our portfolio. With this release, DigitalOcean Managed Databases now supports the following engines:","spans":[{"start":33,"end":50,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/managed-databases/"}}]},{"type":"image","url":"https://images.prismic.io/www-static/87745cc1-1c5f-4463-b104-104b7fc30dc7_managed-databases-logos.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":849,"height":104}},{"type":"paragraph","text":"Managed MongoDB launch comes on the heels of DigitalOcean App Platform, a modern, reimagined PaaS (Platform as a Service) that we released a few months ago. App Platform makes it very easy to build, deploy, and scale apps and static sites. You can deploy code by simply pointing to your GitHub and GitLab repos, and App Platform will do all the heavy lifting of managing infrastructure, app runtimes, and dependencies. App Platform, along with Managed Databases, helps fulfill DigitalOcean’s mission by empowering developers, startups, and SMBs to focus more on their apps, and less on the underlying infrastructure and databases.","spans":[{"start":45,"end":70,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/app-platform/"}}]},{"type":"heading2","text":"How Managed MongoDB works","spans":[]},{"type":"paragraph","text":"DigitalOcean provides you with various compute options to build your apps like:","spans":[]},{"type":"list-item","text":"Droplets: On-demand, Linux virtual machines suitable for production business applications and personal passion projects.","spans":[{"start":0,"end":8,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/droplets/"}}]},{"type":"list-item","text":"DigitalOcean Kubernetes: Managed Kubernetes with automatic scaling, upgrades, and a free control plane.","spans":[{"start":0,"end":23,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/kubernetes/"}}]},{"type":"list-item","text":"DigitalOcean App Platform: A fully managed Platform as a Service.","spans":[{"start":0,"end":25,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/app-platform/"}}]},{"type":"paragraph","text":"No matter which compute option you choose to build your apps, you can easily add Managed MongoDB to it. In addition to this, Managed MongoDB also integrates with the Node.js 1-Click App from DigitalOcean Marketplace making it a lot easier to build Node.js apps.","spans":[{"start":166,"end":215,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/nodejs"}}]},{"type":"heading2","text":"Simple, predictable pricing","spans":[]},{"type":"paragraph","text":"Just like all DigitalOcean products, Managed MongoDB provides simple, predictable pricing that allows you to control costs and prevent any surprise bills. You can spin up a database cluster for just $15/month, or a highly available three-node replica set for $45/month. Click here for more information.","spans":[{"start":270,"end":301,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/pricing/#managed-databases"}}]},{"type":"heading2","text":"Regional availability","spans":[]},{"type":"paragraph","text":"Managed MongoDB is currently available in the following regions:","spans":[]},{"type":"list-item","text":"NYC3 (New York, USA)","spans":[]},{"type":"list-item","text":"FRA1 (Frankfurt, Germany)","spans":[]},{"type":"list-item","text":"AMS3 (Amsterdam, Netherlands)","spans":[]},{"type":"paragraph","text":"We will be making Managed Mongo available in other regions soon. Please check out the release notes for most up to date information on regional availability.","spans":[{"start":86,"end":99,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/release-notes/"}}]},{"type":"heading2","text":"Join us at deploy, DigitalOcean’s virtual user conference","spans":[]},{"type":"paragraph","text":"Today we have deploy, DigitalOcean’s signature user conference, which focuses on celebrating, educating, and connecting awesome builders from all over the world.","spans":[{"start":14,"end":20,"type":"hyperlink","data":{"link_type":"Web","url":"https://deploy.digitalocean.com/home"}}]},{"type":"paragraph","text":"Check out the keynote session from DigitalOcean's CEO, Yancey Spruill, in which he talks about where we're headed as a company and shares some exciting product updates. His keynote will be followed by sessions from community members, engineers, customers, and other experts that are building technologies and businesses powered by the cloud. With live Q&A and an active Discord server, there’s ample opportunity to engage and learn something new. Click here to attend the deploy conference.","spans":[{"start":14,"end":69,"type":"hyperlink","data":{"link_type":"Web","url":"https://deploy.digitalocean.com/agenda/session/552806"}},{"start":347,"end":384,"type":"hyperlink","data":{"link_type":"Web","url":"http://do.co/deploy-discord"}},{"start":461,"end":489,"type":"hyperlink","data":{"link_type":"Web","url":"http://do.co/deploy"}}]},{"type":"paragraph","text":"We are also launching a hackathon for DigitalOcean Managed MongoDB. Learn how you can participate, submit an app and get a t-shirt.","spans":[{"start":24,"end":66,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/mongodb-hackathon"}}]},{"type":"paragraph","text":"We hope you will give Managed MongoDB a try. Here are some sample datasets and sample apps that you can use to kick the tires. Check out the docs and let us know what you think!","spans":[{"start":22,"end":43,"type":"hyperlink","data":{"link_type":"Web","url":"https://cloud.digitalocean.com/databases/new?engine=mongodb"}},{"start":59,"end":90,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/do-community/mongodb-resources","target":"_blank"}},{"start":141,"end":145,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.digitalocean.com/products/databases/mongodb/"}}]},{"type":"paragraph","text":"If you’d like to have a conversation about using DigitalOcean and Managed MongoDB in your business, please feel free to contact our sales team.","spans":[{"start":120,"end":142,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/company/contact/sales/"}}]},{"type":"paragraph","text":"Happy coding!","spans":[]},{"type":"paragraph","text":"André Bearfield","spans":[]},{"type":"paragraph","text":"Director of Product Management","spans":[]}],"tags":[{"tag1":{"__typename":"PRISMIC_Tag","tag":"Product Updates","_linkType":"Link.document","_meta":{"uid":"product-updates"}}}],"author":{"__typename":"PRISMIC_Author","author_name":"André Bearfield","author_image":{"dimensions":{"width":553,"height":547},"alt":"André Bearfield","copyright":null,"url":"https://images.prismic.io/www-static/fdc7c85186f0a850b04083e1d4306bd1c19772e8_andre-bearfield.png?auto=compress,format"},"_meta":{"uid":"andre-bearfield"}},"_meta":{"uid":"introducing-digitalocean-managed-mongodb"}},"featured_blog_2":{"__typename":"PRISMIC_Blog","_linkType":"Link.document","blog_header_image":{"dimensions":{"width":790,"height":400},"alt":"Droplet Console","copyright":null,"url":"https://images.prismic.io/www-static/710499ae-78cc-4179-afc1-15793637b200_DODX3727-790x400-logo-2.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Securely connect to Droplets with SSH key pairs using a new Droplet Console","spans":[]}],"blog_post_date":"2021-08-10","blog_post_content":[{"type":"paragraph","text":"The famous author Ken Blanchard once said, “Feedback is the breakfast of champions.\" This is something we truly believe at DigitalOcean, and we always strive to enhance our products based on customer feedback.","spans":[]},{"type":"paragraph","text":"With this goal in mind, we are excited to introduce a new Droplet Console that will make it much easier to connect to your Droplets securely. The new Droplet Console provides one-click SSH access to your Droplets through a native-like SSH/Terminal experience. It also eliminates the need for a password or manual configuration of SSH keys. Starting today, we’re pleased to announce that the new Droplet Console is now available to all Droplet users.","spans":[]},{"type":"heading2","text":"Why you should be using Secure Shell (SSH) ","spans":[]},{"type":"paragraph","text":"Password-based security is notoriously insecure due to password fatigue and the overuse of passwords such as ‘123456’. Secure Shell or SSH is a network communication protocol that solves this by using passwordless solutions for encryption, enabling two computers to communicate and securely share data. At a high level, SSH works by creating cryptographic key pairs consisting of a public and private key, which are computer generated and stored separately to ensure their security. ","spans":[{"start":80,"end":117,"type":"hyperlink","data":{"link_type":"Web","url":"https://cybernews.com/best-password-managers/most-common-passwords/"}}]},{"type":"paragraph","text":"SSH has become the default encryption protocol for many industries, but it was difficult to use SSH keys with DigitalOcean’s current Recovery (VNC) console, which is why we developed our new Droplet Console. The new Droplet Console is backed by an agent that security supervises the key pair, while also providing one-click SSH access to our users. You can see the full list of features below.","spans":[]},{"type":"heading2","text":"The new Droplet Console: More time saving, less time wasting ","spans":[]},{"type":"paragraph","text":"The new Droplet Console is for everyone who is looking to build fast, secure apps and avoid hassles with SSH access & usability issues.","spans":[]},{"type":"paragraph","text":"In addition to easier SSH access, the new Droplet Console comes with:","spans":[]},{"type":"list-item","text":"Copy/paste text: Instead of typing lengthy key pairs and text manually, you can use copy/paste to save time. ","spans":[{"start":0,"end":17,"type":"strong"}]},{"type":"list-item","text":"Multi-color support: Multi-color support makes the console more useful and intuitive, and breaks the conventional standard appearance which is black text on a white background. ","spans":[{"start":0,"end":41,"type":"strong"}]},{"type":"list-item","text":"Multi-language support: DigitalOcean’s new Droplet Console supports multiple languages, meaning you can now type and view any content in any language that is supported by UTF-8","spans":[{"start":0,"end":24,"type":"strong"}]},{"type":"list-item","text":"OS/images supported: Linux distributions (Ubuntu(16.04 - 20.04), Fedora (32 & 33), Debian (9), CentOS (7.6 & 8.3), CentOS 8 Stream, Rocky Linux and Marketplace images.","spans":[{"start":0,"end":20,"type":"strong"},{"start":148,"end":159,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"paragraph","text":"The new Droplet Console is available by default on any new Droplets you spin up. You can also enable it manually on older Droplets. Click here to learn more!","spans":[{"start":132,"end":157,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.digitalocean.com/products/droplets/how-to/connect-with-console/"}}]},{"type":"paragraph","text":"Check out this short walkthrough video that shows the new Droplet Console in action: ","spans":[]},{"type":"embed","oembed":{"type":"video","embed_url":"https://www.youtube.com/watch?v=Qt7QihVuxiE","title":"Access Your Droplet Terminal Through the Web Console","provider_name":"YouTube","thumbnail_url":"https://i.ytimg.com/vi/Qt7QihVuxiE/hqdefault.jpg","provider_url":"https://www.youtube.com/","author_name":"DigitalOcean","author_url":"https://www.youtube.com/c/Digitalocean","height":113,"width":200,"version":"1.0","thumbnail_height":360,"thumbnail_width":480,"html":"<iframe width=\"200\" height=\"113\" src=\"https://www.youtube.com/embed/Qt7QihVuxiE?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>"}},{"type":"paragraph","text":"We hope you’re excited about the new Droplet Console. You’re welcome to spin some Droplets up right now, and try out the new Droplet Console – why wait?","spans":[{"start":72,"end":103,"type":"hyperlink","data":{"link_type":"Web","url":"https://cloud.digitalocean.com/droplets/new"}}]},{"type":"paragraph","text":"Happy coding!","spans":[]},{"type":"paragraph","text":"Harsh Banwait, Senior Product Manager","spans":[]}],"tags":[{"tag1":{"__typename":"PRISMIC_Tag","tag":"Product Updates","_linkType":"Link.document","_meta":{"uid":"product-updates"}}}],"author":{"__typename":"PRISMIC_Author","author_name":"Harsh Banwait","author_image":{"dimensions":{"width":600,"height":399},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/e83ff690-b20c-4d88-a2b6-57e562558cd6_download.png?auto=compress,format"},"_meta":{"uid":"harsh-banwait"}},"_meta":{"uid":"new-droplet-console-ssh-support"}},"featured_blog_3":{"__typename":"PRISMIC_Blog","_linkType":"Link.document","blog_header_image":{"dimensions":{"width":790,"height":400},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/588e28d3-d41e-480b-937b-8c3b19201f6e_DODX3568-790x400-Blog.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"How to scale your SaaS product without breaking the bank","spans":[]}],"blog_post_date":"2021-06-22","blog_post_content":[{"type":"paragraph","text":"These days, if you are in the business of software, chances are you are delivering or plan to deliver your services using a Software-as-a-Service (SaaS) model. A combination of internet-based delivery, subscription-based pricing, and low-friction product experiences have made SaaS solutions valuable tools for their users, and an excellent vehicle for software builders looking to distribute their products.","spans":[]},{"type":"paragraph","text":"These factors have made SaaS solutions ubiquitous; SaaS is the largest segment in the public cloud market, and is used to provide functionality ranging from personal finance apps for consumers, to productivity software for businesses, and even tools and services for software developers themselves to compose their applications and simplify their workflows. It is also not uncommon to find micro-SaaS applications being built for specific industries such as retail, job functions such as accounting or marketing, or tasks such as event management. ","spans":[]},{"type":"paragraph","text":"The best thing about this SaaS wave has been that it has allowed a new generation of software builders to build and monetize applications and participate in the digital economy. Previously, you had to be a big company with lots of resources, name recognition and distribution networks to successfully sell software products. Now, irrespective of whether you are a single person working on a passion project, a small team of developers in a startup, or a small and medium-sized business (SMB), the SaaS model enables you to express your ideas in the form of software and deliver them to customers anywhere in the world.","spans":[]},{"type":"heading2","text":"The unique challenges of building SaaS solutions","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Despite the opportunities that come with the widespread adoption of SaaS products, software builders still have to answer key questions in their journey to building successful SaaS products. Understanding what customers to target, features to prioritize, how to price your product, and how to acquire customers are all critical questions to figure out while you are also doing the important job of actually building and operating the product. ","spans":[]},{"type":"paragraph","text":"Writing the code, testing, deployment, monitoring the usage in production, and ensuring that your apps are able to handle the additional demand when customer base and usage grows are all essential and time-consuming tasks.","spans":[]},{"type":"paragraph","text":"Additionally, being able to test multiple ideas, pivot, and double down on the ideas that actually work is critical in early stages of SaaS development. Once growth comes, it is equally important to scale up without compromising on performance or reliability. Needless to say, all of this needs to be economically viable as well, since not everyone has the resources of large SaaS providers like Salesforce or Adobe.","spans":[]},{"type":"heading2","text":"Cloud Computing enables builders but also poses challenges","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Fortunately, for the act of building and operating your apps, cloud computing can help take some load off your shoulders. Unless you have the scale and resources of Facebook, chances are you are not going to set up your own data centers to host the computing infrastructure that powers your SaaS company. Public cloud infrastructure providers can bring great value to SaaS builders by providing on-demand computing services with usage-based pricing. However, just like how the legacy software companies weren't built for the SaaS model, the early (and big) cloud computing services were not optimized for the unique needs of small SaaS building teams. ","spans":[]},{"type":"paragraph","text":"Smaller SaaS teams face challenges with large cloud computing providers, including:","spans":[]},{"type":"heading4","text":"Too many technology options","spans":[]},{"type":"paragraph","text":"There are just too many options for tech stacks on which to build your SaaS - programming languages, application development frameworks, libraries, runtime environments, architectural patterns, and deployment models - and the list is growing by the day.","spans":[]},{"type":"heading4","text":"Complexity of cloud computing services","spans":[]},{"type":"paragraph","text":"Even when you have decided on a technology stack, there is a lot of cloud vendor-specific terminology you need to learn and heavy lifting you need to do to build on the cloud, not all of which contributes to making your SaaS applications successful.","spans":[]},{"type":"heading4","text":"Unpredictable costs","spans":[]},{"type":"paragraph","text":"The experimentation necessary in early stages of SaaS development, as well as the scaling of applications required during the growth phase, call for affordable and predictable pricing from your cloud provider. The last thing SaaS teams want is surprising and indecipherable bills from your cloud provider. Unfortunately, smaller businesses often experience unpredictable costs with cloud providers who are busy serving only the large enterprises.","spans":[]},{"type":"heading2","text":"DigitalOcean provides a simple, cost effective solution for SaaS builders","spans":[]},{"type":"paragraph","text":"Fortunately, at DigitalOcean we have a laser focus on small software development teams, who are trying to build the next generation of applications. Today, DigitalOcean customers are already building SaaS applications which serve all kinds of customers.","spans":[{"start":191,"end":217,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/solutions/saas/"}}]},{"type":"paragraph","text":"We believe SaaS builders should focus on building apps that power their business, and not spend their valuable time on managing infrastructure. That is exactly what we have been able to enable through our intuitive products that are built for scale and reliability.","spans":[{"start":205,"end":223,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/"}}]},{"type":"list-item","text":"Vidazoo is an advertising technology company specializing in video streaming and serving. It serves video ads to thousands of websites and handles close to 10 billion requests per day. \n\n“We are as much a data company as an adtech company. Our business relies on speedy and accurate data processing at massive scale. DigitalOcean provides us the perfect set of tools to operate our SaaS business profitably, while not making us feel the need to become full time system administrators. We plan to move a lot of our apps to DigitalOcean App Platform and other fully managed products.” - Roman Svichar, CTO of Vidazoo","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://vidazoo.com/"}},{"start":187,"end":583,"type":"em"}]},{"type":"paragraph","text":"We believe in meeting customers where they are. If they already have an understanding of cloud infrastructure technologies, they should be able to leverage that knowledge and get started with our products without any further ramp up.","spans":[]},{"type":"list-item","text":"Whatfix is an enterprise SaaS provider that offers a digital adoption platform to businesses. The company helps enterprises gain the full value of their investments in enterprise applications by providing real-time, interactive, and contextual guidance to users of those applications. \n\n“What we really love about the DigitalOcean platform is the ease of use. We feel like we know infrastructure and can handle most of the configuration and management. What we needed from a cloud was not bells and whistles but efficiency and reliability. DigitalOcean provides us a platform to build our apps and then gets out of the way. Just how we like it.” - Achyuth Krishna, Director of Engineering of Whatfix","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://whatfix.com/blog/driving-the-future-now-were-excited-to-announce-our-90-million-series-d-funding/"}},{"start":287,"end":648,"type":"em"}]},{"type":"paragraph","text":"We understand that scaling while maintaining reliability of applications and profitability of business is important, so we provide robust solutions which minimize downtime.","spans":[]},{"type":"list-item","text":"Centra is a SaaS-based e-commerce platform for global direct-to-consumer and wholesale e-commerce brands. Centra provides a powerful e-commerce backend that lets brands build pixel-perfect, custom designed, online flagship stores. \n\n“How do we enable our customers to create differentiated online experiences? How do we ensure their e-commerce apps stay up and running at all times? How do we scale on-demand when traffic grows or new customers come in? These are the questions that we ask ourselves every day. Thankfully, we have a partner in DigitalOcean that provides just the platform to answer those questions enabling us to guarantee 99.9% uptime for our clients.” - Martin Jensen, CEO of Centra","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://centra.com/"}},{"start":233,"end":673,"type":"em"}]},{"type":"paragraph","text":"These are just a few examples of SaaS businesses finding success on DigitalOcean. We are constantly amazed by the creativity and innovation that software builders are utilizing our platform for. If you are interested in learning more about product updates, technical deep-dives and best practices for building SaaS products and businesses, please contact us to learn how we can help you get started. ","spans":[{"start":340,"end":357,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/migrate/?utmmedium=blog","target":"_blank"}}]},{"type":"paragraph","text":"Come build with DigitalOcean!","spans":[]},{"type":"paragraph","text":"Looking to migrate your SaaS to DigitalOcean? Leverage free infrastructure credits, robust training, and technical support to ensure a worry-free migration.","spans":[{"start":0,"end":156,"type":"strong"},{"start":0,"end":156,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/migrate/?utmmedium=blog","target":"_blank"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Raman Sharma","spans":[]},{"type":"paragraph","text":"Vice President, Product & Programs Marketing","spans":[]}],"tags":[{"tag1":{"__typename":"PRISMIC_Tag","tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"author":{"__typename":"PRISMIC_Author","author_name":"Raman Sharma","author_image":{"dimensions":{"width":512,"height":512},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/497b4b14-d192-493a-8b66-7ae176ba99f3_raman.png?auto=compress,format"},"_meta":{"uid":"raman-sharma"}},"_meta":{"uid":"how-to-scale-your-saas-product-without-breaking-the-bank"}}}}]}}},"pageContext":{"limit":12,"skip":0,"numTagPages":2,"currentPage":1,"uid":"developer-relations","data":[{"node":{"author":{"_linkType":"Link.document","author_name":"Ali Wunderman","author_image":{"dimensions":{"width":512,"height":512},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/3765ffa6-f1d6-405f-8e9d-0b336d3c7db8_ali+wunderman.jpeg?auto=compress,format"},"_meta":{"uid":"ali-wunderman"}},"blog_header_image":{"dimensions":{"width":870,"height":440},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/49d47d15-ad17-41ef-aba8-69e58eb0ff33_blog-illustration-3-870x440.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Multi-cloud vs hybrid cloud: Everything your business needs to know ","spans":[]}],"blog_post_content":[{"type":"heading2","text":"Finding the cloud computing approach that’s right for your business ","spans":[]},{"type":"paragraph","text":"The rise of cloud computing has revolutionized business as we know it. The cloud has enabled businesses to focus their time and energy on building technology, rather than maintaining infrastructure, which has led to an innovation boom in startups and small businesses. ","spans":[]},{"type":"paragraph","text":"If you’re reading this, you probably don’t need a cloud computing definition, but here it is just in case: Cloud computing is the delivery of computing resources as a service, managed by the cloud provider rather than the end-user. This is in contrast to how businesses used to operate, namely by buying and managing their own software and hardware. Now that you’re committed to the cloud, the next step is knowing the differences and similarities between hybrid cloud and multi-cloud strategies, and figuring out which approach is right for your business. ","spans":[{"start":162,"end":174,"type":"em"}]},{"type":"paragraph","text":"To learn more about cloud computing, including its rich history, the various models and environments it incorporates, and the overall business benefits, read through this in-depth tutorial from DigitalOcean. If you’re not already familiar with DigitalOcean tutorials, they are an excellent and expansive treasure trove of information and resources created by DO employees and community members. ","spans":[{"start":166,"end":180,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/a-general-introduction-to-cloud-computing"}}]},{"type":"paragraph","text":"Right off the bat, hybrid cloud and multi-cloud sound suspiciously similar and are regularly used interchangeably. While both incorporate cloud services from more than one source, they do so in unique ways and refer to different cloud setups. Read on to dig deeper into this topic.","spans":[{"start":19,"end":31,"type":"strong"},{"start":36,"end":47,"type":"strong"}]},{"type":"heading2","text":"What is hybrid cloud computing?","spans":[]},{"type":"paragraph","text":"There is one major factor that makes hybrid cloud computing unique from multi-cloud: Hybrid cloud computing incorporates private cloud infrastructure alongside public cloud computing providers. For a cloud computing setup to be considered hybrid, it generally includes a private component, such as an onsite data center managed and maintained directly by the business using it.","spans":[{"start":37,"end":59,"type":"strong"},{"start":37,"end":59,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.zdnet.com/article/hybrid-cloud-what-it-is-why-it-matters/"}}]},{"type":"paragraph","text":"The benefits of adopting a hybrid cloud model include the ability to deploy in private IT environments, which can give businesses more control over their configurations and, especially for larger businesses, may lead to cost savings. Combining a private cloud environment with a public cloud for certain services can provide the required redundancy and reduce outages. Public clouds also enable businesses to scale quickly, so a hybrid model can take advantage of the scalability of public cloud services while hosting certain systems which may have specialized requirements to stay on a private cloud. ","spans":[{"start":27,"end":45,"type":"hyperlink","data":{"link_type":"Web","url":"https://venturebeat.com/2021/07/11/the-hybrid-cloud-balance-knowing-when-to-shift-between-public-and-private/"}}]},{"type":"paragraph","text":"The definition gets tricky when businesses start to incorporate multiple public cloud services into the mix. When multiple public cloud services are working in conjunction with each other, that’s multi-cloud, baby.  ","spans":[]},{"type":"heading2","text":"What is multi-cloud computing?","spans":[]},{"type":"paragraph","text":"A multi-cloud computing setup in its simplest form means that a business is using multiple public cloud providers to accomplish specific tasks or workloads; for example, one provider for analytics workloads and one for public-facing websites. As Raman Sharma, DigitalOcean’s VP of Product & Program Marketing, says: ‘The reasons given for choosing a multi-cloud setup are various - risk management, avoiding lock-in and reliance on one provider, compliance with regulatory requirements etc.’ ","spans":[{"start":2,"end":23,"type":"strong"},{"start":2,"end":23,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.netapp.com/knowledge-center/what-is-multicloud/"}},{"start":316,"end":379,"type":"hyperlink","data":{"link_type":"Web","url":"https://ramansharma.substack.com/p/multi-cloud-makes-sense-but-not-for"}}]},{"type":"paragraph","text":"Every cloud provider has its own pros and cons, and by combining several, a business can leverage the unique aspects of each. Typically this approach is taken to distribute resources in computing and lessen the chances of losing data or going offline. A multi-cloud setup is especially useful for small businesses as it enables them to optimize costs and services by choosing specific cloud providers for specific needs, rather than one larger cloud provider which may include services small businesses do not utilize. ","spans":[]},{"type":"paragraph","text":"Some businesses may leverage both a hybrid and multi-cloud environment, in which they use a private cloud in addition to multiple public clouds. However, if a business only uses one public cloud service in addition to a private cloud, that is considered only a hybrid cloud. The real differentiator here is that multi-cloud incorporates more than one public cloud. ","spans":[]},{"type":"heading3","text":"Choosing the right cloud option for your startup or small business","spans":[]},{"type":"paragraph","text":"Small businesses are the ideal candidate for adopting a hybrid or multi-cloud strategy. Many of today’s small businesses are cloud-native, meaning they are born in the cloud, and do not ever maintain their own infrastructure but instead adopt a multi-cloud strategy. As Sharma explains, “Most smaller companies adopt multi-cloud architectures almost by necessity. They have two needs: 1) They want to focus on building their apps and not reinvent the wheel. 2) They want tools and services that are best of breed in their respective areas, in terms of productivity and ROI.” Often, working with outside cloud providers is the result of going after their desired results on a budget that makes sense for their growing business.","spans":[{"start":104,"end":137,"type":"hyperlink","data":{"link_type":"Web","url":"https://en.wikipedia.org/wiki/Cloud_native_computing"}},{"start":270,"end":285,"type":"hyperlink","data":{"link_type":"Web","url":"https://ramansharma.substack.com/p/multi-cloud-makes-sense-but-not-for"}}]},{"type":"paragraph","text":"There is a case to be made for going with only one service provider, but this risks being unable to access specialized technology that supports innovative ideas and startup-speed iteration. ","spans":[]},{"type":"paragraph","text":"When it comes to going the hybrid or multi-cloud route, the decision really lies in where your business’s non-cloud resources are located. If purchasing and operating an onsite private cloud is possible and preferable due to unique needs your company may have around security or configuration, then hybrid is the right way to go. However, if that’s not essential or desirable, then taking a multi-cloud approach will still bring the same benefits in terms of fortification against disaster, dealing with compliance, and having flexibility, but without the autonomy that comes with having a private cloud. For most small and mid-sized businesses, multi-cloud provides the flexibility and reliability that is needed, without the complexity and infrastructure investment required for a hybrid cloud setup. ","spans":[{"start":299,"end":305,"type":"strong"},{"start":391,"end":402,"type":"strong"}]},{"type":"paragraph","text":"Cloud computing is all about empowering developers and businesses to create and explore as many options as possible, and building a hybrid or multi-cloud computing architecture is just another way to leverage what makes the cloud so powerful. To learn more about DigitalOcean’s cloud services and how they can benefit your business, view our product suite here.","spans":[{"start":338,"end":361,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/"}}]}],"blog_post_date":"2021-07-22","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"multi-cloud-vs-hybrid-cloud-computing"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Raman Sharma","author_image":{"dimensions":{"width":512,"height":512},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/497b4b14-d192-493a-8b66-7ae176ba99f3_raman.png?auto=compress,format"},"_meta":{"uid":"raman-sharma"}},"blog_header_image":{"dimensions":{"width":790,"height":400},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/0f1ce1e8-b345-419c-8b6a-4704a7d47b2d_DODX3673-790x400-logo.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Building a developer tools business on a developer-focused cloud","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Software is not only eating the world, it has developed an uncanny knack of inserting itself into every aspect of this world’s functioning. This means that any improvements to how software is developed have a real impact on the overall productivity of the global economy. Tools that improve the efficiency of software development, tools that allow software builder teams to raise the quality of their software, and services that enable businesses to run their apps reliably and securely are more important than ever before. As a result, there is an increased focus on building products and tools that make developer’s lives easier and their work more efficient.","spans":[{"start":21,"end":37,"type":"hyperlink","data":{"link_type":"Web","url":"https://a16z.com/2011/08/20/why-software-is-eating-the-world/"}}]},{"type":"heading2","text":"More developers are building more apps for more platforms than ever before","spans":[]},{"type":"paragraph","text":"Given such a huge demand for software experiences in our world, it is no surprise that the number of software developers globally continues to grow, and is projected to reach ~29M by 2024. The number of platforms available for developers to build apps upon is also growing. Desktop, mobile, smart devices, servers, web, cloud, embedded systems, edge, and IoT are just some examples of platforms developers are building on today. ","spans":[{"start":169,"end":187,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.statista.com/statistics/627312/worldwide-developer-population/"}}]},{"type":"paragraph","text":"The growth in software development presents an opportunity for software builders who build tools for other software builders. Some of the challenges software developers face today include: ","spans":[]},{"type":"list-item","text":"Developers need to write, build, test and debug their code with greater velocity. They need to collaborate with their teams and get feedback from their users. Most importantly, they need to continue to learn and skill up on the latest technologies and methodologies.","spans":[]},{"type":"list-item","text":"The apps they are building need to be composed together using building block services - databases, notifications, authentication, payments, communications, machine learning, visualization, and so on.","spans":[]},{"type":"list-item","text":"These apps need to be deployed using automated pipelines and once in production, they need to be operated using modern management and monitoring tools.","spans":[]},{"type":"list-item","text":"These apps need to be served from the most optimal locations to ensure low latency experience for their users.","spans":[]},{"type":"paragraph","text":"These and many more requirements stemming from the deluge of software development have created a new breed of developer tools. These tools are coming not only from large software companies of the past, but also from newer, more nimble companies that hyperfocus on certain important but underserved aspects of software development.","spans":[]},{"type":"heading2","text":"The growing demand for  developer tools and services","spans":[]},{"type":"paragraph","text":"In the past decade or so, the developer tools and services business has exploded. Innovative developer teams have found new and more efficient ways to solve software development challenges, and many have also created new possibilities and scenarios. Github, Twilio, Stripe, MongoDB, and even DigitalOcean are only a few examples of developer-focused companies that came in and carved out a name for themselves in crowded areas. They accomplished this by focusing on the needs of underserved audiences and underserved scenarios, and by providing differentiated experiences to developers. In fact, online Q&A forums like Stack Overflow, online programming interview practice tools like LeetCode, and online community forums like DEV are also examples of services that make developers’ lives easier.","spans":[]},{"type":"paragraph","text":"We can see this growth by looking at the Forbes Cloud 100 list, the annual ranking of the world’s top private cloud companies. In the previous iteration, ~20% of the companies included in that list were developer-focused businesses. Many others, though not developer products themselves, still provide extensibility points that allow software developers to extend the core capabilities of those products.","spans":[{"start":41,"end":57,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.forbes.com/cloud100/#7a5c6ce35f94"}}]},{"type":"heading2","text":"DigitalOcean: The ideal cloud for building developer tools","spans":[]},{"type":"paragraph","text":"At DigitalOcean, we know a thing or two about building a business that is focused on developers. We do it by:","spans":[]},{"type":"list-item","text":"Making developers more productive through a simple product experience.","spans":[{"start":44,"end":69,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/"}}]},{"type":"list-item","text":"Providing rich resources around all kinds of system administration, programming languages, cloud computing, and other software development topics. ","spans":[{"start":10,"end":24,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community"}}]},{"type":"list-item","text":"Giving back to the open source community. ","spans":[{"start":19,"end":40,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/open-source/"}}]},{"type":"heading2","text":"DigitalOcean is a trusted advisor for those building developer tools ","spans":[]},{"type":"paragraph","text":"Given our popularity with developers, it is no surprise that other businesses who are also trying to serve developers find DigitalOcean to be an ally with a shared ethos and developer-first mindset. Those building tools for developers need cloud services, and DigitalOcean helps them every step of the way.","spans":[]},{"type":"paragraph","text":"Businesses building developer tools on DigitalOcean include managed database solutions, edge computing platforms, serverless platforms, CI/CD solutions, API services, developer collaboration tools, online programming environments, among many others.","spans":[]},{"type":"list-item","text":"Bunnyshell’s mission is to allow developers to focus on building applications without worrying about infrastructure. It offers cloud-agnostic infrastructure management and application management, as well as DevOps tools for deployment and monitoring. Bunnyshell was looking for an infrastructure provider that allowed it to provide reliable services to its customers in a cost-effective manner.\n\n\"What we really like about DigitalOcean is the keen customer focus. As a company that serves developers, we want a partner that understands our customers and has a shared purpose. DigitalOcean not only provides world-class infrastructure but they truly get developers. This makes our job easier in terms of building management services for them.” \n- Roxana Ciobanu, CTO of BunnyShell","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.bunnyshell.com/"}},{"start":396,"end":743,"type":"em"},{"start":744,"end":779,"type":"em"}]},{"type":"heading4","text":"We create opportunities for others to do what they do best","spans":[]},{"type":"paragraph","text":"One unique thing about DigitalOcean as a cloud provider is our intense focus. We know where our strengths lie and where we need to rely on ecosystem partners. We believe the developer tools space is big enough for many successful businesses to not only co-exist but thrive, so instead of building everything ourselves we believe in nurturing an ecosystem of partners.  These partners build developer tools to enrich the experience of developers building on our platform or on any other. In fact, we go one step further, and actively invest in builders helping builders through our Hatch program.","spans":[{"start":543,"end":568,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/hatch/"}}]},{"type":"list-item","text":"Section is a developer-centric, container-based edge computing platform that empowers application engineers to run any workload, anywhere. They achieve this by leveraging a global network of DigitalOcean compute resources to run their edge network, aggregating existing infrastructure to make it flexible enough to meet the performance, security, and scalability needs of all websites.  \n\n“With DigitalOcean, we get a low-cost option with great support and reliability.”  \n- Director of Education & Awareness, Molly Wojcik","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/customers/section/"}},{"start":389,"end":472,"type":"em"},{"start":473,"end":522,"type":"em"}]},{"type":"heading4","text":"Developer tools providers can start small and scale economically","spans":[]},{"type":"paragraph","text":"Like most SaaS businesses, even developer tools and services products are faced with the problem of managing scale effectively. By providing industry leading price-performance packages, and especially attractive bandwidth economics at high scale, DigitalOcean provides a perfect cloud computing platform for developers tools.","spans":[{"start":100,"end":126,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/blog/how-to-scale-your-saas-product-without-breaking-the-bank"}},{"start":158,"end":184,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/pricing/calculator/"}},{"start":201,"end":231,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/blog/its-all-about-the-bandwidth-why-many-network-intensive-services-select-digitalocean-as-their-cloud/"}}]},{"type":"list-item","text":"Scraper API is an API that allows developers to build scalable web scrapers. If you’re running an online price comparison site and need to look up millions of websites a day, it requires a lot of infrastructure, IP addresses, browsers, etc. Scraper API manages this kind of platform which allows data-intensive companies to easily get the data they need. \n\n“We had been growing 10 to 20% a month. And every step of the way, DO had tools that were exactly what it was right. We came for the simplicity and then, as we scaled, we were actually very pleasantly surprised by many things on the platform”  \n- Daniel Ni, CEO & Founder of Scraper API","spans":[{"start":0,"end":11,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/customers/scraperapi/"}},{"start":357,"end":601,"type":"em"},{"start":602,"end":643,"type":"em"}]},{"type":"heading4","text":"Even developer tools builders need to build software efficiently","spans":[]},{"type":"paragraph","text":"Even if your team is capable of handling all the complexity thrown at you by major cloud providers, it doesn't mean you should. As a business, ultimately you are rewarded for the value you deliver to your customers, and not for the heavy lifting of infrastructure behind the scenes. DigitalOcean handles your infrastructure needs with ease, enabling you to focus on growing your business.","spans":[]},{"type":"list-item","text":"Parabol is a remote meeting platform for developer teams embracing agile practices. With the COVID-19 pandemic accelerating the business world’s shift online and to digital meetings, Parabol saw weekly customer sign-ups increase by more than ten times. They were able to quickly and easily redesign their infrastructure setup to allow for greater scale using additional DigitalOcean Droplets and the Managed Databases offering. This allowed Parabol to move from hosting approximately 10,000 simultaneous sessions to several hundred thousand without disruption. \n\n“As a founder, I didn’t want to spend time micromanaging infrastructure and doing XML sit-ups. When the pandemic happened and traffic grew tenfold, the robust DigitalOcean platform allowed us to scale seamlessly, without any disruption to our operations, so we could continue to focus on running our business.”  \n- Jordan Husney, Cofounder & CEO of Parabol","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/customers/parabol/"}},{"start":563,"end":875,"type":"em"},{"start":876,"end":919,"type":"em"}]},{"type":"paragraph","text":"DigitalOcean is proud to be a developer-focused company. If you are also in the business of serving software developers, then we would love to hear from you. ","spans":[]},{"type":"paragraph","text":"Contact us to learn more about free infrastructure credits, robust training, and technical support to help you migrate your developer-focused tools and services to our platform.","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/migrate/?utmmedium=blog"}}]},{"type":"paragraph","text":"Come build with DigitalOcean!","spans":[]},{"type":"paragraph","text":"","spans":[]}],"blog_post_date":"2021-07-15","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"building-a-developer-tools-business-on-a-developer-focused-cloud"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Raman Sharma","author_image":{"dimensions":{"width":512,"height":512},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/497b4b14-d192-493a-8b66-7ae176ba99f3_raman.png?auto=compress,format"},"_meta":{"uid":"raman-sharma"}},"blog_header_image":{"dimensions":{"width":790,"height":400},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/588e28d3-d41e-480b-937b-8c3b19201f6e_DODX3568-790x400-Blog.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"How to scale your SaaS product without breaking the bank","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"These days, if you are in the business of software, chances are you are delivering or plan to deliver your services using a Software-as-a-Service (SaaS) model. A combination of internet-based delivery, subscription-based pricing, and low-friction product experiences have made SaaS solutions valuable tools for their users, and an excellent vehicle for software builders looking to distribute their products.","spans":[]},{"type":"paragraph","text":"These factors have made SaaS solutions ubiquitous; SaaS is the largest segment in the public cloud market, and is used to provide functionality ranging from personal finance apps for consumers, to productivity software for businesses, and even tools and services for software developers themselves to compose their applications and simplify their workflows. It is also not uncommon to find micro-SaaS applications being built for specific industries such as retail, job functions such as accounting or marketing, or tasks such as event management. ","spans":[]},{"type":"paragraph","text":"The best thing about this SaaS wave has been that it has allowed a new generation of software builders to build and monetize applications and participate in the digital economy. Previously, you had to be a big company with lots of resources, name recognition and distribution networks to successfully sell software products. Now, irrespective of whether you are a single person working on a passion project, a small team of developers in a startup, or a small and medium-sized business (SMB), the SaaS model enables you to express your ideas in the form of software and deliver them to customers anywhere in the world.","spans":[]},{"type":"heading2","text":"The unique challenges of building SaaS solutions","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Despite the opportunities that come with the widespread adoption of SaaS products, software builders still have to answer key questions in their journey to building successful SaaS products. Understanding what customers to target, features to prioritize, how to price your product, and how to acquire customers are all critical questions to figure out while you are also doing the important job of actually building and operating the product. ","spans":[]},{"type":"paragraph","text":"Writing the code, testing, deployment, monitoring the usage in production, and ensuring that your apps are able to handle the additional demand when customer base and usage grows are all essential and time-consuming tasks.","spans":[]},{"type":"paragraph","text":"Additionally, being able to test multiple ideas, pivot, and double down on the ideas that actually work is critical in early stages of SaaS development. Once growth comes, it is equally important to scale up without compromising on performance or reliability. Needless to say, all of this needs to be economically viable as well, since not everyone has the resources of large SaaS providers like Salesforce or Adobe.","spans":[]},{"type":"heading2","text":"Cloud Computing enables builders but also poses challenges","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Fortunately, for the act of building and operating your apps, cloud computing can help take some load off your shoulders. Unless you have the scale and resources of Facebook, chances are you are not going to set up your own data centers to host the computing infrastructure that powers your SaaS company. Public cloud infrastructure providers can bring great value to SaaS builders by providing on-demand computing services with usage-based pricing. However, just like how the legacy software companies weren't built for the SaaS model, the early (and big) cloud computing services were not optimized for the unique needs of small SaaS building teams. ","spans":[]},{"type":"paragraph","text":"Smaller SaaS teams face challenges with large cloud computing providers, including:","spans":[]},{"type":"heading4","text":"Too many technology options","spans":[]},{"type":"paragraph","text":"There are just too many options for tech stacks on which to build your SaaS - programming languages, application development frameworks, libraries, runtime environments, architectural patterns, and deployment models - and the list is growing by the day.","spans":[]},{"type":"heading4","text":"Complexity of cloud computing services","spans":[]},{"type":"paragraph","text":"Even when you have decided on a technology stack, there is a lot of cloud vendor-specific terminology you need to learn and heavy lifting you need to do to build on the cloud, not all of which contributes to making your SaaS applications successful.","spans":[]},{"type":"heading4","text":"Unpredictable costs","spans":[]},{"type":"paragraph","text":"The experimentation necessary in early stages of SaaS development, as well as the scaling of applications required during the growth phase, call for affordable and predictable pricing from your cloud provider. The last thing SaaS teams want is surprising and indecipherable bills from your cloud provider. Unfortunately, smaller businesses often experience unpredictable costs with cloud providers who are busy serving only the large enterprises.","spans":[]},{"type":"heading2","text":"DigitalOcean provides a simple, cost effective solution for SaaS builders","spans":[]},{"type":"paragraph","text":"Fortunately, at DigitalOcean we have a laser focus on small software development teams, who are trying to build the next generation of applications. Today, DigitalOcean customers are already building SaaS applications which serve all kinds of customers.","spans":[{"start":191,"end":217,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/solutions/saas/"}}]},{"type":"paragraph","text":"We believe SaaS builders should focus on building apps that power their business, and not spend their valuable time on managing infrastructure. That is exactly what we have been able to enable through our intuitive products that are built for scale and reliability.","spans":[{"start":205,"end":223,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/"}}]},{"type":"list-item","text":"Vidazoo is an advertising technology company specializing in video streaming and serving. It serves video ads to thousands of websites and handles close to 10 billion requests per day. \n\n“We are as much a data company as an adtech company. Our business relies on speedy and accurate data processing at massive scale. DigitalOcean provides us the perfect set of tools to operate our SaaS business profitably, while not making us feel the need to become full time system administrators. We plan to move a lot of our apps to DigitalOcean App Platform and other fully managed products.” - Roman Svichar, CTO of Vidazoo","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://vidazoo.com/"}},{"start":187,"end":583,"type":"em"}]},{"type":"paragraph","text":"We believe in meeting customers where they are. If they already have an understanding of cloud infrastructure technologies, they should be able to leverage that knowledge and get started with our products without any further ramp up.","spans":[]},{"type":"list-item","text":"Whatfix is an enterprise SaaS provider that offers a digital adoption platform to businesses. The company helps enterprises gain the full value of their investments in enterprise applications by providing real-time, interactive, and contextual guidance to users of those applications. \n\n“What we really love about the DigitalOcean platform is the ease of use. We feel like we know infrastructure and can handle most of the configuration and management. What we needed from a cloud was not bells and whistles but efficiency and reliability. DigitalOcean provides us a platform to build our apps and then gets out of the way. Just how we like it.” - Achyuth Krishna, Director of Engineering of Whatfix","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://whatfix.com/blog/driving-the-future-now-were-excited-to-announce-our-90-million-series-d-funding/"}},{"start":287,"end":648,"type":"em"}]},{"type":"paragraph","text":"We understand that scaling while maintaining reliability of applications and profitability of business is important, so we provide robust solutions which minimize downtime.","spans":[]},{"type":"list-item","text":"Centra is a SaaS-based e-commerce platform for global direct-to-consumer and wholesale e-commerce brands. Centra provides a powerful e-commerce backend that lets brands build pixel-perfect, custom designed, online flagship stores. \n\n“How do we enable our customers to create differentiated online experiences? How do we ensure their e-commerce apps stay up and running at all times? How do we scale on-demand when traffic grows or new customers come in? These are the questions that we ask ourselves every day. Thankfully, we have a partner in DigitalOcean that provides just the platform to answer those questions enabling us to guarantee 99.9% uptime for our clients.” - Martin Jensen, CEO of Centra","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://centra.com/"}},{"start":233,"end":673,"type":"em"}]},{"type":"paragraph","text":"These are just a few examples of SaaS businesses finding success on DigitalOcean. We are constantly amazed by the creativity and innovation that software builders are utilizing our platform for. If you are interested in learning more about product updates, technical deep-dives and best practices for building SaaS products and businesses, please contact us to learn how we can help you get started. ","spans":[{"start":340,"end":357,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/migrate/?utmmedium=blog","target":"_blank"}}]},{"type":"paragraph","text":"Come build with DigitalOcean!","spans":[]},{"type":"paragraph","text":"Looking to migrate your SaaS to DigitalOcean? Leverage free infrastructure credits, robust training, and technical support to ensure a worry-free migration.","spans":[{"start":0,"end":156,"type":"strong"},{"start":0,"end":156,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/migrate/?utmmedium=blog","target":"_blank"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Raman Sharma","spans":[]},{"type":"paragraph","text":"Vice President, Product & Programs Marketing","spans":[]}],"blog_post_date":"2021-06-22","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"how-to-scale-your-saas-product-without-breaking-the-bank"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Shantanu Kedar","author_image":{"dimensions":{"width":96,"height":96},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/17e306fb-0754-418d-8606-e7b481c613c0_shantanu-kedar.png?auto=compress,format"},"_meta":{"uid":"shantanu_kedar"}},"blog_header_image":{"dimensions":{"width":790,"height":395},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/15d39f37-70c3-48bb-acaa-191ae1ce9e65_Blog_design-01.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Jump-start your startup with DigitalOcean App Platform","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Starting a business is hard, we get it. It wasn’t so long ago that DigitalOcean was there too. There’s a lot to tackle and decisions big and small often need to be made quickly. What cloud platform should I use to build the app? Will I be able to release features quickly? Will I be able to find product/ market fit? On top of that, entrepreneurs also have to worry about raising funds, recruiting the right talent, and business and legal processes. Though DigitalOcean can’t help with everything, we can definitely help you with one of your critical needs: building, deploying, and scaling your apps. ","spans":[{"start":90,"end":93,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.wired.com/2015/05/amazon-isnt-one-killing-cloud-computing/"}}]},{"type":"paragraph","text":"‘Simplicity in all we do’ is one of the core values at DigitalOcean and we strive to simplify cloud computing. We want you to spend more time building software that changes the world. That’s why we recently launched DigitalOcean App Platform, our modern, reimagined PaaS (Platform as a Service) offering that makes it easy to build, deploy, and scale apps. App Platform is a fully managed solution, meaning we handle provisioning and managing infrastructure, operating systems, databases, application runtimes, and other dependencies so that you can focus on your apps. ","spans":[{"start":216,"end":241,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/blog/introducing-digitalocean-app-platform-reimagining-paas-to-make-it-simpler-for-you-to-build-deploy-and-scale-apps/"}}]},{"type":"paragraph","text":"Let’s explore how App Platform can help you with your startup.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading2","text":"Marketing websites and landing pages","spans":[]},{"type":"paragraph","text":"No matter where you are in your startup journey, it’s very likely that you will need a marketing website. Such websites are often static sites, and are typically used to showcase your app and get users excited, among other purposes. You can deploy these sites using App Platform with just a few clicks. The site will be up and running in a few minutes and will be backed by CDN, HTTPS enabled, and protected from DDoS attacks, making it super-fast and secure. You can also add your custom domain to infuse your brand into the site and make it discoverable. The first 3 static sites are free, and every additional site will be charged just $3/month. ","spans":[{"start":130,"end":142,"type":"hyperlink","data":{"link_type":"Web","url":"https://scotch.io/bar-talk/5-reasons-static-sites-rock"}},{"start":468,"end":495,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/app-platform/how-to/manage-domains/"}},{"start":561,"end":590,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/pricing/#app-platform"}}]},{"type":"embed","oembed":{"type":"video","embed_url":"https://www.youtube.com/watch?v=3nK6rcAbuzo","title":"Deploy a Gatsby Static Site on App Platform","provider_name":"YouTube","thumbnail_url":"https://i.ytimg.com/vi/3nK6rcAbuzo/hqdefault.jpg","height":113,"width":200,"version":"1.0","author_name":"DigitalOcean","author_url":"https://www.youtube.com/user/DigitalOceanVideos","provider_url":"https://www.youtube.com/","cache_age":null,"thumbnail_width":480,"thumbnail_height":360,"html":"<iframe width=\"200\" height=\"113\" src=\"https://www.youtube.com/embed/3nK6rcAbuzo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>"}},{"type":"paragraph","text":"","spans":[]},{"type":"heading2","text":"Main app","spans":[]},{"type":"paragraph","text":"The power of App Platform isn’t limited to just static sites. In fact, you can build, deploy, and scale your entire app with App Platform. It supports many programming languages and frameworks out of the box, like Node.js, Python, Django, Go, PHP, Laravel, React, Ruby, Ruby on Rails, Gatsby, and Hugo. If your apps are built using other languages, simply provide the pre-built container image and App Platform will do the rest. ","spans":[{"start":142,"end":193,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/app-platform/languages-frameworks/"}}]},{"type":"paragraph","text":"App Platform integrates with GitHub and you can deploy code directly from your repositories. It also makes it easy to rapidly iterate on the app. Just enable ‘Autodeploy on Push’ and your app will be re-deployed automatically each time you push to the branch containing the source code. ","spans":[]},{"type":"paragraph","text":"App Platform utilizes a component-based design, making it easy to build apps with modular and loosely coupled components. For example, the image below shows the typical elements that make up any dynamic app. When you run your app, App Platform builds and deploys containers for each of your app’s components on Kubernetes clusters.","spans":[{"start":24,"end":46,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/blog/build-component-based-apps-with-digitalocean-app-platform"}}]},{"type":"image","url":"https://images.prismic.io/www-static/42453250-63ca-498a-8077-42b00eb5f543_Phil_blog3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":919,"height":448}},{"type":"paragraph","text":"The advantage of component-based design is that you can evolve the app based on your startup’s needs. This allows you to move at your own pace and there’s no need to go all in with App Platform right away. For example, check out the below video which shows how you can build a dynamic app with App Platform. The app has three components: a front end in React, a backend that consists of Laravel API, and a PostgreSQL database. You can deploy these components independent of each other and then attach them together to get the app working. ","spans":[]},{"type":"embed","oembed":{"type":"video","embed_url":"https://www.youtube.com/watch?v=qkg5Cufa-C8","title":"Deploy a Laravel API with React Front End on App Platform","provider_name":"YouTube","thumbnail_url":"https://i.ytimg.com/vi/qkg5Cufa-C8/hqdefault.jpg","height":113,"width":200,"version":"1.0","author_name":"DigitalOcean","author_url":"https://www.youtube.com/user/DigitalOceanVideos","provider_url":"https://www.youtube.com/","cache_age":null,"thumbnail_width":480,"thumbnail_height":360,"html":"<iframe width=\"200\" height=\"113\" src=\"https://www.youtube.com/embed/qkg5Cufa-C8?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>"}},{"type":"paragraph","text":"Another benefit of component-based design is that you can scale the components independent of each other. You get a lot of flexibility and can scale the components vertically and horizontally. This allows you to have precise control on your costs. The pricing for dynamic apps starts at $5/month and since we run App Platform on our own infrastructure, the costs are low not only when you are starting out, but also when you scale your apps. ","spans":[{"start":252,"end":296,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/pricing/#app-platform"}}]},{"type":"heading2","text":"Blog","spans":[]},{"type":"paragraph","text":"It’s very likely that you will also need a blog so that you can promote new product features, write thought leadership articles, and boost your SEO ratings. You can build and deploy your blog via App Platform. Just point us to the GitHub repo and let App Platform do all the heavy lifting. Deploy this sample app for a blog that uses Strapi as the backend and Next.js as the front end to see the App Platform in action.","spans":[{"start":334,"end":355,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/do-community/blog-strapi"}},{"start":360,"end":384,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/do-community/blog-next"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"heading2","text":"Internal apps","spans":[]},{"type":"paragraph","text":"In addition to customer facing apps and sites, App Platform can also help you with internal apps. For example, you can use App Platform to build and deploy an app that monitors your customer facing app’s uptime and performance. Other examples of internal apps include dashboards for web analytics and apps to perform operations like data deduplication. ","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"We hope you will try App Platform and build your startup on DigitalOcean. Check out the docs, deploy this sample app for an RSS feed aggregator (here are the repos for frontend and API), and let us know what you think!  ","spans":[{"start":38,"end":72,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/solutions/startups/"}},{"start":88,"end":92,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/app-platform/"}},{"start":106,"end":143,"type":"hyperlink","data":{"link_type":"Web","url":"https://rss-reader.shark.codes/"}},{"start":168,"end":176,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/do-community/rss-reader-frontend"}},{"start":181,"end":184,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/do-community/rss-reader-api"}},{"start":191,"end":217,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/questions/new?tags=Digitalocean%20App%20Platform"}}]},{"type":"paragraph","text":"If you’d like to have a conversation about using DigitalOcean and App Platform in your business, please feel free to contact our sales team.","spans":[{"start":117,"end":139,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/company/contact/sales/"}}]},{"type":"paragraph","text":"Happy coding!","spans":[]},{"type":"paragraph","text":"Shantanu Kedar","spans":[]},{"type":"paragraph","text":"Senior Manager, Product Marketing","spans":[]}],"blog_post_date":"2021-01-12","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"jump-start-your-startup-with-digitalocean-app-platform"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Kamal Nasser","author_image":{"dimensions":{"width":1008,"height":1008},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/e2285fcfaf32ce7ec26329fe7e416ae896fbf991_portrait_2k18_bw_smallres.jpg?auto=compress,format"},"_meta":{"uid":"kamal-nasser"}},"blog_header_image":{"dimensions":{"width":646,"height":220},"alt":"ssh img 1","copyright":null,"url":"https://images.prismic.io/www-static/aad6a8cd-1c72-4d04-a16b-e58af7a94fec_ssh-alias-1.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Have a lot of Droplets? Use do-ssh-alias for easier SSH access","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"If you have a lot of Droplets on your account, you probably agree that it's hard to keep track of all of them—especially if you create new ones and destroy them frequently for one-off workloads. A common pain point is having to look up a Droplet's IP address when needing to SSH into it.","spans":[]},{"type":"paragraph","text":"I created do-ssh-alias to help address that. Let's look at how it can help.","spans":[{"start":10,"end":22,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/kamaln7/do-ssh-alias"}}]},{"type":"heading2","text":"What is do-ssh-alias?","spans":[]},{"type":"paragraph","text":"Let's assume you have a Droplet named shiny-blog. Usually you would look up its IP address and then SSH into it like so:","spans":[]},{"type":"preformatted","text":"ssh username@1.2.3.4  \n","spans":[]},{"type":"paragraph","text":"What if, instead, you could simply run the following command?","spans":[]},{"type":"preformatted","text":"ssh shiny-blog  \n","spans":[]},{"type":"paragraph","text":"This is where do-ssh-alias comes in. It creates SSH aliases for all your Droplets at once so you can easily SSH in, without having to worry about what user or hostname to use.","spans":[]},{"type":"paragraph","text":"It is especially useful if your Droplets' hostnames are FQDNs (e.g. shiny.example.com) that don't point directly to the Droplets' IP addresses. One example is using Cloudflare in front of your website, so your domain name resolves to a Cloudflare server instead of your Droplet.","spans":[]},{"type":"heading2","text":"How do I use it?","spans":[]},{"type":"paragraph","text":"do-ssh-alias depends on the programs jq and doctl. The first step is installing jq and installing doctl. Linked are the installation instructions for each program. Once you install doctl, log it in to your DigitalOcean account.","spans":[{"start":37,"end":39,"type":"em"},{"start":44,"end":49,"type":"em"},{"start":69,"end":82,"type":"hyperlink","data":{"link_type":"Web","url":"https://stedolan.github.io/jq/download/"}},{"start":87,"end":103,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/digitalocean/doctl#installing-doctl"}},{"start":188,"end":226,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/digitalocean/doctl#authenticating-with-digitalocean"}}]},{"type":"paragraph","text":"With the dependencies taken care of, let's now install do-ssh-alias. You can either download the script from GitHub or use the command line:","spans":[{"start":93,"end":103,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/kamaln7/do-ssh-alias/blob/master/do-ssh-alias.sh"}}]},{"type":"preformatted","text":"wget https://do.co/do-ssh-alias  \n","spans":[]},{"type":"paragraph","text":"It's always a good idea to review any scripts you download from the internet before executing them.","spans":[]},{"type":"paragraph","text":"Once you have the file on your computer, update its permissions to allow it to be executed:","spans":[]},{"type":"preformatted","text":"chmod +x do-ssh-alias  \n","spans":[]},{"type":"paragraph","text":"It's now ready to use. To generate aliases for your Droplets, run:","spans":[]},{"type":"preformatted","text":"./do-ssh-alias > ~/.ssh/do_aliases\n","spans":[]},{"type":"paragraph","text":"This will run do-ssh-alias and save the results in the file ~/.ssh/do_aliases.","spans":[]},{"type":"paragraph","text":"Finally, update your ssh config to actually use the file with the aliases. Open ~/.ssh/config in a text editor and add the following line at the top:","spans":[]},{"type":"preformatted","text":"Include do_aliases  \n","spans":[]},{"type":"paragraph","text":"That's it. You can now SSH to your Droplets using their hostnames! Any time you create or remove Droplets, simply run it again to update the configuration.","spans":[]},{"type":"heading2","text":"What else can it do?","spans":[]},{"type":"paragraph","text":"do-ssh-alias only generates SSH aliases, but it accepts a few options for some flexibility:","spans":[]},{"type":"list-item","text":"-u: Pass your SSH username like -u sammy to automatically use it for all hosts.","spans":[]},{"type":"list-item","text":"-i: To ignore certain Droplets and not create aliases for them, pass their hostnames like -i ignored-hostname-1 -i ignored-hostname-2.","spans":[]},{"type":"list-item","text":"-s: Pass a suffix with the -s option to generate additional aliases with that suffix stripped. For example, if your Droplet's hostname is shiny.example.com passing -s .example.com will generate an alias for ssh shiny in addition to ssh shiny.example.com.","spans":[]},{"type":"paragraph","text":"Below is an example of using all three options.","spans":[]},{"type":"heading2","text":"Show me an example, please!","spans":[]},{"type":"paragraph","text":"Let's assume you have the following Droplets on your account:","spans":[]},{"type":"list-item","text":"droplet1","spans":[]},{"type":"list-item","text":"droplet2.domain.com","spans":[]},{"type":"list-item","text":"droplet3.domain.com","spans":[]},{"type":"paragraph","text":"Running:","spans":[]},{"type":"preformatted","text":"do-ssh-alias -u sammy -i droplet1 -s .domain.com  \n","spans":[]},{"type":"paragraph","text":"will generate aliases for:","spans":[]},{"type":"list-item","text":"ssh droplet2.domain.com","spans":[]},{"type":"list-item","text":"ssh droplet2","spans":[]},{"type":"list-item","text":"ssh droplet3.domain.com","spans":[]},{"type":"list-item","text":"ssh droplet3","spans":[]},{"type":"paragraph","text":"all using the username sammy to log in. The SSH config will look like so:","spans":[]},{"type":"preformatted","text":"Host droplet2.domain.com droplet2  \n    Hostname Droplet2-IP\n    User sammy\n\nHost droplet3.domain.com droplet3  \n    Hostname Droplet3-IP\n    User sammy\n","spans":[]},{"type":"heading2","text":"Resources","spans":[]},{"type":"paragraph","text":"Here are some resources you may find useful:","spans":[]},{"type":"list-item","text":"do-ssh-alias on GitHub","spans":[{"start":0,"end":22,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/kamaln7/do-ssh-alias"}}]},{"type":"list-item","text":"How To Configure Custom Connection Options for your SSH Client","spans":[{"start":0,"end":62,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/how-to-configure-custom-connection-options-for-your-ssh-client"}}]},{"type":"paragraph","text":"Note: doctl itself provides similar functionality through the doctl compute ssh command which allows you to SSH into a Droplet using its ID or name. The main difference is that doctl looks up the Droplet's IP address using the DigitalOcean API every time you run it, while do-ssh-alias generates a static config file that ssh reads. You might prefer do-ssh-alias if:","spans":[{"start":0,"end":5,"type":"strong"}]},{"type":"list-item","text":"you want an SSH config that can be copied to other computers without having to install doctl or store your API token on them; or","spans":[]},{"type":"list-item","text":"want to avoid the added latency of doctl's API request.","spans":[]}],"blog_post_date":"2020-05-07","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"use-do-ssh-alias-for-easier-droplet-ssh-access"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"André Bearfield","author_image":{"dimensions":{"width":553,"height":547},"alt":"André Bearfield","copyright":null,"url":"https://images.prismic.io/www-static/fdc7c85186f0a850b04083e1d4306bd1c19772e8_andre-bearfield.png?auto=compress,format"},"_meta":{"uid":"andre-bearfield"}},"blog_header_image":{"dimensions":{"width":784,"height":418},"alt":"Dolphin and cool sunglasses octopus illustration ","copyright":null,"url":"https://images.prismic.io/www-static/7c9f7b73a465dfe2513468f11776703999b00736_mysql_redis_blogheader_lockup.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Take the worry out of managing your MySQL & Redis databases","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Our mission at DigitalOcean is to simplify the cloud so you can focus more on building apps and less on managing the underlying infrastructure. To that end, we introduced Managed Databases for PostgreSQL earlier this year, which removes many of the hassles in maintaining PostgreSQL databases. Our team has been hard at work these past few months, and we are so excited to finally launch Managed Databases for MySQL and Redis! You can now spin up MySQL and Redis database clusters with just a few clicks, without having to worry about configuring, managing, scaling, updating, and securing your databases.","spans":[{"start":171,"end":203,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/announcing-managed-databases-for-postgresql/"}},{"start":410,"end":415,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/mysql"}},{"start":420,"end":425,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/redis"}}]},{"type":"paragraph","text":"Managed Databases for MySQL & Redis now available. (PostgreSQL support launched February 2019.)","spans":[{"start":0,"end":95,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/7dd6999b71da769c6f500aa56fe468107932ab43_3_engines--1-.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":859,"height":183}},{"type":"heading2","text":"Why you need Managed Databases","spans":[]},{"type":"paragraph","text":"If you are building a modern app or website, it’s very likely you will need a database. Databases are one of the most critical components of an application. They should provide terabytes of storage, be able to process thousands of I/O operations per second, and allow data access with minimum latency. If your app usage grows, the database needs to scale easily and quickly to support millions of users.","spans":[]},{"type":"paragraph","text":"Relational databases such as MySQL and PostgreSQL are widely used in the market. Typical use cases include traditional CRUD websites that need persistent storage and an ability to quickly retrieve data from the database.","spans":[{"start":0,"end":20,"type":"hyperlink","data":{"link_type":"Web","url":"https://en.wikipedia.org/wiki/Relational_database"}},{"start":29,"end":34,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.mysql.com/"}},{"start":39,"end":49,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.postgresql.org/"}}]},{"type":"paragraph","text":"Redis has gained a lot of momentum in the last few years as an open source, in-memory data structure store, used as a database, cache, and message broker. Typical use cases include apps with real-time analytics, high-speed transactions, and machine learning.","spans":[{"start":0,"end":5,"type":"hyperlink","data":{"link_type":"Web","url":"https://redis.io/"}}]},{"type":"paragraph","text":"Whether you are using MySQL, Redis, or PostgreSQL, building and managing database clusters from the ground up is a herculean task. Developers often spend valuable time and resources on database management, which prevents them from focusing on building and enhancing apps.","spans":[]},{"type":"paragraph","text":"We introduced Managed Databases to simplify the lives of developers by addressing these common challenges:","spans":[]},{"type":"list-item","text":"Determining the optimal infrastructure needed to host your databases is time-intensive","spans":[]},{"type":"list-item","text":"Scaling the infrastructure that supports your database is often a slow and expensive task","spans":[]},{"type":"list-item","text":"Implementing reliable failover processes is difficult","spans":[]},{"type":"list-item","text":"Over-provisioning of underlying infrastructure leads to increased costs","spans":[]},{"type":"list-item","text":"Setting up a complete and reliable backup and recovery process requires a lot of effort","spans":[]},{"type":"list-item","text":"Maintaining and updating databases often needs dedicated personnel","spans":[]},{"type":"heading3","text":"How Managed Databases work","spans":[]},{"type":"paragraph","text":"We're proud to extend the simplicity that DigitalOcean is known for to Managed Databases. Developers of all skill levels, even those with no prior experience in databases, can spin up database clusters with just a few clicks. Select the database engine, storage, vCPU, memory, and standby nodes and we take care of the rest. The following database engines are currently supported:","spans":[]},{"type":"list-item","text":"MySQL (version 8)NEW","spans":[]},{"type":"list-item","text":"Redis (version 5)NEW","spans":[]},{"type":"list-item","text":"PostgreSQL (version 10 and 11)","spans":[]},{"type":"paragraph","text":"Managed Databases are built on top of our core compute platform and use local SSD storage, which makes them lightning fast. In addition to a simple dashboard, you can manage your database clusters programmatically with the DigitalOcean API.","spans":[{"start":223,"end":239,"type":"hyperlink","data":{"link_type":"Web","url":"https://developers.digitalocean.com/documentation/v2/#databases"}}]},{"type":"image","url":"https://images.prismic.io/www-static/12a097782406d270de32d27f5236859e5d6a3dd0_swatch?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":100,"height":54}},{"type":"heading2","text":"Simple, predictable pricing","spans":[]},{"type":"paragraph","text":"Just like all DigitalOcean products, Managed Databases provide simple, predictable pricing that allows you to control costs and prevent any surprise bills. You can spin up a database cluster for just $15/month, or a high-availability cluster with a standby node for $50/ month. Pricing is the same for MySQL, PostgreSQL, and Redis engines. Backups are free and included as part of the service. Ingress bandwidth is always free, and egress fees ($0.01/GB per month) will be waived for 2019.","spans":[{"start":63,"end":90,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/pricing/#anchor--Databases"}}]},{"type":"heading2","text":"Benefits of Managed Databases","spans":[]},{"type":"paragraph","text":"Worry-free setup & maintenance: Save time by launching a database cluster with just a few clicks. Never worry again about security patches to the OS or database engine – once a new version or patch is available, simply click a button to enable it.","spans":[{"start":0,"end":30,"type":"strong"}]},{"type":"paragraph","text":"High scalability to support your growth: You can scale up at any time with no impact to your application. You have flexibility, so you can spin up read-only nodes to scale read operations or remove compute overhead from reporting requirements. This also keeps expenses in check as you reduce overprovisioning of infrastructure.","spans":[{"start":0,"end":39,"type":"strong"}]},{"type":"paragraph","text":"Free daily backups with point-in-time recovery: We automatically back up your databases every day. If things go wrong, you can easily restore data to any point within the past seven days.","spans":[{"start":0,"end":46,"type":"strong"}]},{"type":"paragraph","text":"Automated failover to maximize availability: In the event of a failure, Managed Databases will automatically fail over to a standby node and minimize downtime for your customers.","spans":[{"start":0,"end":43,"type":"strong"}]},{"type":"paragraph","text":"End-to-end security: Databases run in your account’s private network, which isolates communication at the account or team level. You can restrict requests to your database from the public internet by whitelisting specific inbound sources. Data is encrypted when at rest and in transit to prevent cyberattacks.","spans":[{"start":0,"end":19,"type":"strong"}]},{"type":"heading2","text":"Regional availability","spans":[]},{"type":"paragraph","text":"There is a huge demand for Managed MySQL and Redis among customers. In order to provide the best user experience, we plan to do a phased roll out of these engines. The table below provides tentative timeline for the data center availability. Please refer to our release notes for the most up-to-date information. \nAugust 20August 27September 4NYC1AMS3SGP1FRA1LON1BLR1SFO2NYC3TOR1\n## What’s next","spans":[{"start":258,"end":275,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/platform/release-notes/"}}]},{"type":"paragraph","text":"We hope that you are excited about this release and will give the service a try. Managed Databases for MySQL and Redis are currently in Limited Availability (LA) and will move to General Availability (GA) in a few weeks. Managed Redis will include database-level metrics to monitor performance, usage, and errors after it moves to GA.","spans":[]},{"type":"paragraph","text":"Ready to create a database? Try Managed Databases now.","spans":[{"start":0,"end":54,"type":"hyperlink","data":{"link_type":"Web","url":"http://cloud.digitalocean.com/databases"}}]},{"type":"paragraph","text":"If you’d like to have a conversation about using DigitalOcean and Managed Databases in your business, please feel free to contact our sales team.","spans":[{"start":122,"end":144,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/company/contact/sales/"}}]}],"blog_post_date":"2019-08-20","tags":[{"tag1":{"tag":"Product Updates","_linkType":"Link.document","_meta":{"uid":"product-updates"}}},{"tag1":{"tag":"News","_linkType":"Link.document","_meta":{"uid":"news"}}},{"tag1":{"tag":"Engineering","_linkType":"Link.document","_meta":{"uid":"engineering"}}},{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"take-the-worry-out-of-managing-your-mysql-redis-databases"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Al Sene","author_image":null,"_meta":{"uid":"al_sene"}},"blog_header_image":null,"blog_headline":[{"type":"heading1","text":"Currents","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"As the VP of Engineering at DigitalOcean, I work with developers around the world. We’re a company that prides itself on allowing employees to work wherever they want — from different regions, to different personal setups in their homes and more. And it’s no secret that this trend extends beyond our walls (or lack of walls!). Across the industry, workplace flexibility is undoubtedly on the rise.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"With this in mind, DigitalOcean’s sixth and latest installment of our Currents research series explores everything related to remote work within the developer community. With insights from more than 4,500 developers, the report provides an inside look into how developers work (whether remote or in office), their experiences working remotely, how they stay connected to the community, and how they maintain a healthy work-life balance.","spans":[{"start":70,"end":87,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/currents/july-2019/"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Key findings include:","spans":[]},{"type":"list-item","text":"Remote work is the new normal for developers – not just something they prefer, but something they increasingly demand from employers. Eighty-six percent of respondents currently work remotely in some capacity, with nearly 1/3 working from home full time. Forty-three percent say the ability to work remotely is a must-have when considering an offer with a company.","spans":[]},{"type":"list-item","text":"Remote workers are connected. The traditional narrative of remote workers as isolated and disengaged from their companies is proving false for many. Seventy-one percent of developers who work remotely said they feel connected to their company’s community.","spans":[]},{"type":"list-item","text":"But the issue hasn’t disappeared entirely. The 29 percent who don’t feel connected say they feel excluded from offline team conversations or don’t feel integrated into their company’s culture when working remotely.","spans":[]},{"type":"list-item","text":"The burnout problem is real. Two-thirds of all respondents said their stress levels have caused them to feel burnt out or work fatigued, regardless of whether or not they work remotely.","spans":[]},{"type":"list-item","text":"Developers expect remote work to improve work-life balance. But the reality doesn’t always line up with that hope. Seventy-six percent of respondents said they think working remotely improves work-life balance, yet many remote workers reported working longer hours and ultimately rated their work-life balance as only slightly higher than in-office workers.","spans":[]},{"type":"list-item","text":"Remote work has started to boom in the past four years — and it’s reshaping developers’ perceptions.","spans":[]},{"type":"list-item","text":"On a scale of 1–5, how important a factor was a flexible work schedule in your career decision?\n\nOver the past four years, 82 percent of developers have started working remotely in some fashion, from being remote full time to splitting their time in and out of an office, to isolated circumstances. Today, it is critical that companies offer remote work flexibility, or they risk seeming behind the times. In fact, over half of respondents noted that they think less highly of a company that doesn’t offer remote work options.","spans":[]},{"type":"heading3","text":"Despite the rise of remote workers, connections to the community are still strong","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Seventy-one percent of respondents feel connected to their company’s community while working remotely. This is primarily due to advances in workplace communication tools, as there is an increasing number of channels for employees to engage through regularly. On a scale of one to five, developers most prefer using business collaboration software and least prefer video conferencing.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"In addition to their colleagues, the broader developer community connects with each other through local events or meetups, online forums, and conferences — in the last 12 months, 77 percent have attended an industry event.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading4","text":"How do you stay in touch with other developers and the dev community at large?","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/bdf0bdca-e814-4ba8-bfff-100dbc69f713_II.Remote-Work-and-Isolation_3-1.svg?auto=compress,format","alt":"remote worker graph on industry event preferences","copyright":null,"dimensions":{"width":183,"height":150}},{"type":"heading4","text":"Companies must continue to support remote workers","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Despite most developers feeling connected, issues of isolation still exist for nearly a third of remote workers, so it is important that companies proactively connect and include remote employees. Forty percent of respondents indicated that their employers don’t have specific programs or policies in place to include remote workers.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading4","text":"Does your company have specific programs or policies to ensure remote employees feel included?","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/cd711b8c-a86a-4cca-83a1-fe17dcecf0dc_II.Remote-Work-and-Isolation_7.svg?auto=compress,format","alt":"remote worker graph on feelings of isolation","copyright":null,"dimensions":{"width":175,"height":150}},{"type":"paragraph","text":"Of these respondents who were aware that their company had a remote-employee programs, 88 percent said they have a positive impact. This disparity highlights a valuable opportunity for companies to invest in connecting their employees.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading4","text":"Burnout exists across the developer community","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Globally, burnout is present in the developer community with 66 percent of respondents indicating they’ve experienced work fatigue — whether they work remotely or not. This number is even higher in the U.S., at 82 percent of respondents.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading4","text":"Reported burnout by region","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/b9309f61-8624-4e0c-96f6-1920baa54c3b_III.Work-from-Home--Wellness_2.svg?auto=compress,format","alt":"remote worker burn out graph - The highest percentage being US then UK, CA, and IN. ","copyright":null,"dimensions":{"width":164,"height":150}},{"type":"paragraph","text":"Remote work is seen as one solution to this stress. The majority of developers who work from home (76 percent) believe that it helps them improve their work-life balance.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Developers — and their managers, co-workers alike — need to be aware of the signs of burnout and take advantage of employer-sponsored stress-reduction and mental health programs. Remote workers in particular need to disconnect and create separation between their personal and work environments.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"This includes finding time to work out, foster their social circles, take vacations, and cultivate hobbies. Our survey found that outside of work, developers turn to family and friends, physical activity, video games, reading, or playing music to de-stress. Many companies are also now offering longer paid leaves to support people during significant life transitions and milestones.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading3","text":"Remote work (mostly) positively impacts work-life balance","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading4","text":"Reasons people think remote work improves work-life balance","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/e9f67e74-1cd1-47ff-92b5-29dc34d86183_III.Work-from-Home--Wellness_1.svg?auto=compress,format","alt":"remote workers reasons to be remote chart","copyright":null,"dimensions":{"width":165,"height":150}},{"type":"paragraph","text":"A majority of respondents believe remote work helps improve work-life balance, as it eliminates the stress of commuting and gives people the flexibility to work from wherever they’d like to live. A quarter of respondents reported remote work had no impact on work-life balance. The remaining 11 percent said remote work worsens work-life balance, most frequently citing the issues of working longer hours at home and feeling expectations to contribute more. Remote work expectations could be an area for improvement, as companies make efforts to implement remote-work policies and programs.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"heading3","text":"About Currents","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"DigitalOcean Currents highlights the latest trends among developers in the cloud.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"If you would like to be among the first to receive Currents each quarter, sign up here. You’ll also be able to share ideas on what topics we should cover and participate in our next survey.","spans":[{"start":74,"end":86,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.google.com/forms/d/e/1FAIpQLScwem9s1c7oKRbRF_ZKcTdxQUQlns52L2CgltSyHn1UJOpvkw/viewform"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"Read more about these and other findings in the full Currents report.","spans":[{"start":53,"end":68,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/currents/july-2019/"}}]},{"type":"paragraph","text":"","spans":[]},{"type":"paragraph","text":"","spans":[]}],"blog_post_date":"2019-07-17","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"currents-july2019"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Community Team","author_image":null,"_meta":{"uid":"community_team"}},"blog_header_image":{"dimensions":{"width":1024,"height":512},"alt":"Machine Learning book illustration","copyright":null,"url":"https://images.prismic.io/www-static/f19477fbeea318fdb15d057e9ccc8ee570ae2da3_machine-learning-book.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Celebrate PyCon 2019 With Our Free Python Machine Learning Projects eBook","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"To commemorate the 2019 PyCon conference and the worldwide Python community, we have put together a free eBook of Python Machine Learning Projects!","spans":[{"start":114,"end":146,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/py-ml-book"}}]},{"type":"paragraph","text":"Project-based learning offers the opportunity to gain hands-on experience by digging into complex, real-world challenges. You can download this book and read it offline, allowing you to work at your own pace as you go through machine learning Python projects. If you are a teacher or workshop leader, you may also use this resource with students or community members.","spans":[]},{"type":"paragraph","text":"The book is Creative Commons licensed, so feel free to redistribute and remix the tutorials (with attribution) for your noncommercial educational needs!","spans":[{"start":12,"end":37,"type":"hyperlink","data":{"link_type":"Web","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"}}]},{"type":"paragraph","text":"You can download the book in the following formats:","spans":[]},{"type":"list-item","text":"ePub","spans":[{"start":0,"end":4,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/py-ml-book-epub"}}]},{"type":"list-item","text":"PDF","spans":[{"start":0,"end":3,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/py-ml-book-pdf"}}]},{"type":"list-item","text":"Mobi (compatible with Kindle).","spans":[{"start":0,"end":4,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/py-ml-book-mobi"}}]},{"type":"heading2","text":"Why Machine Learning?","spans":[]},{"type":"paragraph","text":"Machine learning is increasingly being used to find patterns, conduct analysis, and make decisions – sometimes without final input from humans who may be impacted by these findings. We created this book to equip developers with tools they can use to better understand, evaluate, and shape machine learning, in order to help ensure that it serves everyone fairly.","spans":[]},{"type":"paragraph","text":"This book will set you up with a Python programming environment if you don’t have one already, then provide you with a conceptual understanding of machine learning. It includes three Python machine learning tutorials that will help you create a machine learning classifier, build a neural network to recognize handwritten digits, and give you a background in deep reinforcement learning through building a bot for Atari.","spans":[]},{"type":"paragraph","text":"If you need Python support or would like reference material, check out our free How To Code in Python 3 eBook!","spans":[{"start":80,"end":103,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/digitalocean-ebook-how-to-code-in-python"}}]},{"type":"heading2","text":"By the Community for the Community 🐍","spans":[]},{"type":"paragraph","text":"These chapters originally appeared as articles on DigitalOcean's Community site, written and edited by members of the international software developer community. If you are interested in contributing to this knowledge base, consider participating in our Write for DOnations program. DigitalOcean offers payment to authors and provides a matching donation to tech-focused nonprofits.","spans":[{"start":50,"end":79,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community"}},{"start":254,"end":281,"type":"hyperlink","data":{"link_type":"Web","url":"https://do.co/w4do"}}]},{"type":"paragraph","text":"This eBook was put together by members of the DigitalOcean Developer Education team. To learn more about our eBook creation process,  read the blog post we wrote announcing our How To Code in Python 3 eBook.","spans":[{"start":143,"end":206,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/how-to-code-in-python-ebook/"}}]},{"type":"heading2","text":"Find Us at PyCon","spans":[]},{"type":"paragraph","text":"This year we are happy to be sponsoring PyCon 2019 Sprints, which offer developers the opportunity to collaborate in person on open source projects. Members of the DigitalOcean Community team will be at the conference, so if you are in Cleveland come find us for some great Sammy swag! We also proudly support the Python Software Foundation as a Bronze Sponsor.","spans":[{"start":40,"end":58,"type":"hyperlink","data":{"link_type":"Web","url":"https://us.pycon.org/2019/community/sprints/"}},{"start":314,"end":340,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.python.org/psf/"}}]}],"blog_post_date":"2019-05-03","tags":[{"tag1":{"tag":"Community","_linkType":"Link.document","_meta":{"uid":"community"}}},{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}},{"tag1":{"tag":"Engineering","_linkType":"Link.document","_meta":{"uid":"engineering"}}}],"_meta":{"uid":"gear-up-for-pycon-2019-with-digitaloceans-free-python-machine-learning-projects-ebook"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Kamal Nasser","author_image":{"dimensions":{"width":1008,"height":1008},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/e2285fcfaf32ce7ec26329fe7e416ae896fbf991_portrait_2k18_bw_smallres.jpg?auto=compress,format"},"_meta":{"uid":"kamal-nasser"}},"blog_header_image":{"dimensions":{"width":1200,"height":900},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/c4446d83-1e27-4c4e-822c-5ca308967e52_database-mostov_dribbble.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Creating a Simple Contacts List with Go and PostgreSQL","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"In this post, we will build a simple web page containing a contacts list, with the contacts fetched from a PostgreSQL database. We will connect to the database in Go and use PostgreSQL's support for JSON columns. This is what the result will look like:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/76f9193e-d599-4c79-b1d0-015d71bce6fb_Kamal-1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":1396}},{"type":"paragraph","text":"By following this post, you will learn how to connect to a PostgreSQL database in Go using the sqlx and pgx packages, render data dynamically using a template, and serve the resulting page on an HTTP server.","spans":[]},{"type":"heading1","text":"Requirements","spans":[]},{"type":"paragraph","text":"Before we get started:","spans":[]},{"type":"o-list-item","text":"Make sure you have Go installed. See this post for instructions.  ","spans":[{"start":37,"end":46,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.callicoder.com/golang-installation-setup-gopath-workspace/"}}]},{"type":"o-list-item","text":"Make sure you know where your GOPATH is. It's usually ~/go unless set differently.","spans":[]},{"type":"heading1","text":"Getting an HTTP Server Up","spans":[]},{"type":"paragraph","text":"In a new empty directory inside your $GOPATH, create a file named main.go. You can name the directory anything you like: I went with go-contacts. We'll start with setting up the HTTP server using Go's built-in net/http package.","spans":[]},{"type":"preformatted","text":"    package main\n    import (\n        \"flag\"\n        \"log\"\n        \"net/http\"\n        \"os\"\n    )\n    var (\n        listenAddr = flag.String(\"addr\", getenvWithDefault(\"LISTENADDR\", \":8080\"), \"HTTP address to listen on\")\n    )\n    func getenvWithDefault(name, defaultValue string) string {\n            val := os.Getenv(name)\n            if val == \"\" {\n                    val = defaultValue\n            }\n            return val\n    }\n    func main() {\n        flag.Parse()\n        log.Printf(\"listening on %s\\n\", *listenAddr)\n        http.ListenAndServe(*listenAddr, nil)\n    }","spans":[]},{"type":"paragraph","text":"The server will want a host and a port to listen on, so we ask for that in a CLI flag named addr. We also want to offer the option to pass in the setting in an environment variable, so the default value for the flag will be taken from the LISTENADDR environment variable. This means that if the CLI flag is passed, the value of the environment variable will be used. If neither are set, we'll fall back to port 8080.","spans":[]},{"type":"paragraph","text":"If you save the file and run it now, you should be able to browse to http://localhost:8080.","spans":[{"start":69,"end":90,"type":"hyperlink","data":{"link_type":"Web","url":"http://localhost:8080"}}]},{"type":"preformatted","text":"go run main.go","spans":[]},{"type":"paragraph","text":"and see—hold on, is that a \"404 page not found\" error?!","spans":[]},{"type":"paragraph","text":"That's fine! It's because we haven't configured any routes or pages yet, so the server doesn't know how to respond to the request. Why don't we go ahead and do that now.","spans":[]},{"type":"heading2","text":"Contacts List Page","spans":[]},{"type":"paragraph","text":"Let's create the contacts list page and serve it on the root path, /. We'll use the template/html package so that we can easily pass in dynamic data (the contacts) to be rendered in the page later.","spans":[]},{"type":"paragraph","text":"Create a directory named templates alongside main.go and within it a file named index.html with the following content:","spans":[]},{"type":"preformatted","text":"    <!doctype html>\n    <html>\n        <head>\n            <meta charset=\"utf-8\">\n            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n            <title>Contacts</title>\n            <link rel=\"stylesheet\" href=\"https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css\"/>\n        </head>\n        <body>\n            <div class=\"mw6 center pa3 sans-serif\">\n                <h1 class=\"mb4\">Contacts</h1>\n            </div>\n        </body>\n    </html>","spans":[]},{"type":"paragraph","text":"This is a page with basic styling that will serve as the base for our contacts list.","spans":[]},{"type":"paragraph","text":"Now we need to read the index.html template in our program. Import html/template and add a global variable to hold the templates right after listenAddr at the top:","spans":[]},{"type":"preformatted","text":"    import (\n        \"flag\"\n        \"log\"\n        \"html/template\"\n        \"net/http\"\n    )\n    var (\n            listenAddr       = flag.String(\"addr\", getenvWithDefault(\"LISTENADDR\", \":8080\"), \"HTTP address to listen on\")\n            tmpl             = template.New(\"\")\n    )","spans":[]},{"type":"paragraph","text":"Inside main(), after the flag.Parse() line, add the following. For compatibility with all operating systems, import the path/filepath package as we will use to construct the path to the template files.","spans":[]},{"type":"preformatted","text":"    var err error\n    _, err = tmpl.ParseGlob(filepath.Join(\".\", \"templates\", \"*.html\"))\n    if err != nil {\n        log.Fatalf(\"Unable to parse templates: %v\\n\", err)\n    }","spans":[]},{"type":"paragraph","text":"This will read every HTML file in the templates directory and prepare it for rendering. Now that we've done that, we want to configure the template to be rendered on /. Add a new function at the very bottom of the file to serve the page:","spans":[]},{"type":"preformatted","text":"    func handler(w http.ResponseWriter, r *http.Request) {\n        tmpl.ExecuteTemplate(w, \"index.html\", nil)\n    }","spans":[]},{"type":"paragraph","text":"Finally, configure the server to use this handler function. Above the log.Printf() line in main(), add:","spans":[]},{"type":"preformatted","text":"http.HandleFunc(\"/\", handler)","spans":[]},{"type":"paragraph","text":"Now we're ready! The whole file should look like this:","spans":[]},{"type":"preformatted","text":"    package main\n\n    import (\n        \"flag\"\n        \"log\"\n        \"html/template\"\n        \"net/http\"\n    )\n    var (\n        listenAddr = flag.String(\"addr\", getenvWithDefault(\"LISTENADDR\", \":8080\"), \"HTTP address to listen on\")\n        tmpl       = template.New(\"\")\n    )\n    \n    func getenvWithDefault(name, defaultValue string) string {\n            val := os.Getenv(name)\n            if val == \"\" {\n                    val = defaultValue\n            }\n            return val\n    }\n\n    func main() {\n        flag.Parse()\n        var err error\n        _, err = tmpl.ParseGlob(filepath.Join(\".\", \"templates\", \"*.html\"))\n        if err != nil {\n            log.Fatalf(\"Unable to parse templates: %v\\n\", err)\n        }\n        http.HandleFunc(\"/\", handler)\n        log.Printf(\"listening on %s\\n\", *listenAddr)\n        http.ListenAndServe(*listenAddr, nil)\n    }\n    func handler(w http.ResponseWriter, r *http.Request) {\n        tmpl.ExecuteTemplate(w, \"index.html\", nil)\n    }","spans":[]},{"type":"paragraph","text":"Run go run main.go again and you should see the template we've configured.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/4d14d033-b87d-4e16-ad5c-84cad8b14f7a_Kamal-2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":994}},{"type":"heading1","text":"Contacts in a Database","spans":[]},{"type":"paragraph","text":"Something is missing in the page—the actual contacts! Let's add them in.","spans":[]},{"type":"paragraph","text":"We will use DigitalOcean Databases to quickly get a PostgreSQL cluster up. If you haven’t yet, create a new one—it only takes a few minutes: if you prefer a text post, see the product documentation for Databases. If you prefer a video, click here.","spans":[{"start":172,"end":211,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/databases/how-to/clusters/create/"}},{"start":236,"end":246,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.youtube.com/watch?v=jY5FhyiEdig"}}]},{"type":"image","url":"https://images.prismic.io/www-static/a010b376-74b2-4837-892f-db1d7d8da99e_Kamal-3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":427}},{"type":"paragraph","text":"Once you've created the cluster, copy its Connection String from the control panel. In the Connection Details section in the Overview page, choose \"Connection string\" from the list and copy it:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/2c01727f-4ff8-4f5c-ada2-80ff7f171168_Kamal-4.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":990,"height":540}},{"type":"paragraph","text":"The connection string contains all the details necessary to connect to your database (including your password) so be sure to keep it safe.","spans":[]},{"type":"heading2","text":"Initializing the Database","spans":[]},{"type":"paragraph","text":"Our Go app will only handle displaying the contacts, so I have prepared an SQL export containing 10 randomly generated contacts that you can import into your database. You can find it here.","spans":[{"start":184,"end":188,"type":"hyperlink","data":{"link_type":"Web","url":"https://raw.githubusercontent.com/digitalocean/databases/master/examples/contacts.sql"}}]},{"type":"paragraph","text":"On macOS, I like to use TablePlus to work with my databases, but you can use any client you prefer or import it using the ```[php]{`psql`}``` CLI command like so:","spans":[]},{"type":"preformatted","text":"psql 'your connection string here' < contacts.sql","spans":[]},{"type":"heading2","text":"Fetching the Contacts","spans":[]},{"type":"paragraph","text":"Ok, so now we have a database with some contacts in it 🎉 Let's have our program connect to it and fetch the contacts. We'll build this functionality step by step.","spans":[]},{"type":"paragraph","text":"There are many ways to connect to a PostgreSQL database in Go. In this case, we also need a convenient way to access JSONB fields since our contacts database uses them. I personally found the combination of `github.com/jmoiron/sqlx` and `github.com/jackc/pgx` to work best.","spans":[{"start":207,"end":232,"type":"hyperlink","data":{"link_type":"Web","url":"http://github.com/jmoiron/sqlx"}},{"start":237,"end":259,"type":"hyperlink","data":{"link_type":"Web","url":"http://github.com/jackc/pgx"}}]},{"type":"paragraph","text":"Start by importing the packages:","spans":[]},{"type":"preformatted","text":"go get -u -v github.com/jackc/pgx github.com/jmoiron/sqlx","spans":[]},{"type":"paragraph","text":"And adding them at the top of main.go:","spans":[]},{"type":"preformatted","text":"    import (\n        ...\n\n        _ \"github.com/jackc/pgx/stdlib\"\n        \"github.com/jmoiron/sqlx\"\n        \"github.com/jmoiron/sqlx/types\"\n    )","spans":[]},{"type":"paragraph","text":"Now, there are a few things that we need to do. We need to define the Contact type based on the database's table structure and connect to our PostgreSQL database. When serving the contacts page, we will query the database for the contacts and pass them to the template for rendering.","spans":[]},{"type":"heading3","text":"Contact Type","spans":[]},{"type":"paragraph","text":"Add these types to main.go. They match the structure of the contacts database export and prepare support for the JSONB field favorites:  ","spans":[{"start":56,"end":84,"type":"hyperlink","data":{"link_type":"Web","url":"https://raw.githubusercontent.com/digitalocean/databases/master/examples/contacts.sql"}}]},{"type":"preformatted","text":"    // ContactFavorites is a field that contains a contact's favorites\n    type ContactFavorites struct {  \n        Colors []string `json:\"colors\"`\n    }\n    // Contact represents a Contact model in the database \n    type Contact struct {  \n        ID                   int\n        Name, Address, Phone string\n        FavoritesJSON types.JSONText    `db:\"favorites\"`\n        Favorites     *ContactFavorites `db:\"-\"`\n        CreatedAt string `db:\"created_at\"`\n        UpdatedAt string `db:\"updated_at\"`\n    }","spans":[]},{"type":"heading3","text":"Database Connection","spans":[]},{"type":"paragraph","text":"Note that we haven't connected to the database yet 👀 Let's do that now. We'll pass in the PostgreSQL connection string as a CLI flag and add a global database variable. So again at the top of main.go:","spans":[]},{"type":"preformatted","text":"    var (\n        connectionString = flag.String(\"conn\", getenvWithDefault(\"DATABASE_URL\", \"\"), \"PostgreSQL connection string\")\n        listenAddr       = flag.String(\"addr\", \":8080\", \"HTTP address to listen on\")\n        db               *sqlx.DB\n        tmpl             = template.New(\"\")\n    )","spans":[]},{"type":"paragraph","text":"Note that we use the function getenvWithDefault like with the listen address to allow the connection string to be passed using an environment variable (DATABASE_URL) in addition to the CLI flag (-conn).","spans":[]},{"type":"paragraph","text":"After the templating logic in main()(right above http.HandleFunc()), add the following:","spans":[]},{"type":"preformatted","text":"    if *connectionString == \"\" {\n        log.Fatalln(\"Please pass the connection string using the -conn option\")\n    }\n    \n    db, err = sqlx.Connect(\"pgx\", *connectionString)\n    if err != nil {\n        log.Fatalf(\"Unable to establish connection: %v\\n\", err)\n    }","spans":[]},{"type":"paragraph","text":"We're now connected to our PostgreSQL database!","spans":[]},{"type":"heading3","text":"Querying the Database for Contacts","spans":[]},{"type":"paragraph","text":"Add a new function to the bottom of the file to fetch all contacts from the database. For clearer errors, we'll make use of another package: github.com/pkg/errors. Download it and import it at the top of main.go as usual.","spans":[]},{"type":"preformatted","text":"    go get -u -v github.com/pkg/errors\n    \n    import (\n        ...\n        \"github.com/pkg/errors\"\n        ...\n    )\n    \n    …\n    \n    func fetchContacts() ([]*Contact, error) {\n        contacts := []*Contact{}\n        err := db.Select(&contacts, \"select * from contacts\")\n        if err != nil {\n            return nil, errors.Wrap(err, \"Unable to fetch contacts\")\n        }\n    \n        return contacts, nil\n    }","spans":[]},{"type":"paragraph","text":"One thing that's missing right now is the favorites column. If you look at the Contact type, we've defined this field: FavoritesJSON types.JSONText db:\"favorites\". This maps the favorites column in the database to the FavoritesJSON field in the Contact struct, making it available as a JSON object serialized as text.","spans":[]},{"type":"paragraph","text":"This means that we need to manually parse and un-marshal the JSON objects into actual Go structs. We will use Go’s encoding/json package so make sure to import it at the top of main.go. Adding onto fetchContacts():","spans":[]},{"type":"preformatted","text":"    import (\n        ...\n        \"encoding/json\"\n        ...\n    )\n    ...\n    func fetchContacts() ([]*Contact, error) {\n        ...\n    \n        for _, contact := range contacts {\n            err := json.Unmarshal(contact.FavoritesJSON, &contact.Favorites)\n    \n            if err != nil {\n                return nil, errors.Wrap(err, \"Unable to parse JSON favorites\")\n            }\n        }\n    \n        return contacts, nil\n    }","spans":[]},{"type":"paragraph","text":"The resulting structs will be stored in the Favorites field in the Contact struct.","spans":[]},{"type":"heading2","text":"Rendering the Contacts","spans":[]},{"type":"paragraph","text":"Cool, we have data. Let's use it! Inside the handler() function, we'll use fetchContacts() to get the contacts and then pass them to the template:","spans":[]},{"type":"preformatted","text":"    func handler(w http.ResponseWriter, r *http.Request) {\n        contacts, err := fetchContacts()\n        if err != nil {\n            w.WriteHeader(http.StatusInternalServerError)\n            w.Write([]byte(err.Error()))\n            return\n        }\n    \n        tmpl.ExecuteTemplate(w, \"index.html\", struct{ Contacts []*Contact }{contacts})\n    }","spans":[]},{"type":"paragraph","text":"This will attempt to fetch the contacts, display an error on failure, and pass them to the template. Note that if an error occurs, the full error will be sent as the response. In a production environment you will want to log the error and send a generic error message instead.","spans":[]},{"type":"paragraph","text":"Now we need to modify the template to do something with the contacts we are passing to it. To display favorite colors as a comma-separated list, we'll use the strings.Join function. Before we are able to use it inside the template, we need to define it as a template function, inside main() above the tmpl.ParseGlob line. Don’t forget to import the strings package at the top:","spans":[]},{"type":"preformatted","text":"    import (\n        ...\n        \"strings\"\n        ...\n    )\n    \n    …\n    \n    tmpl.Funcs(template.FuncMap{\"StringsJoin\": strings.Join})\n    _, err = tmpl.ParseGlob(filepath.Join(\".\", \"templates\", \"*.html\"))\n    \n    ...","spans":[]},{"type":"paragraph","text":"Then, under the <h1> line in the HTML template, add the following:","spans":[]},{"type":"preformatted","text":"    {{range .Contacts}}\n    <div class=\"pa2 mb3 striped--near-white\">\n        <header class=\"b mb2\">{{.Name}}</header>\n        <div class=\"pl2\">\n            <p class=\"mb2\">{{.Phone }}</p>\n            <p class=\"pre mb3\">{{.Address}}</p>\n            <p class=\"mb2\"><span class=\"fw5\">Favorite colors:</span> {{StringsJoin .Favorites.Colors \", \"}}</p>\n        </div>\n    </div>\n    {{end}}","spans":[]},{"type":"paragraph","text":"That's all! The final main.go file should look like so:","spans":[]},{"type":"preformatted","text":"    package main\n    \n    import (\n        \"encoding/json\"\n        \"flag\"\n        \"log\"\n        \"html/template\"\n        \"net/http\"\n        \"path/filepath\"\n        \"strings\"\n    \n        _ \"github.com/jackc/pgx/stdlib\"\n        \"github.com/jmoiron/sqlx\"\n        \"github.com/jmoiron/sqlx/types\"\n        \"github.com/pkg/errors\"\n    )\n    \n    // ContactFavorites is a field that contains a contact's favorites\n    type ContactFavorites struct {\n        Colors []string `json:\"colors\"`\n    }\n    \n    // Contact represents a Contact model in the database    \n    type Contact struct {\n        ID                   int\n        Name, Address, Phone string\n    \n        FavoritesJSON types.JSONText    `db:\"favorites\"`\n        Favorites     *ContactFavorites `db:\"-\"`\n    \n        CreatedAt string `db:\"created_at\"`\n        UpdatedAt string `db:\"updated_at\"`\n    }\n    \n    var (\n        connectionString = flag.String(\"conn\", getenvWithDefault(\"DATABASE_URL\", \"\"), \"PostgreSQL connection string\")\n        listenAddr       = flag.String(\"addr\", getenvWithDefault(\"LISTENADDR\", \":8080\"), \"HTTP address to listen on\")\n        db               *sqlx.DB\n        tmpl             = template.New(\"\")\n    )\n    \n    func getenvWithDefault(name, defaultValue string) string {\n            val := os.Getenv(name)\n            if val == \"\" {\n                    val = defaultValue\n            }\n    \n            return val\n    }\n    \n    func main() {\n        flag.Parse()\n        var err error\n    \n        // templating\n    \n        tmpl.Funcs(template.FuncMap{\"StringsJoin\": strings.Join})\n        _, err = tmpl.ParseGlob(filepath.Join(\".\", \"templates\", \"*.html\"))\n        if err != nil {\n            log.Fatalf(\"Unable to parse templates: %v\\n\", err)\n        }\n    \n        // postgres connection\n    \n        if *connectionString == \"\" {\n            log.Fatalln(\"Please pass the connection string using the -conn option\")\n        }\n    \n        db, err = sqlx.Connect(\"pgx\", *connectionString)\n        if err != nil {\n            log.Fatalf(\"Unable to establish connection: %v\\n\", err)\n        }\n    \n        // http server\n    \n        http.HandleFunc(\"/\", handler)\n    \n        log.Printf(\"listening on %s\\n\", *listenAddr)\n        http.ListenAndServe(*listenAddr, nil)\n    }\n    \n    func fetchContacts() ([]*Contact, error) {\n        contacts := []*Contact{}\n        err := db.Select(&contacts, \"select * from contacts\")\n        if err != nil {\n            return nil, errors.Wrap(err, \"Unable to fetch contacts\")\n        }\n    \n        for _, contact := range contacts {\n            err := json.Unmarshal(contact.FavoritesJSON, &contact.Favorites)\n    \n            if err != nil {\n                return nil, errors.Wrap(err, \"Unable to parse JSON favorites\")\n            }\n        }\n    \n        return contacts, nil\n    }\n    \n    func handler(w http.ResponseWriter, r *http.Request) {\n        contacts, err := fetchContacts()\n        if err != nil {\n            w.WriteHeader(http.StatusInternalServerError)\n            w.Write([]byte(err.Error()))\n            return\n        }\n    \n        tmpl.ExecuteTemplate(w, \"index.html\", struct{ Contacts []*Contact }{contacts})\n    }","spans":[]},{"type":"paragraph","text":"Run the program again, passing in your database's connection string like so and you should see the contacts list:","spans":[]},{"type":"preformatted","text":"    go run main.go -conn \"connection string here\"\n    # alternatively:\n    DATABASE_URL=\"connection string here\" go run main.go","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/3c4e728b-5295-49b9-af19-4ea7e7b4a730_Kamal-5.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":1396}},{"type":"heading1","text":"Conclusion","spans":[]},{"type":"paragraph","text":"After following this post, you will have learned how to build a simple contacts list step-by-step, starting with an empty page served by an HTTP web-server and ending with one that renders a list of contacts fetched from a PostgreSQL database. Along the way, you will have become familiar with using html/template to render a web page with dynamic data, connecting to a PostgreSQL database, and interacting with JSONB objects stored in the database.","spans":[]},{"type":"heading2","text":"Next Steps","spans":[]},{"type":"paragraph","text":"Here are some things you can do after following this post for further practice:","spans":[]},{"type":"list-item","text":"Print favorite colors as a bullet point list with each color being a separate item. Use html/template's built-in range function to loop over the favorite colors slice.","spans":[]},{"type":"list-item","text":"Add a favorite shape (square, circle, etc.) to one or more contacts and edit the template to display it. The Contact struct should stay unmodified.","spans":[]},{"type":"list-item","text":"List the contacts in the order that they were last updated, most recent first.","spans":[]},{"type":"paragraph","text":"[Hungry for more tutorials? Try Kamal's guides to \"Creating a Simple Contacts List with Laravel and PostgreSQL\" and \"Deploying a Fully-automated Git-based Static Website in Under 5 Minutes\"]","spans":[{"start":50,"end":111,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/create-simple-contacts-laravel-postgresql/"}},{"start":116,"end":189,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/deploying-a-fully-automated-git-based-static-website-in-under-5-minutes/"}}]},{"type":"paragraph","text":"Kamal Nasser is a Developer Advocate at DigitalOcean. He is also a Computer Science student with a passion for software engineering and avocados. You can find him on Twitter @kamaln7.","spans":[{"start":0,"end":183,"type":"em"},{"start":174,"end":182,"type":"hyperlink","data":{"link_type":"Web","url":"https://twitter.com/kamaln7"}}]}],"blog_post_date":"2019-03-18","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}},{"tag1":{"tag":"Community","_linkType":"Link.document","_meta":{"uid":"community"}}}],"_meta":{"uid":"create-a-simple-contacts-list-with-go"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Kenneth Reitz","author_image":{"dimensions":{"width":1000,"height":667},"alt":"Kenneth Reitz","copyright":null,"url":"https://images.prismic.io/www-static/a7da1b896a3f5366e6be9c9594fc7668e1094197_dscf3147.jpg?auto=compress,format"},"_meta":{"uid":"kenneth_reitz"}},"blog_header_image":{"dimensions":{"width":800,"height":600},"alt":"lighthouse","copyright":null,"url":"https://images.prismic.io/www-static/d3aad1c2-29a8-4c99-b211-0212aa4ac7f6_qa-dribbble.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Some Postgres Best Practices","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Over my many years of serving as a Developer Relations Advocate, I’ve met and spoken with thousands of developers all over the world about their database configurations, and many have shared with me their lessons learned.","spans":[]},{"type":"paragraph","text":"To celebrate the launch of our new managed database product, I'd like to share a few key takeaways you might apply to your new managed PostgreSQL databases.","spans":[{"start":31,"end":59,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/managed-databases/"}},{"start":127,"end":155,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/databases/"}}]},{"type":"heading3","text":"Use Connection Strings & Environment Variables","spans":[]},{"type":"paragraph","text":"The Twelve Factors, a set of best practices for building web applications for the cloud, is very clear about one thing: never store your database credentials in your codebase! Your code should be considered both proprietary and of inherent value, while at the same time being considered inherently insecure. Your data is what’s truly valuable.","spans":[{"start":0,"end":18,"type":"hyperlink","data":{"link_type":"Web","url":"https://12factor.net/"}}]},{"type":"paragraph","text":"So, to provide trusted credentials to trusted code, we recommend exposing the connection string of your Postgres database as an environment variable, named ```[php]{`DATABASE_URL`}```. Every common web framework has support for this (e.g., with Django, see dj-database-url).  ","spans":[{"start":257,"end":272,"type":"hyperlink","data":{"link_type":"Web","url":"https://pypi.org/project/dj-database-url/"}}]},{"type":"heading3","text":"Rotate Credentials Regularly","spans":[]},{"type":"paragraph","text":"It’s a good idea to rotate your database credentials periodically, across your organization. However, during special times, like when offboarding a member of the team, it’s important to rotate the credentials to the Managed Databases and Spaces they had access to.","spans":[]},{"type":"paragraph","text":"All it takes is one stray ```[php]{`.env`}``` file from a couple of years ago to get into the wrong hands, and then your entire infrastructure could be instantly compromised. I recommend having organization-wide quarterly “credential rotation” days, with hard deadlines. This (likely) tedious process will be forced to become streamlined as your team continues to rotate the credentials month after month, encouraging automation. It’s best for everyone. ","spans":[]},{"type":"heading3","text":"Use BIGINT or UUID for Primary Keys","spans":[]},{"type":"paragraph","text":"A big mistake in a lot of applications is using `INT` instead of `BIGINT` for primary keys (every Django application does this by default, for example).","spans":[]},{"type":"paragraph","text":"When you use ```[php]{`INT`}``` instead of ```[php]{`BIGINT`}```, eventually, one day, the value in your database exceeds the “storage capacity” of an ```[php]{`INT`}```, and a ```[php]{`BIGINT`}``` must be migrated to. The migration time to convert a table from ```[php]{`INT`}``` to ```[php]{`BIGINT`}``` usually takes around 4 hours, once the problem has been located.","spans":[]},{"type":"paragraph","text":"```[php]{`BIGINT`}``` is much more appropriate as a default PK, and is not that much more expensive to store or index.","spans":[]},{"type":"paragraph","text":"I personally use UUIDs in all of my databases: ","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    -- Enable pgcrypto for UUID support.","spans":[]},{"type":"paragraph","text":"    CREATE EXTENSION pgcrypto;","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"    -- Table: notes","spans":[]},{"type":"paragraph","text":"    CREATE TABLE notes (  ","spans":[]},{"type":"paragraph","text":"        uuid UUID DEFAULT gen_random_uuid(),","spans":[]},{"type":"paragraph","text":"        body text  NOT NULL,","spans":[]},{"type":"paragraph","text":"        byline text,","spans":[]},{"type":"paragraph","text":"        CONSTRAINT notes_pk PRIMARY KEY (uuid)","spans":[]},{"type":"paragraph","text":"    );","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"Learn more about uuid4 / pgcrypto in Postgres.","spans":[{"start":0,"end":45,"type":"hyperlink","data":{"link_type":"Web","url":"https://til.hashrocket.com/posts/64b5949eac-generating-uuids-with-pgcrypto"}}]},{"type":"heading3","text":"Use Connection Pooling","spans":[]},{"type":"paragraph","text":"When connecting to a high-throughput Postgres database server, it’s considered best practice to configure your clients to use PgBouncer, a lightweight connection pooler for PostgreSQL, instead of connecting to the database server directly.","spans":[{"start":126,"end":135,"type":"hyperlink","data":{"link_type":"Web","url":"https://wiki.postgresql.org/wiki/PgBouncer"}}]},{"type":"paragraph","text":"Connection pooling has many performance advantages, and will make the query performance characteristics of your database much more deterministic.","spans":[{"start":28,"end":50,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/managed-databases-connection-pools-and-postgresql-benchmarking-using-pgbench"}}]},{"type":"paragraph","text":"However, here at DigitalOcean, we take care of that for you! So, there’s no need to run your own instance of PgBouncer when using DigitalOcean Postgres, as it’s already provided and pre-tuned out of the box. Simplicity at scale.","spans":[{"start":34,"end":59,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/databases/how-to/postgresql/manage-connection-pools/"}}]},{"type":"heading3","text":"Further Reading","spans":[]},{"type":"paragraph","text":"Here are some further resources for upping your Postgres game:","spans":[]},{"type":"list-item","text":"Postgres Guide","spans":[{"start":0,"end":14,"type":"hyperlink","data":{"link_type":"Web","url":"http://postgresguide.com/"}}]},{"type":"list-item","text":"Managed Databases Connection Pools and PostgreSQL Benchmarking","spans":[{"start":0,"end":62,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/managed-databases-connection-pools-and-postgresql-benchmarking-using-pgbench"}}]},{"type":"paragraph","text":"Happy hacking! ","spans":[]},{"type":"paragraph","text":"Kenneth Reitz (@kennethreitz) ","spans":[{"start":15,"end":28,"type":"hyperlink","data":{"link_type":"Web","url":"https://twitter.com/kennethreitz"}}]},{"type":"paragraph","text":"Developer Relations Advocate","spans":[]}],"blog_post_date":"2019-03-04","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"some-postgres-best-practices"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Kamal Nasser","author_image":{"dimensions":{"width":1008,"height":1008},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/e2285fcfaf32ce7ec26329fe7e416ae896fbf991_portrait_2k18_bw_smallres.jpg?auto=compress,format"},"_meta":{"uid":"kamal-nasser"}},"blog_header_image":{"dimensions":{"width":1200,"height":900},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/cfc9e5e6-a7f9-4565-8023-a8a94fd572fb_database-mostov_dribbble.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Creating a Simple Contacts List with Laravel and PostgreSQL","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"In this post, we will build a simple Laravel app that displays a contacts list on a page. Using Eloquent and PostgreSQL's JSON object support, the app will query the database for the contacts and their details. This is what the result will look like:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/03a96195-00d0-4aa8-8c94-d05375d015f6_Kamal-1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":1396}},{"type":"paragraph","text":"By doing this, you will learn how to connect Laravel to a DigitalOcean Managed PostgreSQL database cluster, initialize the database with random data using Laravel factories and seeders, and store and read JSON documents in PostgreSQL using Laravel Eloquent.","spans":[{"start":58,"end":106,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/announcing-managed-databases-for-postgresql/"}}]},{"type":"heading2","text":"Requirements","spans":[]},{"type":"paragraph","text":"This post assumes that you have a working PHP development environment. You will need:","spans":[]},{"type":"list-item","text":"PHP 7.1+","spans":[]},{"type":"list-item","text":"Composer","spans":[{"start":0,"end":8,"type":"hyperlink","data":{"link_type":"Web","url":"https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos"}}]},{"type":"list-item","text":"Laravel’s required PHP extensions","spans":[{"start":10,"end":33,"type":"hyperlink","data":{"link_type":"Web","url":"https://laravel.com/docs/5.7/installation#server-requirements"}}]},{"type":"heading2","text":"Step 1: Create a Base Laravel App","spans":[]},{"type":"paragraph","text":"Let's start by creating a new blank Laravel app that will serve as a base for our web app. In a directory of your liking, generate a new project using Composer:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    composer create-project --prefer-dist laravel/laravel laravel-contacts","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"If you browse into the newly-created `laravel-contacts` directory and run Laravel's built-in web server, you will see the default Laravel welcome page:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    cd laravel-contacts","spans":[]},{"type":"paragraph","text":"    php artisan serve","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/67fc2310-14e8-420c-b837-1c583b84708b_Kamal-2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":869}},{"type":"paragraph","text":"Let's think about what our app will need. We want to have a list of contacts, so the database will store them. This means that we will need a ```[php]{`Contact`}``` model and a migration for the ```[php]{`contacts`}``` table. We'll also want to initialize the database with some random contacts so we have something to see, which will require a ```[php]{`Contact`}``` Factory and Seeder. ","spans":[]},{"type":"paragraph","text":"Let's build them out step by step:","spans":[]},{"type":"heading2","text":"Step 2: Create the Contact Model","spans":[]},{"type":"paragraph","text":"As shown in the screenshot above, a contact will have the following properties:","spans":[]},{"type":"list-item","text":"Name","spans":[]},{"type":"list-item","text":"Phone Number","spans":[]},{"type":"list-item","text":"Address","spans":[]},{"type":"list-item","text":"Favorite Colors","spans":[]},{"type":"paragraph","text":"For the favorite colors property, we will make use of Postgres's JSON data type. We will create a generic \"favorites\" column that will contain a JSON object with a list of favorite things. In this post we will store favorite colors only, but using a generic “favorites” object allows us to add different types in the future.","spans":[]},{"type":"paragraph","text":"Generate a model, factory, and migration using ```[php]{`artisan`}```:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    php artisan make:model -f -m Contact","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"Now we'll configure the different parts of the model.","spans":[]},{"type":"heading3","text":"Migration","spans":[]},{"type":"paragraph","text":"Edit the generated migration file stored in ```[php]{`database/migrations/*_create_contacts_table.php`}```. Inside the ```[php]{`Schema::create()`}``` block, we will define the structure of the table:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <?php  ","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Support\\Facades\\Schema;  ","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Database\\Schema\\Blueprint;  ","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Database\\Migrations\\Migration;  ","spans":[]},{"type":"paragraph","text":"    class CreateContactsTable extends Migration  ","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"        /**","spans":[]},{"type":"paragraph","text":"          * Run the migrations.","spans":[]},{"type":"paragraph","text":"          *","spans":[]},{"type":"paragraph","text":"          * @return void","spans":[]},{"type":"paragraph","text":"          */","spans":[]},{"type":"paragraph","text":"        public function up()","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"            Schema::create('contacts', function (Blueprint $table) {","spans":[]},{"type":"paragraph","text":"                $table->increments('id');","spans":[]},{"type":"paragraph","text":"                $table->string('name');","spans":[]},{"type":"paragraph","text":"                $table->string('phone');","spans":[]},{"type":"paragraph","text":"                $table->string('address');","spans":[]},{"type":"paragraph","text":"                $table->jsonb('favorites')->default('{}');","spans":[]},{"type":"paragraph","text":"                $table->timestamps();","spans":[]},{"type":"paragraph","text":"            });","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"        /**","spans":[]},{"type":"paragraph","text":"            * Reverse the migrations.","spans":[]},{"type":"paragraph","text":"            *","spans":[]},{"type":"paragraph","text":"            * @return void","spans":[]},{"type":"paragraph","text":"            */","spans":[]},{"type":"paragraph","text":"        public function down()","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"            Schema::dropIfExists('contacts');","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"This will configure the database column as described above with the `favorites` column set to a JSON object. The default value is an empty object.","spans":[]},{"type":"heading3","text":"Random Contact Generation","spans":[]},{"type":"paragraph","text":"The contact factory generates random values for a contact. We will configure a seeder that will use the factory to insert 10 random contacts to the database. This will provide us with an option to seed the database with random contacts when running the migration. Let's start with the factory.","spans":[]},{"type":"heading4","text":"Factory","spans":[]},{"type":"paragraph","text":"Edit the generated factory file stored in ```[php]{`database/factories/ContactFactory.php`}```. First, we will generate three random colors to use as favorites:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    $colors = collect(range(1, 3))->map(function() use ($faker) {","spans":[]},{"type":"paragraph","text":"        return $faker->colorName;","spans":[]},{"type":"paragraph","text":"    })->toArray();","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"We will use these colors along with other data generated using the Faker library to return the contact's properties:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    return [","spans":[]},{"type":"paragraph","text":"        'name' => $faker->name,","spans":[]},{"type":"paragraph","text":"        'phone' => $faker->e164PhoneNumber,","spans":[]},{"type":"paragraph","text":"        'address' => $faker->address,","spans":[]},{"type":"paragraph","text":"        'favorites' => ['colors' => $colors],","spans":[]},{"type":"paragraph","text":"    ];","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"The resulting factory should look like this:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <?php","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"    use Faker\\Generator as Faker;","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"    $factory->define(App\\Contact::class, function (Faker $faker) {","spans":[]},{"type":"paragraph","text":"        // generate 3 random colors","spans":[]},{"type":"paragraph","text":"        $colors = collect(range(1, 3))->map(function() use ($faker) {","spans":[]},{"type":"paragraph","text":"            return $faker->colorName;","spans":[]},{"type":"paragraph","text":"        })->toArray();","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"        return [","spans":[]},{"type":"paragraph","text":"            'name' => $faker->name,","spans":[]},{"type":"paragraph","text":"            'phone' => $faker->e164PhoneNumber,","spans":[]},{"type":"paragraph","text":"            'address' => $faker->address,","spans":[]},{"type":"paragraph","text":"            'favorites' => ['colors' => $colors],","spans":[]},{"type":"paragraph","text":"        ];","spans":[]},{"type":"paragraph","text":"    });","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"heading4","text":"Seeder","spans":[]},{"type":"paragraph","text":"Now we need to define a seeder that will be run upon migrating the database. Start by generating the file:","spans":[]},{"type":"paragraph","text":"    ```[php]{`php artisan make:seeder ContactsTableSeeder`}```","spans":[]},{"type":"paragraph","text":"Open the generated file ```[php]{`database/seeds/ContactsTableSeeder.php`}``` in an editor and call the factory inside the ```[php]{`run()`}``` function like so:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <?php","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Database\\Seeder;","spans":[]},{"type":"paragraph","text":"    class ContactsTableSeeder extends Seeder","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"        /**","spans":[]},{"type":"paragraph","text":"         * Run the database seeds.","spans":[]},{"type":"paragraph","text":"         *","spans":[]},{"type":"paragraph","text":"         * @return void","spans":[]},{"type":"paragraph","text":"         */","spans":[]},{"type":"paragraph","text":"        public function run()","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"            factory(App\\Contact::class, 10)->create();","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"To enable the seeder, edit `database/seeds/DatabaseSeeder.php` and call it inside the `run()` function:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <?php","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Database\\Seeder;","spans":[]},{"type":"paragraph","text":"    class DatabaseSeeder extends Seeder","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"        /**","spans":[]},{"type":"paragraph","text":"         * Seed the application's database.","spans":[]},{"type":"paragraph","text":"         *","spans":[]},{"type":"paragraph","text":"         * @return void","spans":[]},{"type":"paragraph","text":"         */","spans":[]},{"type":"paragraph","text":"        public function run()","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"            $this->call(ContactsTableSeeder::class);","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"heading3","text":"Model","spans":[]},{"type":"paragraph","text":"There is one final adjustment that we need to make to the model. The ```[php]{`artisan make:model`}``` command generated a model file for us, located in ```[php]{`app/Contact.php`}```. The `favorites` column represents a JSON object, so we need to cast it to a PHP array before using it. Eloquent makes this very easy by automatically casting back and forth between the correct formats. Inside the ```[php]{`Contact`}``` class in ```[php]{`app/Contact.php`}```, we can add the cast:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <?php","spans":[]},{"type":"paragraph","text":"    namespace App;","spans":[]},{"type":"paragraph","text":"    use Illuminate\\Database\\Eloquent\\Model;","spans":[]},{"type":"paragraph","text":"    class Contact extends Model","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"        protected $casts = [","spans":[]},{"type":"paragraph","text":"            'favorites' => 'array',","spans":[]},{"type":"paragraph","text":"        ];","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"heading2","text":"Step 3: Configure the Database","spans":[]},{"type":"paragraph","text":"We will use DigitalOcean Databases for our PostgreSQL cluster. If you haven’t yet, create a new one—it only takes a few minutes. If you prefer a text post, see the product documentation for Databases. If you prefer a video, click here.","spans":[{"start":160,"end":199,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/databases/how-to/clusters/create/"}},{"start":224,"end":234,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.youtube.com/watch?v=jY5FhyiEdig"}}]},{"type":"image","url":"https://images.prismic.io/www-static/e80ddc5c-b52d-42a0-a6b9-faf3e0a10a51_Kamal-3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":427}},{"type":"paragraph","text":"In the cluster’s Overview page in the control panel, get its connection details as “Connection parameters”. Open Laravel's ```[php]{`.env`}``` file and set ```[php]{`DB_CONNECTION=pgsql`}```. Below it, set all the other variables according to your connection credentials.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/3f4ec974-28d2-46ca-80b3-18a8461abca2_Kamal-4.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":990,"height":540}},{"type":"paragraph","text":"There is one setting that isn’t available as an environment variable: ```[php]{`sslmode`}```. DigitalOcean Databases do not support non-TLS connections so we need to set ```[php]{`sslmode`}``` to ```[php]{`require`}```. Open ```[php]{`config/database.php`}``` in an editor, scroll down to the ```[php]{`pgsql`}``` definition, and update the setting like so:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    'pgsql' => [","spans":[]},{"type":"paragraph","text":"        ...","spans":[]},{"type":"paragraph","text":"        'sslmode' => 'require',","spans":[]},{"type":"paragraph","text":"    ],","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"[Related: Check out our Resource Center for resources and guides on Managed Databases]","spans":[{"start":0,"end":86,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/resources/managed-databases/"}}]},{"type":"heading2","text":"Step 4: Migrate and seed","spans":[]},{"type":"paragraph","text":"Now that we have completed defining everything database-related in our app, from the Contact model to the database connection info, we can execute the migration and seed the database:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    php artisan migrate --seed","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"We have a database filled with contacts now—nice!  ","spans":[]},{"type":"heading2","text":"Step 4: Build the Contact List Page","spans":[]},{"type":"paragraph","text":"Ok, let's finish up by creating a page to show our contacts.","spans":[]},{"type":"paragraph","text":"To keep things simple we will replace Laravel's default home page. Edit `routes/web.php` and set the `/` route to the following:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    Route::get('/', function () {","spans":[]},{"type":"paragraph","text":"        $contacts = App\\Contact::all();","spans":[]},{"type":"paragraph","text":"        return view('welcome', ['contacts' => $contacts]);","spans":[]},{"type":"paragraph","text":"    });","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"This will fetch all the contacts from the database and pass them to the ```[php]{`welcome`}``` view.","spans":[]},{"type":"paragraph","text":"Then, edit the view located in `resources/views/welcome.blade.php` and replace its contents with the following:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    <!doctype html>","spans":[]},{"type":"paragraph","text":"    <html>","spans":[]},{"type":"paragraph","text":"        <head>","spans":[]},{"type":"paragraph","text":"            <meta charset=\"utf-8\">","spans":[]},{"type":"paragraph","text":"            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"            <title>Contacts</title>","spans":[]},{"type":"paragraph","text":"            <link rel=\"stylesheet\" href=\"https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css\"/>","spans":[]},{"type":"paragraph","text":"        </head>","spans":[]},{"type":"paragraph","text":"        <body>","spans":[]},{"type":"paragraph","text":"            <div class=\"mw6 center pa3 sans-serif\">","spans":[]},{"type":"paragraph","text":"                <h1 class=\"mb4\">Contacts</h1>","spans":[]},{"type":"paragraph","text":"    ","spans":[]},{"type":"paragraph","text":"                @foreach($contacts as $contact)","spans":[]},{"type":"paragraph","text":"                <div class=\"pa2 mb3 striped--near-white\">","spans":[]},{"type":"paragraph","text":"                    <header class=\"b mb2\">{{ $contact->name }}</header>","spans":[]},{"type":"paragraph","text":"                    <div class=\"pl2\">","spans":[]},{"type":"paragraph","text":"                        <p class=\"mb2\">{{ $contact->phone }}</p>","spans":[]},{"type":"paragraph","text":"                        <p class=\"pre mb3\">{{ $contact->address }}</p>","spans":[]},{"type":"paragraph","text":"                        <p class=\"mb2\"><span class=\"fw5\">Favorite colors:</span> {{ implode(', ', $contact->favorites['colors']) }}</p>","spans":[]},{"type":"paragraph","text":"                    </div>","spans":[]},{"type":"paragraph","text":"                </div>","spans":[]},{"type":"paragraph","text":"                @endforeach","spans":[]},{"type":"paragraph","text":"            </div>","spans":[]},{"type":"paragraph","text":"        </body>","spans":[]},{"type":"paragraph","text":"    </html>","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"heading2","text":"Step 5: Deploy","spans":[]},{"type":"paragraph","text":"We're all done! Let's go ahead and try out our app.","spans":[]},{"type":"paragraph","text":"Like before, we can use Laravel's built-in web server. Simply run ```[php]{`php artisan serve`}``` and browse to http://127.0.0.1:8000.","spans":[{"start":113,"end":134,"type":"hyperlink","data":{"link_type":"Web","url":"http://127.0.0.1:8000/"}}]},{"type":"heading3","text":"Deploy on a Droplet","spans":[]},{"type":"paragraph","text":"To deploy the app on a Droplet, follow the How To Deploy a Laravel Application with Nginx on Ubuntu 16.04 guide on the DigitalOcean community.","spans":[{"start":43,"end":105,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/how-to-deploy-a-laravel-application-with-nginx-on-ubuntu-16-04"}}]},{"type":"heading2","text":"Conclusion","spans":[]},{"type":"paragraph","text":"In this post, we went through building a simple contacts list step by step and explored a few concepts such as seeding the database with sample random data, using JSON-type database fields, and connecting to a managed PostgreSQL Database.","spans":[]},{"type":"paragraph","text":"Here are a few things you can do following this post, building on the app we created:","spans":[]},{"type":"list-item","text":"Add a favorite shape (square, circle, etc.) to one or more contacts and edit the view template to display it. Update the seeder to include a random shape at random, or for every contact.","spans":[]},{"type":"list-item","text":"Extract the route handler (in ```[php]{`web.php`}```) into its own controller. Use ```[php]{`artisan make:controller`}``` to create the controller file. Consult the Laravel documentation on controllers for more details.","spans":[]},{"type":"list-item","text":"List the contacts in the order that they were last updated, most recent first.","spans":[]},{"type":"paragraph","text":"[Hungry for another tutorial? Try Kamal's guide to \"Deploying a Fully-automated Git-based Static Website in Under 5 Minutes\"]","spans":[{"start":0,"end":125,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/deploying-a-fully-automated-git-based-static-website-in-under-5-minutes/"}}]},{"type":"paragraph","text":"Kamal Nasser is a Developer Advocate at DigitalOcean. He is also a Computer Science student with a passion for software engineering and avocados. You can find him on Twitter @kamaln7.","spans":[{"start":0,"end":183,"type":"em"},{"start":174,"end":182,"type":"hyperlink","data":{"link_type":"Web","url":"https://twitter.com/kamaln7"}}]}],"blog_post_date":"2019-02-21","tags":[{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}},{"tag1":{"tag":"Community","_linkType":"Link.document","_meta":{"uid":"community"}}}],"_meta":{"uid":"create-simple-contacts-laravel-postgresql"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"DigitalOcean","author_image":{"dimensions":{"width":600,"height":600},"alt":"Sammy avatar","copyright":null,"url":"https://images.prismic.io/www-static/a10e3c2eb15b74ee43f872be3044313423b1c9a9_sammy_avatar.png?auto=compress,format"},"_meta":{"uid":"digitalocean"}},"blog_header_image":{"dimensions":{"width":5000,"height":2500},"alt":"OSCon letters with a dolphin, narwhal, and jellyfish popping out of the letters with the words 'See you there!' underneath illustration","copyright":null,"url":"https://images.prismic.io/www-static/f3efcf4db49e107ee21d7edf28add4c7c9c2fe18_oscon_seeyouthere.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Prepped for Portland and OSCON 2018","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"It's the 20th year of OSCON, held this week in Portland, Oregon, and we will be in attendance!","spans":[{"start":22,"end":27,"type":"hyperlink","data":{"link_type":"Web","url":"https://conferences.oreilly.com/oscon/oscon-or"}}]},{"type":"paragraph","text":"We have two great presentations lined up:","spans":[]},{"type":"list-item","text":"Lauren McCarthy and Tom Spiegelman will share DigitalOcean's approach to tackling the Spectre and Meltdown vulnerabilities, covering what the company chose to move forward with and why, and","spans":[]},{"type":"list-item","text":"Andrew Kim will be sharing a technical deep dive into how DigitalOcean uses anycast IPs, BGP, and Kubernetes to run globally distributed services on containers","spans":[]},{"type":"paragraph","text":"On Wednesday, July 18, from 11:50 AM-12:30 PM, Lauren McCarthy and Tom Spiegelman present \"DigitalOcean’s approach to Spectre and Meltdown\" in E143/144.","spans":[{"start":91,"end":138,"type":"hyperlink","data":{"link_type":"Web","url":"https://conferences.oreilly.com/oscon/oscon-or/public/schedule/detail/71101"}},{"start":91,"end":138,"type":"strong"}]},{"type":"paragraph","text":"News of the security vulnerabilities Spectre and Meltdown gripped headlines earlier this year, and for good reason: the bugs affected an estimated three billion chips in use. The impact to cloud providers was substantial, and DigitalOcean was no exception.","spans":[{"start":0,"end":256,"type":"em"}]},{"type":"paragraph","text":"Lauren McCarthy and Tom Spiegelman share DigitalOcean’s approach to tackling the Spectre and Meltdown vulnerabilities—dubbed \"Smeltdown”—covering what the company chose to move forward with and why. This was one of the biggest challenges the company has dealt with in terms of complexity and scale. One of the key issues was timeliness: while the big cloud companies received advanced notice, DigitalOcean didn’t have that luxury. But it couldn’t use that as an excuse: it just meant working smarter and harder. Lauren and Tom discuss the hardships faced and how the chosen solution left the company with a more secure cloud infrastructure and ready move forward to work toward new offerings so that developers and their teams can focus on what matters: building software that changes the world.","spans":[{"start":0,"end":795,"type":"em"}]},{"type":"paragraph","text":"On Thursday, July 19, from 4:15 PM-4:55 PM, Andrew Kim presents \"Containers and anycast IPs at DigitalOcean\" in D139/140.","spans":[{"start":65,"end":107,"type":"hyperlink","data":{"link_type":"Web","url":"https://conferences.oreilly.com/oscon/oscon-or/public/schedule/detail/67422"}},{"start":65,"end":107,"type":"strong"}]},{"type":"paragraph","text":"Today’s container networking technology has made it significantly easier to build distributed systems on top of container orchestrators such as Kubernetes, Mesosphere, and Docker Swarm. Container networking technologies use Linux primitives such as iptables and IPVS to provide load-balancing capabilities for network traffic across containers in a cluster. These simple yet powerful tools are a cornerstone to the success of containerized systems, as they provide highly available environments with little to no effort.","spans":[{"start":0,"end":520,"type":"em"}]},{"type":"paragraph","text":"Despite the many benefits of container networking, running containerized applications that must be latency sensitive and globally distributed is an extremely challenging task. Container networking is mainly scoped for in-cluster traffic, leaving little room to globally distribute an application across multiple clusters. Moreover, extending a container network for external traffic requires many additional layers of abstraction, usually introducing points of failures in a cluster and increasing end-to-end latency.","spans":[{"start":0,"end":517,"type":"em"}]},{"type":"paragraph","text":"Andrew Kim leads a technical deep dive into how DigitalOcean uses anycast IPs, BGP, and Kubernetes to run globally distributed services on containers. Along the way, Andrew discusses design considerations for scalability, architectural trade-offs, data center networking, lessons learned in production, and challenges to adopting containers for latency sensitive applications.","spans":[{"start":0,"end":376,"type":"em"}]},{"type":"paragraph","text":"You can also catch us at booth #101 at the following times:","spans":[{"start":25,"end":35,"type":"strong"}]},{"type":"list-item","text":"Wednesday, July 18 from 10:20 AM to 7:00 PM, and","spans":[]},{"type":"list-item","text":"Thursday, July 19 from 10:20 AM to 4:15 PM","spans":[]}],"blog_post_date":"2018-07-16","tags":[{"tag1":{"tag":"Engineering","_linkType":"Link.document","_meta":{"uid":"engineering"}}},{"tag1":{"tag":"Developer Relations","_linkType":"Link.document","_meta":{"uid":"developer-relations"}}}],"_meta":{"uid":"oscon-2018"}}}]}}}