{"componentChunkName":"component---src-templates-tag-jsx","path":"/blog/tag/marketplace/","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":1,"currentPage":1,"uid":"marketplace","data":[{"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":null,"blog_headline":[{"type":"heading1","text":"Meet Microweber: Open source, drag & drop website builder for ecommerce entrepreneurs","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"This is a guest post from Boris Sokolov and Peter Ivanov, cofounders of Microweber. Microweber is featured as a 1-Click App on the DigitalOcean Marketplace.","spans":[{"start":0,"end":156,"type":"em"},{"start":112,"end":123,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/microweber"}},{"start":131,"end":155,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"paragraph","text":"If you're looking to launch an ecommerce venture, you probably have lots of questions. Is it going to be too complicated? Take too much time? Need too many plugins and add-ons?","spans":[]},{"type":"paragraph","text":"That’s why we created Microweber. It's an open source, drag-and-drop website builder specifically tailored to address the needs of ecommerce entrepreneurs.","spans":[]},{"type":"heading2","text":"Why we built Microweber","spans":[]},{"type":"paragraph","text":"We noticed that most existing website builders and CMS platforms felt clunky and lacked simple drag-and-drop capabilities. Meanwhile, platforms that did use drag-and-drop tended to lock users into their ecosystem, were expensive, and didn't offer a built-in online store function.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/5ca697bc-90d3-4510-862b-74aac095f84b_image1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":948,"height":969}},{"type":"paragraph","text":"We saw an enormous opportunity to create an all-in-one platform for setting up websites and online stores. We wanted it to be so dead simple that anyone without any web experience could launch an ecommerce business in a matter of minutes.","spans":[]},{"type":"heading2","text":"Making it simple – even for beginners","spans":[]},{"type":"paragraph","text":"To make things easier for you, Microweber runs on a smooth technological duo: our Drag and Drop and special Live Edit features, which fit perfectly into our modular architecture.","spans":[]},{"type":"paragraph","text":"Drag and drop means that you can use your mouse to drag any element or module – text fields, images, videos, layouts, you name it. This makes arranging or rearranging anything from blog posts to e-commerce catalogs easy and intuitive.","spans":[]},{"type":"paragraph","text":"Pairing this functionality with Live Edit mode makes it particularly powerful. With Live Edit, you can work live on your page. No pop-ups, no infuriating navigation or inter-page loading times. Take a look at how it works:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/a978f2fd-c037-4adc-b661-ad17a1c16e3e_mwcreate.gif?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":774,"height":477}},{"type":"paragraph","text":"Even users who consider themselves bad with technology find Microbrewer intuitive to use.","spans":[]},{"type":"heading2","text":"Building in a rich online store","spans":[]},{"type":"paragraph","text":"Building an online storefront can be taxing. Existing platforms either require external plugins or charge you a flat fee plus additional transaction fees for using their online store functions.","spans":[]},{"type":"paragraph","text":"We didn't think that was fair. So we decided to give Microweber a built-in online store that doesn't cost you anything extra.","spans":[]},{"type":"paragraph","text":"To make it easier, we have included various e-commerce features such as:","spans":[{"start":44,"end":63,"type":"hyperlink","data":{"link_type":"Web","url":"https://microweber.org/e-commerce"}}]},{"type":"list-item","text":"Multiple payment methods accepted (Paypal, Stripe, Authorize.net, Payza etc.)","spans":[]},{"type":"list-item","text":"Management and tracking of orders inside Microweber","spans":[]},{"type":"list-item","text":"Automated emails after customer purchases","spans":[]},{"type":"list-item","text":"A variety of shipping options","spans":[]},{"type":"list-item","text":"Custom product fields – sizes, colors, prices, and numbers, among others","spans":[]},{"type":"list-item","text":"Social network linking","spans":[]},{"type":"list-item","text":"Currency options and setting your taxes as a merchant","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/ef7ad84a-0d1d-47cf-bf01-ad1d0ae1c8f3_image2.jpg?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1141,"height":768}},{"type":"paragraph","text":"Everything's inside Microweber, so you don't need to deal with external plugins or suffer from additional costs when you're bootstrapping.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/b8a9492d-ba5d-4767-bcb7-83f5e034bf5d_image5.jpg?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1139,"height":938}},{"type":"heading2","text":"Open-source and self-hosted: because privacy, flexibility and security matter","spans":[]},{"type":"paragraph","text":"As the Open Source Initiative notes, open source equals reliability. With open source you benefit from a whole community rigorously testing your software for bugs, vulnerabilities, or anything else that might affect the platform. Microweber has always been open source, and our own community is constantly on the watch to give us their input. The CMS has more than 50,000 downloads and installations around the world.","spans":[{"start":230,"end":268,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/microweber/microweber"}}]},{"type":"paragraph","text":"Microweber is also self-hosted. You can use us as your hosting provider, but you're also free to use any other provider on the Web. We don't lock you in, because we believe that you deserve choice and flexibility when it comes to installations.","spans":[]},{"type":"paragraph","text":"We are also proud to be part of the DigitalOcean Marketplace! You can find us in the Blogs and Forums category, or try out our app with just one click.","spans":[{"start":36,"end":60,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}},{"start":85,"end":110,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/category/blogs-and-forums"}},{"start":115,"end":150,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/microweber"}}]},{"type":"heading2","text":"What about the templates?","spans":[]},{"type":"paragraph","text":"Let's be honest: functionality alone doesn't cut it. You want beautiful customization at your fingertips. In a competitive environment, appealing templates play a big role in capturing your customers' attention.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/3d6b81c6-79a2-4236-863d-c32d66d8de74_image4.jpg?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1363,"height":1015}},{"type":"paragraph","text":"We've got good news – although we offer free and paid (premium) templates, as a DigitalOcean customer you gain free access to all templates!","spans":[]},{"type":"paragraph","text":"Stay tuned, because we are continually adding to our template portfolio, tailoring it to the needs of ecommerce entrepreneurs.","spans":[]},{"type":"paragraph","text":"We hope this was a useful introduction to what Microweber can do for you! We'd be thrilled if you give our CMS a try and share your feedback with us.","spans":[{"start":121,"end":148,"type":"hyperlink","data":{"link_type":"Web","url":"https://microweber.com/contact-us"}}]}],"blog_post_date":"2019-11-08","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"meet-microweber"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Antonio Rosales","author_image":null,"_meta":{"uid":"antonio_rosales"}},"blog_header_image":{"dimensions":{"width":1200,"height":628},"alt":"Marketplace","copyright":null,"url":"https://images.prismic.io/www-static/ac7642d1-0418-4949-a43e-8ba84d3b6cbe_Kubernetes_Marketplace_Header_PR.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"How we launched our Marketplace using DigitalOcean Kubernetes – Part 1","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"The mission of the DigitalOcean Marketplace team is to provide a platform to distill operational knowledge into sharable and repeatable software through community collaboration. We work together with DigitalOcean users and vendors to identify solutions, whether they’re single or multiple pieces of software, and encode best practices into single-click software easily deployable on a Droplets or Kubernetes infrastructure. Ultimately, we want to enable DigitalOcean users to focus 100% of their energy on developing, iterating, and perfecting their project.","spans":[{"start":19,"end":43,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}},{"start":385,"end":393,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/droplets/"}},{"start":397,"end":407,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/kubernetes/"}}]},{"type":"paragraph","text":"This post is the first in a multipart series describing how the DigitalOcean team built marketplace.digitalocean.com on DigitalOcean Kubernetes. If you have questions or ideas, we’d love to continue the conversation with you about deploying and operating resilient, scalable Kubernetes-based architectures on DigitalOcean Community Questions.","spans":[{"start":306,"end":341,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/questions/thoughts-on-deploying-and-operating-resilient-scalable-kubernetes-based-architectures"}}]},{"type":"heading2","text":"The three core components of Marketplace","spans":[]},{"type":"paragraph","text":"The design of the Marketplace platform needed to be performant, globally available, resilient, and scalable to user demand and growth of Marketplace platform features. To meet the latency and availability requirements of our global user base, we selected three main infrastructure components:","spans":[]},{"type":"o-list-item","text":"DigitalOcean Kubernetes for the core infrastructure","spans":[{"start":0,"end":23,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/kubernetes/"}}]},{"type":"o-list-item","text":"OpenChannel for the catalog API and data warehouse","spans":[{"start":0,"end":11,"type":"hyperlink","data":{"link_type":"Web","url":"https://openchannel.io/"}}]},{"type":"o-list-item","text":"Cloudflare for CDN and load-balancing","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.cloudflare.com/"}}]},{"type":"paragraph","text":"We deploy production and staging for Kubernetes clusters in DigitalOcean’s regional data centers located in:","spans":[{"start":60,"end":96,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/docs/platform/availability-matrix/"}}]},{"type":"list-item","text":"San Francisco, United States","spans":[]},{"type":"list-item","text":"New York City, United States","spans":[]},{"type":"list-item","text":"Frankfurt, Germany","spans":[]},{"type":"list-item","text":"Bangalore, India","spans":[]},{"type":"list-item","text":"Singapore","spans":[]},{"type":"paragraph","text":"We then configure Cloudflare to determine which Kubernetes cluster has the lowest latency from the user’s perspective. Thus, when a user goes to marketplace.digitalocean.com, the Kubernetes cluster with the least amount of latency is selected to deliver the Marketplace catalog, increasing performance. This configuration also improves availability as unhealthy Kubernetes clusters are removed from the Cloudflare load balancer and Marketplace traffic continues to be served from the next most performant Kubernetes cluster. This configuration is represented in the following figure.","spans":[{"start":145,"end":173,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"image","url":"https://images.prismic.io/www-static/0cdf15cc-300b-409f-b456-bb3b654d4741_image2-1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1238,"height":812}},{"type":"heading2","text":"The benefits of GitOps workflows","spans":[]},{"type":"paragraph","text":"We configure and deploy each Kubernetes cluster in an identical fashion in each region. Our Kubernetes deployment and service config is kept in GitHub in an effort to manage our Kubernetes deployment and operations through Git. This gives us multiple benefits, such as:","spans":[]},{"type":"o-list-item","text":"Safe upgrades: With this GitOps approach, we can redeploy clusters when we need to upgrade. If a cluster or service upgrade needs to be performed, we bring up a new staging cluster and verify operations through integration tests. If that goes well, we bring up a new cluster in a canary deploy fashion and run another integration test to confirm expected behavior. Once we are confident, we move the DNS mapping to the new cluster IP address and roll out the new clusters to all of the other regions.","spans":[{"start":0,"end":13,"type":"strong"},{"start":25,"end":31,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd"}},{"start":280,"end":293,"type":"hyperlink","data":{"link_type":"Web","url":"https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments"}}]},{"type":"o-list-item","text":"Repeatable environment: Development, staging, and production clusters are able to be repeatedly deployed, mirroring production with minimal effort by the developer for testing new features, learning, or bug fixes.","spans":[{"start":0,"end":22,"type":"strong"}]},{"type":"o-list-item","text":"Rollbacks: If we ever have an issue with a new deployment we can revert a commit and return back to a viable operating state.","spans":[{"start":0,"end":9,"type":"strong"}]},{"type":"o-list-item","text":"Automation: Git provides a central place with intuitive tools and process to initiate automated unit testing, integration, and deployments.","spans":[{"start":0,"end":10,"type":"strong"}]},{"type":"o-list-item","text":"Central source of truth and transparency: With staging and production clusters running globally in five regions, and numerous development clusters, the central truth is always the master branch. This also provides transparency to other teams, and new team members can simply examine the source while they are onboarding without affecting production environments.","spans":[{"start":0,"end":40,"type":"strong"}]},{"type":"heading2","text":"What’s on our clusters","spans":[]},{"type":"paragraph","text":"Our Kubernetes clusters run a variety of open source packages, including:","spans":[]},{"type":"list-item","text":"Prometheus (metrics)","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://prometheus.io/"}}]},{"type":"list-item","text":"FluentD (logging)","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.fluentd.org/"}}]},{"type":"list-item","text":"Fluent Bit (logging)","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://fluentbit.io/"}}]},{"type":"list-item","text":"Nginx (ingress and OpenChannel Proxy)","spans":[{"start":0,"end":5,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.nginx.com/"}},{"start":7,"end":14,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/kubernetes/ingress-nginx"}}]},{"type":"list-item","text":"Apollo, GraphQL, Node (front end)","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.apollographql.com/"}},{"start":8,"end":15,"type":"hyperlink","data":{"link_type":"Web","url":"https://graphql.org/"}},{"start":17,"end":21,"type":"hyperlink","data":{"link_type":"Web","url":"https://nodejs.org/en/"}}]},{"type":"list-item","text":"cert-manager and Let’s Encrypt (certification management)","spans":[{"start":0,"end":12,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.cert-manager.io/en/latest/"}},{"start":17,"end":30,"type":"hyperlink","data":{"link_type":"Web","url":"https://letsencrypt.org/"}}]},{"type":"image","url":"https://images.prismic.io/www-static/a6b5eaa8-4f36-44a8-b298-7a558f4499e6_image1-2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1999,"height":1478}},{"type":"paragraph","text":"In support of the clusters we run/utilize the following services outside of the cluster:","spans":[]},{"type":"list-item","text":"Grafana and Runscope (monitoring)","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://grafana.com/"}},{"start":12,"end":20,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.runscope.com/"}}]},{"type":"list-item","text":"Docker Hub (image repositories)","spans":[{"start":0,"end":10,"type":"hyperlink","data":{"link_type":"Web","url":"https://hub.docker.com/"}}]},{"type":"list-item","text":"Kustomize (templating config and passing environmental variables to deploys)","spans":[{"start":0,"end":9,"type":"hyperlink","data":{"link_type":"Web","url":"https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/"}}]},{"type":"list-item","text":"GitHub (code hosting)","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/"}}]},{"type":"list-item","text":"Kibana (log aggregation, viewing, and searching)","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.elastic.co/products/kibana"}}]},{"type":"list-item","text":"DigitalOcean Spaces for Marketplace assets (graphics for front end)","spans":[{"start":0,"end":19,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/products/spaces/"}}]},{"type":"list-item","text":"Shipit (automated deploys to staging and production from Git commits)","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/Shopify/shipit-engine"}}]},{"type":"list-item","text":"Codefresh (automated unit and integration testing on Git commits)","spans":[{"start":0,"end":9,"type":"hyperlink","data":{"link_type":"Web","url":"https://codefresh.io/"}}]},{"type":"heading2","text":"What’s next","spans":[]},{"type":"paragraph","text":"There is a lot to unpack here in terms of how we configure and operate the various pieces of our DigitalOcean Kubernetes-based environment.","spans":[]},{"type":"paragraph","text":"You may be asking:","spans":[]},{"type":"list-item","text":"How do we use Kustomize to pass environment variables?","spans":[]},{"type":"list-item","text":"How is Cloudflare configured to do global load balancing across multiple DigitalOcean Kubernetes clusters?","spans":[]},{"type":"list-item","text":"How does Codefresh enable speedy and accurate automated integration tests?","spans":[]},{"type":"list-item","text":"How are secrets managed?","spans":[]},{"type":"paragraph","text":"We will be addressing each of these topics in the next articles where we’ll cover those questions, as well as questions about:","spans":[]},{"type":"list-item","text":"Deployment and GitOps workflows","spans":[]},{"type":"list-item","text":"Metrics and logging","spans":[]},{"type":"list-item","text":"Automated unit and integration testing","spans":[]},{"type":"list-item","text":"Multi-region and Cloudflare set up","spans":[]},{"type":"paragraph","text":"As we learn from our deployments, we are also working to distill our operational knowledge for our monitoring infrastructure in the DigitalOcean Kubernetes 1-Click Monitoring Stack. Give it a try and let us know what you think.","spans":[{"start":132,"end":180,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/kubernetes-monitoring-stack"}},{"start":212,"end":226,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/digitalocean/marketplace-kubernetes/issues"}}]},{"type":"paragraph","text":"And please let us know what area you would like to see in the next blog post, or ask us a question here.","spans":[{"start":81,"end":103,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/community/questions/thoughts-on-deploying-and-operating-resilient-scalable-kubernetes-based-architectures"}}]}],"blog_post_date":"2019-10-25","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"how-we-launched-our-marketplace-using-digitalocean-kubernetes-part-1"}}},{"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":null,"blog_headline":[{"type":"heading1","text":"Meet Marketplace Partner Restyaboard: Self-Hosted Kanban Boards, With  a Little Extra","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"This is a guest post from Jimmy Johns, cofounder of Restyaboard.","spans":[{"start":0,"end":63,"type":"em"},{"start":52,"end":63,"type":"hyperlink","data":{"link_type":"Web","url":"https://restya.com/board"}}]},{"type":"paragraph","text":"Cloud platforms, mobile apps, and IoT are just a few technologies that are merging workspaces and living spaces. While the benefits of a remote workforce may seem obvious for the worker, there are many operational benefits for organizations when they enable a remote workforce culture. This includes access to a greater talent pool, real estate and overhead savings, higher productivity levels, and even employee retention.","spans":[{"start":123,"end":153,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/currents/july-2019/"}}]},{"type":"paragraph","text":"However, with the rise of virtual companies, having a distributed workforce can make it especially challenging to coordinate plans and track deliverables across remote teams that may comprise vendors, partners, clients, and stakeholders. Collaboration and transparency across remote teams and external stakeholders becomes even more complex and critical.","spans":[]},{"type":"paragraph","text":"This new market dynamic has created space – and demand – for cloud-based productivity and project management tools, and the success of tools like Trello is a testament of a growing market demand. While list-tracking tools have become the mainstay for organizations, privacy and security are at the forefront of deciding which application to go with. If you love Trello but are concerned about security and data privacy, you may want to consider a self-hosted option, which means you install, run, and manage an app by yourself – on your own cloud server.","spans":[]},{"type":"heading3","text":"Enter Restyaboard","spans":[]},{"type":"paragraph","text":"\nRestyaboard is an open-source, enterprise-rich application designed to track and visualize the progress of the project. Using a simple and familiar card-based layout for tasks, users can consolidate boards from Trello, Asana, Github, Kantree.io, Pipefy, Taiga, Taskwarrior, and Wekan into a single pane of glass.","spans":[{"start":178,"end":206,"type":"hyperlink","data":{"link_type":"Web","url":"https://restya.com/board/features"}}]},{"type":"image","url":"https://images.prismic.io/www-static/1761c5969317e0f7a3c4695a2c4921411d9c1f0b_image3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1614,"height":497}},{"type":"paragraph","text":"Other features include calendar syncing, version tracking, two-factor authentication, the ability to define user roles and work offline, and flexible view options to nest comments, expand cards, or color code lists.","spans":[]},{"type":"paragraph","text":"While these are some of the prominent features of the app, there are many use cases for Restyaboard that enhance workplace productivity.","spans":[]},{"type":"heading4","text":"Secure Collaboration for Agencies, Clients & Vendors","spans":[]},{"type":"paragraph","text":"Restyaboard is an extensible platform that can be shared securely to groups of people outside of the organization – agencies, clients, vendors, etc. With security features and role-based privileges, users can easily and securely share links and attachments on projects. Due-date features allow you to implement and modify deadlines, with tasks automatically syncing to Google Calendar. And whether you work autonomously or as part of a team, you can use collaboration features to store ideas, start discussions, and find new approaches to the problems.","spans":[]},{"type":"heading4","text":"Management for Content Strategy, Production & Promotion","spans":[]},{"type":"paragraph","text":"For quick and accurate planning and execution, you can create cards that are color-coded, templatized for faster production, and customized with notification settings. Flexible organization options allow you to create a card for each blog post or batches of articles – or to store everything in a single card. You can also add post topics, keywords, and backlink requirements, and you can keep your content calendar up to date. With Google Docs integration, writers can share the link in the card where stakeholders can add comments directly to the document or cowrite with several writers.","spans":[]},{"type":"heading4","text":"Guided Project Review & Approval Process","spans":[]},{"type":"paragraph","text":"The reviewing features are tailored for content creators and designers alike, allowing tasks to be reviewed by several team members at once and minimizing the likelihood of errors. User can comment on and edit copy, and designers can share multiple mockups for feedback and rounds of revisions. Primary stakeholders can perfect and approve deliverables before they are published or shared with a client.","spans":[]},{"type":"heading4","text":"Streamlined Email Marketing Workflow","spans":[]},{"type":"paragraph","text":"By using a single card for each goal of your email marketing strategy, you can add a mailing list to a single card, the email copy, images to be used, and the newsletter design or code, ensuring all assets are housed in a single place.","spans":[]},{"type":"paragraph","text":"Any project can be templatized and repurposed to speed up creative cycles and maintain brand consistency.","spans":[]},{"type":"heading4","text":"Dynamic & Continuous Feedback Within Your Team","spans":[]},{"type":"paragraph","text":"Once a project is complete, teammates can add feedback to associated project cards. Encourage them to share what went well, what didn’t work, and how processes could be improved going forward. These retrospectives result in continuous improvements and allow for local and distributed teams to provide feedback and contribute to projects, no matter where they are in the world. Moreover, seeing colleagues’ comments can encourage shy people to add their insights to the cause.","spans":[]},{"type":"paragraph","text":"Restyaboard is designed for expansion, and we currently offer several application and widget integrations to help optimize capabilities – including Slack and GSuite. We likely have an integration option for you already, and we're we’re constantly adding new tools to this list.","spans":[{"start":184,"end":210,"type":"hyperlink","data":{"link_type":"Web","url":"https://restya.com/board/integrations/all"}}]},{"type":"paragraph","text":"We hope you give Restyaboard a try! Check out our 1-Click integration on DigitalOcean's Marketplace, and let us know what you think.","spans":[{"start":46,"end":99,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/restyaboard"}}]}],"blog_post_date":"2019-08-06","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"meet-marketplace-partner-restyaboard-self-hosted-kanban-boards"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Betty Candel","author_image":null,"_meta":{"uid":"betty_candel"}},"blog_header_image":null,"blog_headline":[{"type":"heading1","text":"Webinar Recap: An Introduction to Marketplace & 1-Click Apps","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"On Tuesday, May 28, 2019, DigitalOcean hosted a live webinar on our new Marketplace and how to accelerate modern application development with 1-Click Apps. Presenters were Betty Candel, partner enablement and GTM lead at DigitalOcean; Karan M.V, developer relations and community manager for DigitalOcean India; and John Gannon, lead product manager for DigitalOcean Marketplace and ecosystem initiatives.","spans":[]},{"type":"paragraph","text":"The full webinar is now publicly available on YouTube.","spans":[{"start":4,"end":42,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.youtube.com/watch?v=xD-rPg1jT1M&amp;feature=youtu.be"}}]},{"type":"paragraph","text":"Stay tuned for part 2 of our Marketplace series on--details coming soon: \nGetting Started as a Marketplace Vendor – Setting Up Your First 1-Click App.","spans":[{"start":74,"end":149,"type":"em"}]},{"type":"paragraph","text":"Here were the live polling results from the attendees. Your feedback on improvements and enhancements are always appreciated. Please comment below!","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/1cbce716a905bcb2761b14d3ee49f8cb65366e06_screen-shot-2019-05-29-at-1.41.57-pm.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":653,"height":392}},{"type":"image","url":"https://images.prismic.io/www-static/42a37ea9b1a1475107bee6f8e9934c9648c6df51_screen-shot-2019-05-29-at-1.42.09-pm.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":648,"height":265}},{"type":"image","url":"https://images.prismic.io/www-static/852b23e88dd536c5edf92ba38f84c2ec7d046c00_image--1-.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":651,"height":203}},{"type":"paragraph","text":"If you're a Marketplace vendor, these resources may come in handy for you:","spans":[]},{"type":"list-item","text":"DigitalOcean Marketplace","spans":[{"start":0,"end":24,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"list-item","text":"Getting Started as a DigitalOcean Marketplace Vendor","spans":[{"start":0,"end":52,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/vendors/getting-started-as-a-digitalocean-marketplace-vendor"}}]},{"type":"list-item","text":"Using Packer to Create 1-Click Apps","spans":[{"start":0,"end":35,"type":"hyperlink","data":{"link_type":"Web","url":"https://blog.digitalocean.com/using-packer-to-create-a-1-click-nkn-image-on-digitalocean/"}}]},{"type":"list-item","text":"DigitalOcean Marketplace Image Packer Examples","spans":[{"start":0,"end":46,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/digitalocean/marketplace-partners"}}]},{"type":"list-item","text":"Using Packer's DigitalOcean Builder to automate image creation","spans":[{"start":15,"end":35,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io/docs/builders/digitalocean.html"}}]},{"type":"paragraph","text":"If you are building an amazing app and would like to share it with DigitalOcean’s global community of active developers, please submit your idea – we want to hear from you!","spans":[{"start":128,"end":144,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/vendors"}}]}],"blog_post_date":"2019-06-05","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"recap-an-introduction-to-marketplace-1-click-apps"}}},{"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":null,"blog_headline":[{"type":"heading1","text":"Extending Cloud Beyond the Cloud","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"This is a guest post from Kevin Walsh, advisory board member at Vitalpointz.","spans":[{"start":0,"end":76,"type":"em"},{"start":64,"end":75,"type":"hyperlink","data":{"link_type":"Web","url":"http://vitalpointz.io"}}]},{"type":"paragraph","text":"There was a time, not too long ago, when phrases like “private cloud” or “on prem” were forbidden within the offices of cloud providers. The mindset was clearly that everything – and they meant every single thing – ran better as a workload in the cloud.","spans":[{"start":194,"end":212,"type":"em"}]},{"type":"paragraph","text":"And while it certainly is the case that most things run more reliably and less expensively in the cloud, Internet of Things (IoT) technology and use cases have changed the cloud-only mentality (or, at least, changed the meaning of “cloud”). By definition, a workload running on a thing cannot run in the cloud. But also, it clearly must run in conjunction with the cloud. Does the thing connect to the cloud or does the cloud extend to the thing?","spans":[]},{"type":"paragraph","text":"Recent advances in IoT software tools and platforms create an infrastructure in which the traditional boundaries of the cloud are blurred and extended out to potentially millions of endpoints with compute horsepower and massive date generating capacity. This extension of cloud infrastructure beyond the boundaries of the data center is happening at all major cloud providers and portends a rich trove of new applications and use cases that can be engineered by business and industrial users.","spans":[]},{"type":"paragraph","text":"Key to achieving that “cloud extension” is the ability to rapidly establish the foundational underpinnings for a robust IoT infrastructure. This foundation, which Vitalpointz delivers, includes:","spans":[]},{"type":"list-item","text":"Device onboarding and subsequent lifecycle management (including initial connectivity, device authentication, certificate management and periodic over-the-air software and configuration updates)","spans":[]},{"type":"list-item","text":"MQTT & secure-MQTT traffic ingestion and normalization","spans":[]},{"type":"list-item","text":"User authentication","spans":[]},{"type":"list-item","text":"Drag-and-drop flow processing for ingested data streams","spans":[]},{"type":"list-item","text":"Alerts and notifications","spans":[]},{"type":"list-item","text":"Data visualization","spans":[]},{"type":"paragraph","text":"Our vitalpointz IoT Core Lite product allows developers to quickly build IoT infrastructure and onboard devices so that they can focus on application development on top of that infrastructure and integration with disparate enterprise systems.","spans":[]},{"type":"paragraph","text":"We are delighted to share that vitalpointz IoT Core Lite is now available as a 1-Click App in the DigitalOcean Marketplace! We look forward to continuing to stretch and extend the definition of \"cloud\" together.","spans":[{"start":31,"end":122,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/vitalpointz-iot-core-lite"}}]}],"blog_post_date":"2019-05-31","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"extending-cloud-beyond-the-cloud-2"}}},{"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":800,"height":600},"alt":"computer illustration","copyright":null,"url":"https://images.prismic.io/www-static/2406e46c6320ad83e8d9f275de03872e03b759ff_jarvis-company-of-one.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"How to 10X Your Web & WordPress Agency","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"This is a guest post from Lukas Hertig, SVP of Business Development at Plesk.","spans":[{"start":0,"end":77,"type":"em"},{"start":71,"end":76,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.plesk.com/"}}]},{"type":"paragraph","text":"Many agency owners struggle with burnout and profitability, mostly because of one key issue: they offer services on an automated time and material model. These agencies scale only by delivering more hours to customers, making growth cumbersome.","spans":[]},{"type":"paragraph","text":"It doesn’t have to be this way. In this post, you'll explore how to improve your web or WordPress agency's business model.","spans":[]},{"type":"heading2","text":"Leverage the 10X Rule in your Agency","spans":[]},{"type":"paragraph","text":"A cardinal rule in sales is to think big – 10X – for whatever you do in life and business. If you run an agency and are struggling to make it profitable, you can apply this rule to be more successful.","spans":[]},{"type":"paragraph","text":"As an agency owner and entrepreneur, you might want to use this thinking to:","spans":[]},{"type":"list-item","text":"10X the goals you are trying to achieve","spans":[]},{"type":"list-item","text":"10X the number of your clients","spans":[]},{"type":"list-item","text":"10X your income and revenue, especially recurring revenue","spans":[]},{"type":"list-item","text":"Save 10X of your time by automating 10X of your processes","spans":[]},{"type":"list-item","text":"Sleep 10X better because your client websites run on a secure and performant environment","spans":[]},{"type":"heading2","text":"Offer Recurring Value Instead of Time-based Services","spans":[]},{"type":"paragraph","text":"A modern option to help scale your agency by 10X is to implement as many services as possible as recurring services, in a value-based model instead of a time- and material-based model. In this model, you don’t sell your time; you sell results or outcomes.","spans":[]},{"type":"paragraph","text":"Tools such as Plesk and DigitalOcean's agency program have made it easy for smaller agencies to compete against larger firms by offering recurring value.","spans":[{"start":14,"end":19,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/plesk"}},{"start":24,"end":53,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.digitalocean.com/partners/agency-partners/"}}]},{"type":"paragraph","text":"Plesk manages your websites and WordPress instances in a graphical web interface. It’s everything you need to build, secure, and run websites and applications to simplify your life as a web professional.","spans":[]},{"type":"paragraph","text":"Here’s how this might work in practice:","spans":[]},{"type":"paragraph","text":"An auto retailer pays $5 per month for hosting their static website created by a website builder tool years ago. Their website is not optimized for performance, and they are interested in rebuilding it with WordPress and connecting it to a modern technical marketing stack with the goal of selling more cars.","spans":[]},{"type":"paragraph","text":"A traditional pricing model may look something like this: \nActivityCostWebsite development100 hours at $60/hour = $6,000Website content migration50 hours at $60/hour = $3,000Website hosting (shared, non-performant)$5 per monthWebsite monitoring & maintenance2 hours per month at $60/hour = $120 per monthSEO3 hours per month at $60/hour = $180 per monthSocial media marketing (inc ads)8 hours per month at $60/hour = $480 per monthTotal one-time costs$9,000Total monthly costs$785 per month\nLet’s compare that to a customer-centric, value-based approach with the same scope of work.","spans":[{"start":431,"end":451,"type":"strong"},{"start":451,"end":457,"type":"strong"},{"start":457,"end":476,"type":"strong"},{"start":476,"end":490,"type":"strong"}]},{"type":"paragraph","text":"You might begin by asking your customer what the averages sales value is per unit (in this case, a car). Let’s say it’s $25,000. At this price, the retailer may make an average margin of $5,000 per car.","spans":[]},{"type":"paragraph","text":"Therefore, if a website generates three additional sales per month for a customer, that would mean $900,000 additional revenue or $180,000 additional profit annually.","spans":[]},{"type":"paragraph","text":"Let’s say you charge 3% of this additional revenue as a one-time fee and 5% of it for recurring fees, per year. In return, you guarantee a minimum of three additional sales every month.","spans":[]},{"type":"paragraph","text":"Here's what the numbers look like:\nActivityPriceDevelopment fee (3% of AGR)$27,000Annual support fee (5% of AGR)$45,000Total one-time payment$27,000Total monthly payment$3,750 per month\nIncluded with support:","spans":[{"start":119,"end":141,"type":"strong"},{"start":141,"end":148,"type":"strong"},{"start":148,"end":169,"type":"strong"},{"start":169,"end":185,"type":"strong"}]},{"type":"list-item","text":"SEO monitoring and optimization","spans":[]},{"type":"list-item","text":"Ad creation and A/B testing","spans":[]},{"type":"list-item","text":"Social media engagement and management","spans":[]},{"type":"list-item","text":"Monthly reporting on KPIs","spans":[]},{"type":"paragraph","text":"Your one-time revenue just jumped by $18,000, and your recurring revenue increases by $2,965 every month!","spans":[]},{"type":"paragraph","text":"But that's not all:","spans":[]},{"type":"list-item","text":"The customer is happy – they get a guaranteed positive result, and they only spend more when they make more","spans":[]},{"type":"list-item","text":"You are happy – you get more revenue and can automate work, preserving more profit","spans":[]},{"type":"list-item","text":"Most costs for hosting and managing the website do not matter. You can charge whatever you like. Results are what count.","spans":[]},{"type":"paragraph","text":"Results may vary depending on industry and category, but there is almost always a way to create better offerings and maintain a more profitable business using recurring revenue.","spans":[]},{"type":"paragraph","text":"In these situations, your responsibility is to help the customer understand this as a necessary investment and not a cost. You need to demonstrate why your agency is the right team for the work. By focusing on results, you show that you understand the business as the customer does. You are not thinking about hours, as everyone else does.","spans":[]},{"type":"paragraph","text":"It’s crucial that you structure your offering thoughtfully. Here are some basic guidelines:","spans":[]},{"type":"list-item","text":"Target a certain niche with your services. Small and medium-sized businesses offer an armada of sub-niches you could target.","spans":[{"start":0,"end":42,"type":"strong"}]},{"type":"list-item","text":"Include relevant and proper maintenance and support services into your offering that the big agencies can’t.","spans":[{"start":0,"end":108,"type":"strong"}]},{"type":"list-item","text":"Never ever mention hours, days, or other time elements. Remember – you are providing value, not requesting a reimbursement for costs.\nSell your offerings by showing your customer how much value you provide.","spans":[{"start":0,"end":55,"type":"strong"}]},{"type":"list-item","text":"Combine hosting services with other metrics-based services such as social media marketing, content production, SEO, or paid advertising management. When combined with KPIs, reporting, maintenance and ongoing support, you’re delivering extraordinary value to your clients and creating loyal partnerships that will last for years to come.","spans":[{"start":0,"end":147,"type":"strong"}]},{"type":"paragraph","text":"Happy 10X’ing :-)","spans":[]},{"type":"paragraph","text":"Lukas is a born-in-the-cloud international senior business leader, investor, and speaker with 18+ years in IT – including 15+ years in the global cloud and hosting/IT service provider industry. He specializes in scaling tech startups, cloud, web hosting, WordPress, blockchain, entrepreneurship, investing and digital transformation.","spans":[{"start":0,"end":333,"type":"em"}]}],"blog_post_date":"2019-04-17","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"how-to-10x-your-web-wordpress-agency"}}},{"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":800,"height":600},"alt":"computer illustration","copyright":null,"url":"https://images.prismic.io/www-static/2406e46c6320ad83e8d9f275de03872e03b759ff_jarvis-company-of-one.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Catch Paul Jarvis's AMA on Why Intentional Growth Matters","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Writer and entrepreneur Paul Jarvis has advised professional athletes like Steve Nash and Shaquille O’Neal, corporate giants like Microsoft and Mercedes-Benz, and entrepreneurs like Danielle LaPorte, Marie Forleo, and Kris Carr. His latest book, Company of One, explores why a bigger digital business isn’t always better, and he recently shared his knowledge with our DigitalOcean community in an AMA.","spans":[{"start":246,"end":260,"type":"hyperlink","data":{"link_type":"Web","url":"ofone.co"}}]},{"type":"paragraph","text":"He spoke on the importance of “intentional growth,” being that not all growth is good, and should simply be questioned before proceeding. Growing intentionally means, as founders, that we get to determine the type of business we want to run, how we define enough, and what goals should matter.","spans":[]},{"type":"paragraph","text":"You can download the slides from the short talk here or watch the AMA replay here.","spans":[{"start":8,"end":52,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketing.nyc3.cdn.digitaloceanspaces.com/Product-Emails/Marketplace/PaulJarvis-IntentionalGrowth.pdf"}},{"start":56,"end":81,"type":"hyperlink","data":{"link_type":"Web","url":"https://youtu.be/XidEqur_Eyw"}}]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://youtu.be/XidEqur_Eyw"}}]},{"type":"image","url":"https://images.prismic.io/www-static/0a3b765f68cb1420651075391a7ca4b8f368a51c_pauljarvis.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1036,"height":484}}],"blog_post_date":"2019-03-25","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"paul-jarvis-ama"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Shahidh K Muhammed, Hasura","author_image":null,"_meta":{"uid":"shahidh_k_muhammed_hasura"}},"blog_header_image":{"dimensions":{"width":784,"height":418},"alt":null,"copyright":null,"url":"https://images.prismic.io/www-static/481e269f-ebcc-43f8-b5a6-26fbef338309_CodeReview_blog.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Learning GraphQL By Doing","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"In this tutorial, we’ll cover the basic concepts required for app developers to understand GraphQL, with the intention of learning what a GraphQL API looks like—and how it compares to REST-API equivalents—by actually trying it out.","spans":[{"start":91,"end":98,"type":"hyperlink","data":{"link_type":"Web","url":"https://graphql.org/"}}]},{"type":"paragraph","text":"This article is broken into three parts:","spans":[]},{"type":"o-list-item","text":"What is GraphQL and how it works from the client  ","spans":[]},{"type":"o-list-item","text":"Setting up a GraphQL endpoint on Postgres with Hasura and DigitalOcean  ","spans":[]},{"type":"o-list-item","text":"Exploring GraphQL queries (reads), mutations (writes), and subscriptions (real-time)","spans":[]},{"type":"heading3","text":"What is GraphQL?","spans":[]},{"type":"paragraph","text":"GraphQL is a query language and a runtime for executing the queries. Born at Facebook in 2012 and open sourced in 2015, GraphQL represents data not in terms of resource URLs, secondary keys, or join tables; but in terms of a graph of objects and the models that are ultimately used in apps, like NSObjects or JSON. GraphQL is not bound to any data exchange or transport specifications. Typically in an HTTP context, the GraphQL “query string” is POST-ed to the server and the server responds with JSON data.","spans":[]},{"type":"paragraph","text":"There are three key benefits that GraphQL users observe over REST-ful APIs:","spans":[]},{"type":"o-list-item","text":"The speed of building and iterating on the frontend app dramatically increases  ","spans":[]},{"type":"o-list-item","text":"The amount of data sent from the server to client apps reduces, making apps faster and more responsive  ","spans":[]},{"type":"o-list-item","text":"The communication between frontend and backend teams gets streamlined","spans":[]},{"type":"paragraph","text":"Here’s a GIF showing how a GraphQL client can make precise queries to a GraphQL API server to fetch exactly the data it needs and in the “shape” that it wants.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/8a737101-cf78-49d3-9df6-412f239833a1_graphql-api.gif?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":988,"height":784}},{"type":"paragraph","text":"GraphQL adopters see these benefits because:","spans":[]},{"type":"list-item","text":"A GraphQL API allows the frontend developer to easily fetch required data across multiple resources in just a single API call instead of making multiple REST API calls.","spans":[]},{"type":"list-item","text":"A GraphQL API supports subscriptions as a standard part of the spec that makes it easy to consume real-time information on the frontend, without having to bother with setting up complex networking clients manually.","spans":[]},{"type":"list-item","text":"A GraphQL server supports introspection and has a type system that makes it possible to have great tooling for API consumers. Community tools use these introspection APIs to give you everything from auto-complete to API exploration to codegen so that you don’t have to create request/response classes for your APIs!","spans":[]},{"type":"paragraph","text":"The best way of understanding new technology like GraphQL is by trying it out. In this tutorial, we’re going to use Hasura on DigitalOcean to explore GraphQL.","spans":[{"start":116,"end":122,"type":"hyperlink","data":{"link_type":"Web","url":"https://hasura.io/"}}]},{"type":"heading3","text":"Set up a GraphQL Server","spans":[]},{"type":"paragraph","text":"Let’s first set up a GraphQL server and get a hang of it before we deep dive into more GraphQL. We’re going to use the Hasura GraphQL Engine, which is available as a 1-Click application. It is packed with a Postgres database and Caddy web server for easy and automatic HTTPS using Let’s Encrypt.","spans":[{"start":142,"end":185,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/hasura-graphql"}},{"start":229,"end":234,"type":"hyperlink","data":{"link_type":"Web","url":"https://caddyserver.com/"}}]},{"type":"paragraph","text":"[Related: Want more Caddy? Check out the tutorial \"Deploying a Fully-automated Git-based Static Website in Under 5 Minutes\"]","spans":[{"start":0,"end":124,"type":"strong"},{"start":51,"end":122,"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":"Create your Hasura GraphQL Droplet, which installs all the required software and packages itself using Docker. Once the Droplet is ready, head to the Droplet IP on a browser. The Hasura console will open up, and this is where we will interact with the Hasura GraphQL Server to create the schema, test out APIs, manage data etc.","spans":[{"start":12,"end":34,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/hasura?action=deploy&amp;refcode=c4d9092d2c48&amp;utm_source=hasura&amp;utm_campaign=do-blog"}}]},{"type":"image","url":"https://images.prismic.io/www-static/73fc240c-6622-470b-be20-b83c28937d88_graphiql_tab.png?auto=compress,format","alt":"A screenshot of the Hasura console showing the default GraphQL tab where users can try out GraphQL queries","copyright":null,"dimensions":{"width":1266,"height":912}},{"type":"paragraph","text":"A screenshot of the Hasura console showing the default GraphiQL tab where users can try out GraphQL queries.","spans":[{"start":0,"end":108,"type":"em"}]},{"type":"paragraph","text":"This tab where we try out the GraphQL queries is called GraphiQL. It serves as a playground/documentation for the GraphQL server with features like validation, autocomplete, etc.. Throughout this guide, we’ll use GraphiQL to try and test out our queries.","spans":[{"start":56,"end":64,"type":"strong"}]},{"type":"paragraph","text":"There is a public endpoint setup at https://learn-graphql.demo.hasura.app so that users can try out the steps in this tutorial without actually creating a Droplet.","spans":[{"start":36,"end":73,"type":"hyperlink","data":{"link_type":"Web","url":"https://learn-graphql.demo.hasura.app"}}]},{"type":"heading3","text":"Create a table","spans":[]},{"type":"paragraph","text":"Hasura creates the GraphQL schema by looking at Postgres schema. If there is a table called product, there will be a GraphQL type called product. We’ll come back to this again after we’ve made our first GraphQL query.","spans":[]},{"type":"paragraph","text":"Let's create a table in Postgres and get GraphQL on that, all through the Hasura console.","spans":[]},{"type":"paragraph","text":"Navigate to *Data -> Create* table on the console and create a table called `team` with the following columns:","spans":[]},{"type":"paragraph","text":"Column Name         Type","spans":[]},{"type":"paragraph","text":"id                              Integer","spans":[]},{"type":"paragraph","text":"(auto-increment)    nameText","spans":[]},{"type":"paragraph","text":"Other inputs and checkboxes like default value, nullable and, unique can be ignored. Choose `id` as the Primary Key and click the *Create* button.","spans":[]},{"type":"paragraph","text":"","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/b7927157-1a2e-433c-9157-aad62c73e40b_create_team_table.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":834}},{"type":"paragraph","text":"Screenshot of console showing the create table screen with details filled in.","spans":[{"start":0,"end":77,"type":"em"}]},{"type":"heading3","text":"Insert sample data","spans":[]},{"type":"paragraph","text":"Once the table is created, you will be taken to the table's dashboard. Let's go to the *Insert Row* tab and add some rows. The ```[php]{`id`}``` column will be disabled as it is going to be auto-generated. You will only be able to enter the name. Once you enter one name, say Avengers, click the Save button. You will see a notification on the top right corner saying that the row has been inserted! ","spans":[{"start":296,"end":300,"type":"em"}]},{"type":"paragraph","text":"The text will remain in the name input box and the button will change to Insert Again from Save. Edit the name input box to add the next team, Justice League, and click the Insert Again button.","spans":[{"start":73,"end":85,"type":"em"},{"start":91,"end":95,"type":"em"},{"start":173,"end":185,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/98025e01-5ddf-415d-bc54-276eaac7214a_insert_team.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":428}},{"type":"paragraph","text":"Inserting rows into the table.","spans":[{"start":0,"end":30,"type":"em"}]},{"type":"paragraph","text":"Now if we go to Browse Rows tab, we can see that we have inserted two teams with name Avengers and Justice League, for which the ```[php]{`id`}``` has been auto-generated. Avengers got ```[php]{`id`}``` 1 and Justice League got ```[php]{`id`}``` 2.","spans":[{"start":16,"end":27,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/6b1c8cfb-ee7e-47c4-95e9-76aa18d8b894_browse_rows_team.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":584}},{"type":"paragraph","text":"Team table showing two rows.","spans":[{"start":0,"end":28,"type":"em"}]},{"type":"paragraph","text":"This is standard Postgres stuff. Let's jump into GraphQL now.","spans":[]},{"type":"heading3","text":"Try out GraphQL","spans":[]},{"type":"paragraph","text":"Switch to the GraphiQL tab by clicking the link on the top of the console. You will see a URL on top, which is the GraphQL API endpoint; a section to add HTTP headers; and, then the GraphiQL screen split into left and right sides. We enter the GraphQL query on the left side editor and then click the *Play* button on top and the API response appears on the right side.","spans":[]},{"type":"paragraph","text":"Copy the following GraphQL query and paste into the left side of GraphiQL. Now, click on the button with *Play* icon on it. This will send the query to the server and show the response on the right side.","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    query {  ","spans":[]},{"type":"paragraph","text":"     team {","spans":[]},{"type":"paragraph","text":"       id","spans":[]},{"type":"paragraph","text":"       name","spans":[]},{"type":"paragraph","text":"     }","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"The response will appear on the right.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/dc8f5787-e7ee-4fec-83b7-fd2ca5445ceb_team_query.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":812}},{"type":"paragraph","text":"Executing the query to get all teams.","spans":[{"start":0,"end":37,"type":"em"}]},{"type":"paragraph","text":"Woohoo! You have made your first GraphQL query on Hasura. The query above is asking the server to give information on all team objects along with the ```[php]{`id`}``` and ```[php]{`name`}``` columns.","spans":[]},{"type":"heading3","text":"GraphQL Query","spans":[]},{"type":"paragraph","text":"A GraphQL query is a string that is sent to a server to be interpreted and fulfilled, which then returns JSON back to the client. It is designed for developers of web/mobile apps (HTTP clients) to be able to make API calls to fetch the data they need from their backend APIs in a more convenient way.","spans":[]},{"type":"paragraph","text":"Let’s take a closer look at what that means. Let's say you have an API to fetch all teams. This is a typical way to do it with REST: (Request is shown on the left and response on the right).","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/89529091-c86f-4caf-a946-98cde2d1f67f_code_table_1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":996,"height":280}},{"type":"paragraph","text":"But, with a GraphQL API, the request would look like this:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/2f638848-2ca2-4d24-808e-1e7778d7c703_code_table_2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":994,"height":349}},{"type":"paragraph","text":"This is what GraphiQL is doing under the hood. It is taking the query string; wrapping it in a JSON object, with a top-level query key; and, POST-ing it to the server. All GraphQL client libraries takes care of this and, as a user, we will only have to deal with the query string.","spans":[]},{"type":"paragraph","text":"Instead of GET-ing a resource at a URL qualified by the resource, we POST a GraphQL query to a single endpoint. The query defines the resource we want with any parameter of subresources. It is not JSON, but a string representation of the data we need, wrapped in a JSON body. The server responds with a JSON in the same shape of the data we requested: there is ```[php]{`team`}``` key at top level, then ```[php]{`id`}``` and ```[php]{`name`}``` nested inside.","spans":[]},{"type":"heading3","text":"Types of queries","spans":[]},{"type":"paragraph","text":"There are three different types of GraphQL queries.","spans":[]},{"type":"o-list-item","text":"Query  ","spans":[]},{"type":"o-list-item","text":"Mutation  ","spans":[]},{"type":"o-list-item","text":"Subscription","spans":[]},{"type":"paragraph","text":"Queries are typically used to fetch data, mutations for writing data and subscriptions for fetching real-time data. All three of them are collectively called “queries” again.","spans":[]},{"type":"heading3","text":"Parts of a query","spans":[]},{"type":"paragraph","text":"Each GraphQL query, whether it is a query, mutation, or subscription, fundamentally contains fields and arguments. ","spans":[]},{"type":"heading4","text":"Fields","spans":[]},{"type":"paragraph","text":"A field simply indicate that we are asking the server for that particular info. For example, in the following query that we ran earlier:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/23b1c52d-b779-4099-9448-c1e12f09b63c_fields.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1002,"height":288}},{"type":"paragraph","text":"We ask the server for the field ```[php]{`team`}``` and its subfields ```[php]{`id`}``` and ```[php]{`name`}```, and the server returns data in the same shape as we asked for. ","spans":[]},{"type":"heading4","text":"Arguments","spans":[]},{"type":"paragraph","text":"In something like REST, we could only pass a single set of argument - as query parameters and URL segments. For example, to get a particular profile, a typical REST call would look like the following:","spans":[]},{"type":"image","url":"https://prismic-io.s3.amazonaws.com/www-static/50169033-6c0d-4d24-81d1-c1dea4ff5b41_argument_1.png","alt":null,"copyright":null,"dimensions":{"width":1003,"height":129}},{"type":"paragraph","text":"Or like this:","spans":[]},{"type":"image","url":"https://prismic-io.s3.amazonaws.com/www-static/b1df8108-4511-44aa-9ae4-5d801c763829_argument_2.png","alt":null,"copyright":null,"dimensions":{"width":998,"height":137}},{"type":"paragraph","text":"With GraphQL, every field can take any arbitrary number of arguments as defined by the schema. The REST API above would look like this with GraphQL:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/efcd3838-d8d0-4e45-9246-59260243ef8c_argument_3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":996,"height":219}},{"type":"paragraph","text":"Try this out in GraphiQL.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/9e140e9d-5a6e-4ae5-b29d-c13f043202ac_team_where_query.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":747}},{"type":"paragraph","text":"A query using the where argument.","spans":[{"start":0,"end":33,"type":"em"}]},{"type":"paragraph","text":"Similarly, there are many other arguments available for queries, like ```[php]{`order_by`}```, ```[php]{`limit`}```, ```[php]{`offset`}``` etc. You can try them out in GraphiQL: use Ctrl/Cmd+Space to open up the auto-complete suggestions.","spans":[]},{"type":"heading3","text":"Query variables","spans":[]},{"type":"paragraph","text":"But how do we parametrize this? Since the query is a string, a naive way would be to implement string interpolation. But this can introduce bugs and is not efficient.","spans":[]},{"type":"paragraph","text":"Any GraphQL query can be parametrized by defining variables. Variables can be used in the query string and the values of those variables can be sent in a separate object. This makes it easy to re-use queries by using different set of variables instead of something like string interpolation. Variables need to be defined before they can be used and while defining the type also need to be provided. ","spans":[]},{"type":"paragraph","text":"Here is an example:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/c8d5f781-31f5-4cae-b01d-572913f3278a_query.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":995,"height":307}},{"type":"paragraph","text":"The first column contains the query, the column below that contains the variables object, and the right column contains the response, as represented in GraphiQL. Try this query out and see the response:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/2d6f52f5-de77-4d8b-814e-4127ca5926a0_team_variable_query.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":809}},{"type":"paragraph","text":"A query along with variables.","spans":[{"start":0,"end":29,"type":"em"}]},{"type":"paragraph","text":"Here we are defining a variable called ```[php]{`id`}```, as denoted by the prefix ```[php]{`$`}``` and it is of type ```[php]{`Int`}```. The ```[php]{`!`}``` indicates that this variable is mandatory for this query to be executed. The client typically send the variables in a JSON object with key ```[php]{`variables`}``` in the JSON POST body along with the ```[php]{`query`}``` string.","spans":[]},{"type":"paragraph","text":"The POST body would look something like this:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/9399d71a-5308-4790-b6dd-04711cc60253_query_2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":979,"height":172}},{"type":"paragraph","text":"Now, the query string need only be defined once in your application and for various ```[php]{`id`}```s, the variables can be changed as required.","spans":[]},{"type":"heading3","text":"Query","spans":[]},{"type":"paragraph","text":"A query is a GraphQL query typically used to fetch data. All the examples we saw earlier are \"queries.\" For example, when we get the team with ```[php]{`id`}``` 2:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/c5f35492-6e4b-4aa1-a463-078825b971ba_query_3.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":993,"height":217}},{"type":"heading3","text":"Multiple root nodes","spans":[]},{"type":"paragraph","text":"Let's take a look at how we can query multiple tables (or nodes) in the same query. For that, let us create a new table first.","spans":[]},{"type":"paragraph","text":"Go back to the Data tab and create a new table called ```[php]{`superhero`}``` with the following columns:","spans":[{"start":15,"end":19,"type":"em"}]},{"type":"paragraph","text":"Column Name      Type","spans":[]},{"type":"paragraph","text":"id                            Integer","spans":[]},{"type":"paragraph","text":"(auto-increment)   nameText","spans":[]},{"type":"paragraph","text":"team_id                   Integer","spans":[]},{"type":"paragraph","text":"Other inputs and checkboxes like default value, nullable, and unique can be ignored. Choose ```[php]{`id`}``` as the Primary Key and click the Create button.","spans":[{"start":143,"end":149,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/57158fac-75d0-43e1-ae2a-85f18b57b2b4_superhero_table.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":875}},{"type":"paragraph","text":"Creating the superhero table.","spans":[{"start":0,"end":29,"type":"em"}]},{"type":"paragraph","text":"Once the table is created, you'll be taken to the Modify tab. Let's switch to Insert Rows tab and insert some rows like we did last time.","spans":[{"start":50,"end":56,"type":"em"},{"start":78,"end":89,"type":"em"}]},{"type":"paragraph","text":"The ```[php]{`id`}``` column will be disabled like last time, since it will be auto-generated by Postgres. Let's enter the ```[php]{`name`}``` and ```[php]{`team_id`}``` for each super hero. If we scroll back, ```[php]{`team_id`}``` is 1 for Avengers and 2 for Justice League.","spans":[]},{"type":"paragraph","text":"name                     team_id","spans":[]},{"type":"paragraph","text":"Captain America  1","spans":[]},{"type":"paragraph","text":"Black Widow        1","spans":[]},{"type":"paragraph","text":"Batman                 2","spans":[]},{"type":"paragraph","text":"Wonder Woman   2","spans":[]},{"type":"paragraph","text":"Once we add the data, using GraphQL, we can fetch data from multiple nodes (tables) in the same query. Copy the following query into GraphiQL and hit the Play button.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/f012daeb-3f0d-4ad9-8019-7b3a97aa264e_query_4.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":995,"height":679}},{"type":"image","url":"https://images.prismic.io/www-static/31324400-3d0f-4426-a45c-23105e3d9f27_multi-nodes.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":1098}},{"type":"paragraph","text":"Using multiple nodes in the same GraphQL query.","spans":[{"start":0,"end":47,"type":"em"}]},{"type":"paragraph","text":"Similarly how many ever such top-level nodes can be included in a single query. This applies to mutations too, but subscription spec limits subscriptions to contain only one top-level node.","spans":[]},{"type":"heading3","text":"Querying related data","spans":[]},{"type":"paragraph","text":"For data that are related and are in multiple tables, we can define relationships over the columns and query them in a nested format. For e.g. let us see how we can query for the team and get all the superheroes in that team.","spans":[]},{"type":"paragraph","text":"In order to do this, we need to create a relationship between ```[php]{`team`}``` and ```[php]{`superhero`}``` tables. We already know that ```[php]{`team_id`}``` is our link. In relational database modelling, a foreign key constraint is added on this column to indicate the values in this columns should also be present as the ```[php]{`id`}``` column in ```[php]{`team`}``` table. Let's first add that constraint. Note that this is totally optional and will only act as a constraint, as a foreign key is not required to create a relationship. ","spans":[]},{"type":"paragraph","text":"Head to the Data tab, click on the ```[php]{`superhero`}``` table on the left and then go to the Modify tab. On this tab, click on the Edit button next to ```[php]{`team_id`}``` column. This will open up certain properties of that column. The last one among them will be a Foreign Key. Tick the checkbox there and then choose ```[php]{`team`}``` as the reference table and ```[php]{`id`}``` as the reference column. Then click the Save button.","spans":[{"start":12,"end":16,"type":"em"},{"start":97,"end":103,"type":"em"},{"start":135,"end":139,"type":"em"},{"start":431,"end":435,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/8f54f674-f23f-431a-a58d-5836e3f77ed8_add-fk-team-id.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":1012}},{"type":"paragraph","text":"Creating a Foreign Key constraint on team_id column","spans":[{"start":0,"end":51,"type":"em"}]},{"type":"paragraph","text":"Once the save has happened, a notification appears on the top right saying the constraint is created.","spans":[]},{"type":"paragraph","text":"Now, head to the ```[php]{`team`}``` table by clicking on the left sidebar. Go to the Relationships tab and you'll see a new array relationship suggestion.","spans":[{"start":86,"end":99,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/3babf3f7-7f01-4bf0-acc4-a2ad40d003a8_team-array-rel-suggestion.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":605}},{"type":"paragraph","text":"Array relationship suggestion on the team table","spans":[{"start":0,"end":47,"type":"em"}]},{"type":"paragraph","text":"Click on the Add button here. A name will be auto-filled for the relationship, let's change it to just ```[php]{`superheroes`}``` and click the Save button.","spans":[{"start":13,"end":16,"type":"em"},{"start":144,"end":148,"type":"em"}]},{"type":"image","url":"https://images.prismic.io/www-static/146153ac-de50-419b-b905-482fdb8206fb_team-array-rel-save.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1268,"height":617}},{"type":"paragraph","text":"Entering the array relationship name as superheroes","spans":[{"start":0,"end":51,"type":"em"}]},{"type":"paragraph","text":"You'll see a notification on the top right saying the relationship is saved.","spans":[]},{"type":"paragraph","text":"Now, head back to GraphiQL by clicking on the top bar and copy paste the following query and click *Play* button to execute.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/706216b8-225f-41de-8dee-ff426f333037_query_5.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":992,"height":475}},{"type":"image","url":"https://images.prismic.io/www-static/e78d2dd4-50e1-4c3c-b257-bc724df2edf5_related-query.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":1012}},{"type":"paragraph","text":"Querying related data","spans":[{"start":0,"end":21,"type":"em"}]},{"type":"paragraph","text":"One query can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several round trips as one would need in a classic REST architecture.","spans":[]},{"type":"heading3","text":"Mutation","spans":[]},{"type":"paragraph","text":"In GraphQL realm, a mutation is a type of query that typically mutates data, like database insert/update/deletes. REST equivalents would be PUT/POST/DELETE requests. Let's say I need to add a new ```[php]{`superhero`}``` to my database. A REST request would look like the following:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/d06278d9-5850-48c2-8a2c-606eb58856c4_Post_1.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":994,"height":192}},{"type":"paragraph","text":"Now, let's take a look at the GraphQL equivalent:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/3708965e-a4e2-464c-a299-0551b9644513_Post_2.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":994,"height":323}},{"type":"paragraph","text":"We can also have the name as a variable so that we do not have to manipulate strings to insert different Avengers.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/efbc3ddf-bd3f-49d6-a35f-43ddcd034364_mutation.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":993,"height":452}},{"type":"paragraph","text":"Like we mentioned before, the first column is the query, the one on the bottom are the variables, and the right columns shows us the response.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/4ab28403-6564-4f31-9fa3-f7a8afd3d098_inser-hero-mutation.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1256,"height":927}},{"type":"paragraph","text":"A mutation inserting a new superhero","spans":[{"start":0,"end":36,"type":"em"}]},{"type":"paragraph","text":"Like queries, we can mix multiple mutations in the same request. You can also insert related data in a single mutation.","spans":[]},{"type":"heading3","text":"Subscription","spans":[]},{"type":"paragraph","text":"Subscriptions provide real-time capabilities in GraphQL. Traditionally there are two options to obtain live data on the client side. One is calling the REST endpoint at regular intervals of time till we get the desired state (also called polling) and the other is to push an event to the client from the server using websockets.","spans":[]},{"type":"paragraph","text":"Polling is a highly inefficient process and implementing websockets are a nightmare. GraphQL subscriptions also works over websockets, but GraphQL community has standardised what the language the websocket server and client should speak. Because of that, all clients and servers are compatible with each other and the integration is seamless.","spans":[]},{"type":"paragraph","text":"Any query can be converted into a subscription with Hasura. For example, the query to get all Avengers can be made into a subscription like this, and if a web component is rendered (say React) using this data, the component re-renders when a new item gets added. This can be experimented on easily by opening two GraphiQL windows: running a subscription on one window, while try making a mutation on the other one.","spans":[]},{"type":"paragraph","text":"The subscription on the left column, when copied onto GraphiQL and executed, is asking the server to send changes on the ```[php]{`team`}``` table, particularly for the one with ```[php]{`id`}``` equals 1, as and when they happen. The mutation on the right, when executed, will insert a new superhero to the ```[php]{`superhero`}``` table. Since the two tables are related through ```[php]{`team_id`}```, Hasura notices that there is a new superhero that has been added to team 1 and pushed that information onto the client. The result of the subscription updates automatically—all in real-time.","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/a69795d8-deb3-4339-a1a7-3281651ab42d_subscription.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1000,"height":325}},{"type":"image","url":"https://images.prismic.io/www-static/fc71f1c3-59f2-4de5-9fc2-43cefb109399_Peek-2019-02-19-18-16.gif?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":812}},{"type":"paragraph","text":"A GIF showing subscriptions","spans":[{"start":0,"end":27,"type":"em"}]},{"type":"paragraph","text":"Under the hood, the GraphQL client and the server communicate over websockets and messages are transferred without any extra involvement from the user. When used in a application, the client library takes care of all the plumbing for the user.","spans":[]},{"type":"heading3","text":"The GraphQL Schema","spans":[]},{"type":"paragraph","text":"Documenting APIs is very important for team collaboration and feature velocity. The backend team works on the API, documents it, and passes it down to the frontend team. But, by the time the frontend team gets to use the APIs, the documentation could be out of date, missing, or plain wrong, since a separate human process is required for maintaining docs.","spans":[]},{"type":"paragraph","text":"GraphQL attempts to address this issues by introducing a type system for the API: the GraphQL schema. It talks about what fields the client can query, whether it is an integer or a string, what are the parameters for this mutation, is this parameter required or not etc.","spans":[]},{"type":"paragraph","text":"In fact, you could make a GraphQL query to get this schema. This is called introspection, where the server publishes its own schema and clients can query it when required. This opens up lot of possibilities, including type safety for your API. Clients can look at this schema and generate code required for making idiomatic type safe API calls and errors could be caught at compile time itself instead of at runtime (for clients written in typed languages). IDEs can exploit this and show live feedback like autocomplete and validation on the queries written by the developer.","spans":[]},{"type":"paragraph","text":"Try out the following query:","spans":[]},{"type":"image","url":"https://images.prismic.io/www-static/28780361-d364-430e-b833-29948f61228d_query_6.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":897,"height":853}},{"type":"paragraph","text":"There is a *Docs* button on the top right corner of GraphiQL tab where you can browse this schema. It is helpful in figuring out what are the queries/mutations/subscriptions supported by the server and what their arguments and return types are.","spans":[]},{"type":"heading3","text":"Conclusion","spans":[]},{"type":"paragraph","text":"In this tutorial, we learned about GraphQL by setting up a GraphQL server over Postgres and by trying out some GraphQL queries, mutations, and subscriptions. We have gone through the types of GraphQL queries and the different parts within a query. We also walked though how a GraphQL schema helps building intelligent tools for the ecosystem, which increases developer productivity.","spans":[]},{"type":"paragraph","text":"As next steps, you'd want to integrate GraphQL with your application, including authentication and authorization. Check out the resources below for further reading.","spans":[]},{"type":"heading3","text":"Resources","spans":[]},{"type":"list-item","text":"Building your schema","spans":[{"start":0,"end":20,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/schema/index.html"}}]},{"type":"list-item","text":"GraphQL Queries","spans":[{"start":0,"end":15,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/queries/index.html"}}]},{"type":"list-item","text":"GraphQL Mutations","spans":[{"start":0,"end":17,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/mutations/index.html"}}]},{"type":"list-item","text":"GraphQL Subscriptions","spans":[{"start":0,"end":21,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/subscriptions/index.html"}}]},{"type":"list-item","text":"Event Triggers","spans":[{"start":0,"end":14,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/event-triggers/index.html"}}]},{"type":"list-item","text":"Remote Schemas","spans":[{"start":0,"end":14,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/remote-schemas/index.html"}}]},{"type":"list-item","text":"Authentication/Access control","spans":[{"start":0,"end":29,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/event-triggers/index.html"}}]},{"type":"list-item","text":"Database Migrations","spans":[{"start":0,"end":19,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/migrations/index.html"}}]},{"type":"list-item","text":"Hasura GraphQL Guides","spans":[{"start":0,"end":21,"type":"hyperlink","data":{"link_type":"Web","url":"https://docs.hasura.io/1.0/graphql/manual/guides/index.html"}}]},{"type":"heading3","text":"Community and Support","spans":[]},{"type":"paragraph","text":"If you have any questions regarding this tutorial or using Hasura, please reach out to the Hasura community:","spans":[]},{"type":"list-item","text":"Github","spans":[{"start":0,"end":6,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/hasura/graphql-engine"}}]},{"type":"list-item","text":"Discord (community forum)","spans":[{"start":0,"end":25,"type":"hyperlink","data":{"link_type":"Web","url":"https://discord.gg/hasura"}}]},{"type":"list-item","text":"Twitter","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://twitter.com/hasurahq"}}]}],"blog_post_date":"2019-03-12","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"learning-graphql-by-doing"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Yilun Zhang, NKN","author_image":null,"_meta":{"uid":"yilun_zhang_nkn"}},"blog_header_image":{"dimensions":{"width":800,"height":600},"alt":"api token","copyright":null,"url":"https://images.prismic.io/www-static/1e6268d6-b745-4db1-be2b-ebe796066d37_freebsd_install_dribbble.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Using Packer to Create a 1-Click NKN Image on DigitalOcean","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"NKN is the new kind of P2P network connectivity protocol and ecosystem powered by a novel public blockchain. Our open source node software allows internet users to share network connections and unused bandwidth for rewards. By running an NKN node, you become part of the NKN network community helping to build the decentralized internet so everyone can enjoy secure, low cost, and universally accessible connectivity.","spans":[{"start":0,"end":3,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.nkn.org/#/"}}]},{"type":"paragraph","text":"Recently NKN’s Testnet has grown to over 6,000 community full nodes and many of these nodes are running on DigitalOcean. In fact, one of the most frequently asked questions from our community is how to set up a NKN full node on DigitalOcean? We have a few tutorials on how to do that; however, having to properly and safely set up the Droplet environment creates significant friction for those who have just heard of the project and want to try it for themselves, preferably in just a few minutes.","spans":[{"start":15,"end":22,"type":"hyperlink","data":{"link_type":"Web","url":"https://testnet.nkn.org/"}}]},{"type":"paragraph","text":"Luckily for all, DigitalOcean now provides a Marketplace where we can release a 1-Click App for NKN, a very convenient and user-friendly way to deploy preconfigured droplets in 60 seconds. You can quickly start a NKN 1-Click App from the \"Marketplace\" in your DigitalOcean cloud control panel, or by visiting DigitalOcean Marketplace directly.","spans":[{"start":78,"end":99,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/nkn-full-node"}},{"start":322,"end":333,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com"}}]},{"type":"image","url":"https://images.prismic.io/www-static/29fcdc77-d002-414a-b122-cccfbec7641e_NKN+blog+post+-+DigitalOcean+Marketplace+2019-03-07+10-41-16.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":2762,"height":1786}},{"type":"heading3","text":"Using Packer to Create a 1-Click Image","spans":[]},{"type":"paragraph","text":"To make the NKN 1-Click App in DigitalOcean's new Marketplace, we recognized that creating a preconfigured production-ready image takes effort: not just to create the initial image, but every time that image needs to be updated. A few repetitive but necessary steps need to be done every time a snapshot is created, e.g. starting and stopping Droplets, removing user history and trace, creating a snapshot from Droplets. Doing these steps manually for each image update is not only time consuming, but can open the opportunity for mistakes. ","spans":[]},{"type":"paragraph","text":"Another headache comes when we want to create the same image for multiple cloud providers. Typically each cloud providers has their own steps and requirements when creating an image. For example, DigitalOcean requires an image to have `cloud-init` set up, while Google Cloud requires an image to be licensed. These differences need to be taken into account every time an image is updated.","spans":[]},{"type":"paragraph","text":"Since creating and maintaining an app image is repetitive and has a lot of detailed steps that easily break when doing them manually, we chose to handle the process using Packer, which can automate all these processes across many different cloud providers.","spans":[{"start":171,"end":177,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io"}}]},{"type":"paragraph","text":"With Packer, you provide the configuration scripts and your API key for your cloud provider, and Packer will do the rest. The result is a ready-to-use image for each cloud provider. Most importantly, everything is automatic, which means it's reproducible and can be part of continuous integration. Let's dig a little bit deeper to see how to do it in detail.","spans":[]},{"type":"paragraph","text":"There are quite a few ways to install Packer as you can see in the official documentation. The simplest way is to use the precompiled binary.  ","spans":[{"start":67,"end":89,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io/intro/getting-started/install.html"}},{"start":122,"end":140,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io/downloads.html"}}]},{"type":"heading3","text":"Obtaining a DigitalOcean API Token","spans":[]},{"type":"paragraph","text":"To use Packer with DigitalOcean, we first need to obtain (or generate) a DigitalOcean API Token. This can be done on the DigitalOcean website:","spans":[{"start":121,"end":141,"type":"hyperlink","data":{"link_type":"Web","url":"https://cloud.digitalocean.com/account/api/tokens"}}]},{"type":"image","url":"https://images.prismic.io/www-static/078cf34e-59bd-4cee-8ae7-e85313ff016c_api_token.png?auto=compress,format","alt":null,"copyright":null,"dimensions":{"width":1600,"height":526}},{"type":"paragraph","text":"Note that both read and write scope are needed for the API token.","spans":[]},{"type":"heading3","text":"DigitalOcean Packer Builder","spans":[]},{"type":"paragraph","text":"A builder in Packer will create an image on a certain platform from some configuration, and DigitalOcean is one of the supported builders. A basic Packer example which creates a plain Ubuntu image on DigitalOcean is as simple as the following Packer template:  ","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"      \"builders\": [","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"digitalocean\",","spans":[]},{"type":"paragraph","text":"          \"api_token\": \"Your DigitalOcean API Token\",","spans":[]},{"type":"paragraph","text":"          \"image\": \"ubuntu-18-04-x64\",","spans":[]},{"type":"paragraph","text":"          \"region\": \"nyc3\",","spans":[]},{"type":"paragraph","text":"          \"size\": \"s-1vcpu-1gb\",","spans":[]},{"type":"paragraph","text":"          \"ssh_username\": \"root\"","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"      ]","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"If you save the above Packer template as ```[php]{`packer.json`}```, include your DigitalOcean API token and run ```[php]{`packer build packer.json`}```, you will get a working Ubuntu image in your DigitalOcean account in a couple minutes!","spans":[]},{"type":"paragraph","text":"If we look at the template above, the ```[php]{`image`}``` key specifies the basic OS image to use; the ```[php]{`region`}``` and ```[php]{`size`}``` are used for the temporary droplet packer creates (and will destroy automatically); and, the ```[php]{`ssh_username`}``` key specifies which user Packer will use to ssh into the machine.","spans":[]},{"type":"paragraph","text":"You probably noticed that builders in the Packer template are an array, so if we want to build the same image on multiple cloud providers, we just need to add builders into the array, and all of them will be built in parallel.","spans":[]},{"type":"paragraph","text":"Packer supports quite a lot of builders. More information about Packer builder can be found here.","spans":[{"start":92,"end":96,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io/docs/builders/index.html"}}]},{"type":"heading3","text":"Provisioning the Image","spans":[]},{"type":"paragraph","text":"The above basic example only gives you a standard Ubuntu image, which is probably not what you want. To build a custom image, we need provisioners that customize an image by installing and configuring software before taking the Snapshot and turning it into an image. Let's add provisioners to the Packer template in the previous step:","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"      \"builders\": [","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"digitalocean\",","spans":[]},{"type":"paragraph","text":"          \"api_token\": \"Your DigitalOcean API Token\",","spans":[]},{"type":"paragraph","text":"          \"image\": \"ubuntu-18-04-x64\",","spans":[]},{"type":"paragraph","text":"          \"region\": \"nyc3\",","spans":[]},{"type":"paragraph","text":"          \"size\": \"s-1vcpu-1gb\",","spans":[]},{"type":"paragraph","text":"          \"ssh_username\": \"root\"","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"      ],","spans":[]},{"type":"paragraph","text":"      \"provisioners\": [","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"shell\",","spans":[]},{"type":"paragraph","text":"          \"inline\": [","spans":[]},{"type":"paragraph","text":"            \"sleep 30\",","spans":[]},{"type":"paragraph","text":"            \"sudo apt-get update\",","spans":[]},{"type":"paragraph","text":"            \"sudo apt-get install -y supervisor\"","spans":[]},{"type":"paragraph","text":"          ]","spans":[]},{"type":"paragraph","text":"        },","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"shell\",","spans":[]},{"type":"paragraph","text":"          \"script\": \"./scripts.sh\"","spans":[]},{"type":"paragraph","text":"        },","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"file\",","spans":[]},{"type":"paragraph","text":"          \"source\": \"./nkn.conf\",","spans":[]},{"type":"paragraph","text":"          \"destination\": \"/tmp/\"","spans":[]},{"type":"paragraph","text":"        },","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"shell\",","spans":[]},{"type":"paragraph","text":"          \"inline\": [","spans":[]},{"type":"paragraph","text":"            \"sudo mv /tmp/nkn.conf /etc/supervisor/conf.d/\"","spans":[]},{"type":"paragraph","text":"          ]","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"      ]","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"We added quite a few provisioners to the Packer template, and they will be executed sequentially. Let's look at them one by one.","spans":[]},{"type":"paragraph","text":"The first provisioner is an inline shell type, which contains a few commands to run on the droplet before creating the image. Note that the first command ```[php]{`sleep 30`}``` makes sure the system is properly initialized before executing the rest of the command.","spans":[]},{"type":"paragraph","text":"The second provisioner is a shell script type. It's very similar to the first one but specifies a shell script file to run on the droplet.","spans":[]},{"type":"paragraph","text":"The third provisioner uploads a local file to the droplet, and the fourth provisioner copies the uploaded file into the designated location. One can also upload it directly to the target location which works properly on DigitalOcean but not necessarily on other cloud platforms if ```[php]{`ssh_username`}``` does not have the required permissions.","spans":[]},{"type":"paragraph","text":"You can find more information about supported provisioners in the official documentation.","spans":[{"start":66,"end":88,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.packer.io/docs/provisioners/index.html"}}]},{"type":"heading3","text":"Passing Variables to the Packer Template","spans":[]},{"type":"paragraph","text":"Currently, the Packer template contains your DigitalOcean API token in plain text, which is a very dangerous thing to do if you share it with other people. A much better way is to pass the API token as a variable to the template. Do this by modifying the Packer template (“provisioners” section does not need to be changed):","spans":[]},{"type":"preformatted","text":"```[php]{`","spans":[]},{"type":"paragraph","text":"    {","spans":[]},{"type":"paragraph","text":"      \"variables\": {","spans":[]},{"type":"paragraph","text":"        \"do_api_token\": \"\"","spans":[]},{"type":"paragraph","text":"      },","spans":[]},{"type":"paragraph","text":"      \"builders\": [","spans":[]},{"type":"paragraph","text":"        {","spans":[]},{"type":"paragraph","text":"          \"type\": \"digitalocean\",","spans":[]},{"type":"paragraph","text":"          \"api_token\": \"{{user `do_api_token`}}\",","spans":[]},{"type":"paragraph","text":"          \"image\": \"ubuntu-18-04-x64\",","spans":[]},{"type":"paragraph","text":"          \"region\": \"nyc3\",","spans":[]},{"type":"paragraph","text":"          \"size\": \"s-1vcpu-1gb\",","spans":[]},{"type":"paragraph","text":"          \"ssh_username\": \"root\"","spans":[]},{"type":"paragraph","text":"        }","spans":[]},{"type":"paragraph","text":"      ],","spans":[]},{"type":"paragraph","text":"      \"provisioners\": [...]","spans":[]},{"type":"paragraph","text":"    }","spans":[]},{"type":"preformatted","text":"`}```","spans":[]},{"type":"paragraph","text":"To build the image, use the command ```[php]{`packer build -var 'do_api_token=XXX' packer.json`}``` where XXX should be replaced by your DigitalOcean API token. Using the variable is not only safer, but also makes it possible to create the same image in different DigitalOcean accounts with the same template.","spans":[]},{"type":"heading3","text":"A Few Final Tips","spans":[]},{"type":"paragraph","text":"Here are some tips based on our experience in creating a multi-platform images using Packer:","spans":[]},{"type":"o-list-item","text":"Updating an image is typically more complicated than updating the code, so it's better to create your image such that it will fetch the latest version of your software when the Droplet is initiated such that the image itself does not need to be updated every time your software updates.","spans":[]},{"type":"o-list-item","text":"For the same reason as above, it's useful to have some sort of auto-update mechanism so that existing one-click users will not need to update or even re-create the Droplet manually.","spans":[]},{"type":"o-list-item","text":"All Droplets created from the same image are nearly identical on DigitalOcean, so if you have any sort of \"account\" in your software, the credentials may need to be generated randomly and saved to disk so users can access it. In our case we also set the credential file permission so that only a certain user (plus superusers) can view the content of the file.","spans":[]},{"type":"o-list-item","text":"Do not assume ```[php]{`ssh_username`}``` is a superuser like root. Although we use root user on DigitalOcean, other platforms may have different user settings, and provisioners may not work properly due to lack of permissions.","spans":[]},{"type":"heading3","text":"Complete Code","spans":[]},{"type":"paragraph","text":"The complete config we used to create 1-click image on DigitalOcean and other platforms can be found at GitHub.","spans":[{"start":104,"end":110,"type":"hyperlink","data":{"link_type":"Web","url":"https://github.com/nknorg/nkn-cloud-image"}}]},{"type":"paragraph","text":"Yilun Zhang is co-founder and CTO of NKN.org.","spans":[{"start":0,"end":45,"type":"em"}]}],"blog_post_date":"2019-03-07","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"using-packer-to-create-a-1-click-nkn-image-on-digitalocean"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Paul Jarvis","author_image":{"dimensions":{"width":1861,"height":1861},"alt":"Paul Jarvis","copyright":null,"url":"https://images.prismic.io/www-static/71f5eb2e289151778a7c7e71918b8b9f3a26d12b_pauljarvis01-1.jpg?auto=compress,format"},"_meta":{"uid":"paul_jarvis"}},"blog_header_image":{"dimensions":{"width":800,"height":600},"alt":"laptop illustration","copyright":null,"url":"https://images.prismic.io/www-static/2406e46c6320ad83e8d9f275de03872e03b759ff_jarvis-company-of-one.png?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Why You Need These Starter Apps for Any Indie Business","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"Adam Wathan is a popular full-stack developer and entrepreneur who writes code and books for a living. He can easily whip up his own payment processing system with Stripe’s API—something he did for his first product, a video course about Laravel. But for his most recent product, Refactoring UI, he instead chose to use the ecommerce platform Gumroad.","spans":[{"start":0,"end":11,"type":"hyperlink","data":{"link_type":"Web","url":"https://adamwathan.me/"}},{"start":164,"end":176,"type":"hyperlink","data":{"link_type":"Web","url":"https://stripe.com/docs/api"}}]},{"type":"paragraph","text":"Why? Because, by using existing software, he was able to focus on the product and its marketing instead of reinventing the wheel. Gumroad had the features Adam needed, including support for PayPal payments in addition to credit card processing. And, as an added bonus, it acts as the \"merchant of record,\" which means they sell his products and collect sales tax, freeing him of those burdens too.","spans":[]},{"type":"paragraph","text":"The faster you can get your own software out the door to paying customers, the faster you’ll become profitable and can start building traction. Existing software is either free or cheap until you need to scale it—and if you’re scaling it, it means you’re generating a decent amount of revenue! For Adam, giving Refactoring UI as much attention as possible paid off, generating over a million dollars from it in the first two months.","spans":[]},{"type":"paragraph","text":"When starting a small or independent business, especially one that’s based online and selling software, it’s important to have the right digital tools in your toolbox. Most of my business runs on existing platforms or other software—from using Laravel Spark as the scaffolding to handle subscription billing and password resets, to using Mailchimp to send newsletters for marketing.","spans":[{"start":244,"end":257,"type":"hyperlink","data":{"link_type":"Web","url":"https://spark.laravel.com/"}},{"start":338,"end":347,"type":"hyperlink","data":{"link_type":"Web","url":"https://mailchimp.com/"}}]},{"type":"paragraph","text":"I release at least one product a year—from books to software to online courses—and while they’re quite different from one another, I mostly use all the same basic online tools to get them launched, promoted, and most importantly, made so people can buy them from me.","spans":[]},{"type":"paragraph","text":"Here’s the software I’ve currently got in my own “software stack” for starting a small digital product business:","spans":[]},{"type":"list-item","text":"Droplet from DigitalOcean, 1-Click application: Ghost.","spans":[{"start":48,"end":53,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/ghost?action=deploy&amp;refcode=a36c0e8abd93"}}]},{"type":"list-item","text":"This setup gives you a sharp looking and functional website in seconds, when more than just a single-page site is needed. [$5/month]","spans":[]},{"type":"list-item","text":"Droplet from DigitalOcean, 1-Click application: Fathom Analytics.","spans":[{"start":48,"end":64,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/fathom-analytics?action=deploy&amp;refcode=a36c0e8abd93"}}]},{"type":"list-item","text":"Fathom is the most private and simple way to collect website visitor analytics from your marketing site or landing page. (And I co-built it!) [$5/month]","spans":[]},{"type":"list-item","text":"Droplet from DigitalOcean, 1-Click application: Discourse.","spans":[{"start":48,"end":57,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/discourse?action=deploy&amp;refcode=a36c0e8abd93"}}]},{"type":"list-item","text":"If you want to start a community along with your digital product, then Discourse is the easiest way to start and maintain a message board for your customers, your audience, or even as a way for customers to help each other (this is how Ghost partially uses theirs). [$5/month]","spans":[]},{"type":"list-item","text":"Landing page site from Carrd.","spans":[{"start":23,"end":28,"type":"hyperlink","data":{"link_type":"Web","url":"https://carrd.co/"}}]},{"type":"list-item","text":"It’s a quick and easy way to put up a single page website and collect emails/gauge interest in your idea. [$9/year]","spans":[]},{"type":"list-item","text":"Mailing list from Mailchimp.","spans":[{"start":18,"end":27,"type":"hyperlink","data":{"link_type":"Web","url":"https://mailchimp.com/"}}]},{"type":"list-item","text":"Newsletters are a fantastic way to see how many people are interested in your product, and then to keep them posted on your progress. Mailing lists also have the highest return on investment (a 4400% return, meaning $44 for every $1 spent). [Free up to 2,000 subscribers]","spans":[{"start":194,"end":238,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.campaignmonitor.com/company/annual-report/2016/"}}]},{"type":"list-item","text":"Selling digital products via Gumroad.","spans":[{"start":29,"end":36,"type":"hyperlink","data":{"link_type":"Web","url":"https://gumroad.com"}}]},{"type":"list-item","text":"If you’re selling downloadables like ebooks, pre-orders or membership subscriptions, Gumroad saves you the hassle of everything “payment”. [3.5% + 30¢ per charge]","spans":[]},{"type":"list-item","text":"Selling SaaS subscriptions from Stripe.","spans":[{"start":32,"end":38,"type":"hyperlink","data":{"link_type":"Web","url":"https://stripe.com/"}}]},{"type":"list-item","text":"If you’re selling more custom solutions, then just using Stripe and a bit of custom code makes it quick and easy to do subscriptions. [2.9% + 30¢ per charge]","spans":[]},{"type":"list-item","text":"Creating a plan and roadmap using Trello.","spans":[{"start":34,"end":40,"type":"hyperlink","data":{"link_type":"Web","url":"https://trello.com"}}]},{"type":"list-item","text":"This app helps you keep track of what tasks you need to do and have done, and it also enables you to share a public roadmap of your progress with your customers or audience. [Free]","spans":[]},{"type":"list-item","text":"Internal communications from Twist.","spans":[{"start":29,"end":34,"type":"hyperlink","data":{"link_type":"Web","url":"https://twist.com"}}]},{"type":"list-item","text":"If your team is more than just yourself, Twist is a great way to stay in touch with them without being overwhelmed by group messaging platforms. [$6/user/month]","spans":[]},{"type":"list-item","text":"Using Cloudflare to register domains and handle DNS security/performance.","spans":[{"start":6,"end":16,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.cloudflare.com/"}}]},{"type":"list-item","text":"Using Cloudflare to manage your DNS means better defence against online threats and low-priced domain registration (they sell this at cost for TLD extensions they support). [Free]","spans":[]},{"type":"list-item","text":"Scaffolding for things you don’t want to code from Laravel Spark.","spans":[]},{"type":"list-item","text":"If you use Laravel, the Spark lets you forget the boilerplate (subscription billing, authentication, invoices, etc)  and focus on your app. [$99/site]","spans":[]},{"type":"paragraph","text":"With these 11 pieces of software you’ll be able to focus almost entirely on building your unique solutions, and not a lot of time building things you don’t have to. Building a digital product business can be difficult enough – any way you can make it easier (like using the software in this list), will help get you to profitability and beyond much faster.","spans":[]},{"type":"paragraph","text":"Paul Jarvis is the author of Company of One and cofounder of Fathom Analytics.","spans":[{"start":0,"end":78,"type":"em"},{"start":29,"end":43,"type":"hyperlink","data":{"link_type":"Web","url":"https://ofone.co/"}},{"start":61,"end":77,"type":"hyperlink","data":{"link_type":"Web","url":"https://usefathom.com"}}]},{"type":"paragraph","text":"Want more Paul? Join us March 19, 2019, from two P.M. to three P.M. EST, for an AMA (Ask Me Anything) as he discusses his latest book, Company of One, exploring why a bigger digital business isn’t always better, and shares his knowledge with the DigitalOcean community. RSVP today.","spans":[{"start":0,"end":281,"type":"strong"},{"start":270,"end":280,"type":"hyperlink","data":{"link_type":"Web","url":"https://www.eventbrite.com/e/digitalocean-presents-virtual-ask-me-anything-with-paul-jarvis-tickets-57132998336"}}]}],"blog_post_date":"2019-03-06","tags":[{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"starter-apps-for-any-indie-business"}}},{"node":{"author":{"_linkType":"Link.document","author_name":"Nick Wade","author_image":{"dimensions":{"width":556,"height":555},"alt":"Nick Wade","copyright":null,"url":"https://images.prismic.io/www-static/6ad190bd983b9cfd705aca95258383c9f373aed2_nick.png?auto=compress,format"},"_meta":{"uid":"nick_wade"}},"blog_header_image":{"dimensions":{"width":1200,"height":600},"alt":"boxes with different icons in them illustration","copyright":null,"url":"https://images.prismic.io/www-static/c5b48c32f77f85fdcae47f5e028bc6d9d550f106_marketplace_blog_header.jpg?auto=compress,format"},"blog_headline":[{"type":"heading1","text":"Introducing DigitalOcean Marketplace: Our Platform for Preconfigured 1-Click Apps and Tools","spans":[]}],"blog_post_content":[{"type":"paragraph","text":"This year, we’ve kept our focus on providing developers and teams with services that remove operational burden. We know you want to concentrate on what you’re building, to deploy quickly and seamlessly to the cloud, and to scale without hassle.","spans":[]},{"type":"paragraph","text":"Today, I’m excited to announce a big step for our community: the launch of DigitalOcean Marketplace, a platform where developers can find preconfigured applications and solutions to get up and running even more quickly.","spans":[{"start":75,"end":99,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"paragraph","text":"DigitalOcean Marketplace is designed with simplicity at its heart. We work closely with partners to deliver a truly seamless experience for users, creating the ability for developers to deploy fully tested app environments with the click of a button.","spans":[]},{"type":"heading3","text":"DigitalOcean Marketplace Makes Discovering & Integrating Apps Simple","spans":[]},{"type":"paragraph","text":"Modern app development often requires a little help from third parties. An entire ecosystem of software tools – from application frameworks to blogs and business apps – has sprung up to support developers and businesses.","spans":[]},{"type":"paragraph","text":"However, finding, installing, and maintaining compatible software can be overwhelming. Researching latest versions, configuring tools and libraries, and testing for compatibility are burdens that most developers and businesses would rather avoid.","spans":[]},{"type":"paragraph","text":"DigitalOcean Marketplace removes the pain of “dependency hell” by bringing together our user community and a network of trusted partners whose apps and tools we have carefully vetted for seamless integration and deployment. Whether you need a forum platform or an analytics package, you can deploy any app or tool on Marketplace with literally one click.","spans":[]},{"type":"heading3","text":"Marketplace Partners Provide the Services You Need","spans":[]},{"type":"paragraph","text":"We’re delighted to be launching with a handpicked set of 1-Click Apps built by technology partners who provide tools and services that have been in high demand from our developer community.","spans":[]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/wordpress"}}]},{"type":"image","url":"https://images.prismic.io/www-static/967694ee2a9117aa54eeeca07462b3d4399361ec_wordpress20-20digitalocean20marketplace202019-03-012022-48-04.png?auto=compress,format","alt":"drawing","copyright":null,"dimensions":{"width":586,"height":668}},{"type":"paragraph","text":"WordPress powers a third of the web, helping millions of teams from individual bloggers to some of the biggest brands in the world create rich websites and apps online. Whatever you want to create, sell, or share, the WordPress 1-Click is there to help do it quickly.","spans":[{"start":0,"end":9,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/wordpress"}},{"start":0,"end":267,"type":"em"}]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/grafana"}}]},{"type":"image","url":"https://images.prismic.io/www-static/33b617b77072f7b43a17f07757b84770347a98b7_grafana20-20digitalocean20marketplace202019-03-012022-49-12.png?auto=compress,format","alt":"drawing","copyright":null,"dimensions":{"width":590,"height":670}},{"type":"paragraph","text":"Grafana is a data visualization and monitoring tool that integrates with complex data from sources like Prometheus, InfluxDB, Graphite, and ElasticSearch. Grafana lets you create alerts, notifications, and ad hoc filters for your data while also making collaboration with your teammates easier through built-in sharing features.","spans":[{"start":0,"end":7,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/grafana"}},{"start":0,"end":328,"type":"em"}]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/openfaas"}}]},{"type":"image","url":"https://images.prismic.io/www-static/a0a80401bfb19739cf9cf3726239572b7b9e0075_openfaas.png?auto=compress,format","alt":"drawing","copyright":null,"dimensions":{"width":594,"height":672}},{"type":"paragraph","text":"With OpenFaaS® you can package anything as a serverless function - from Node.js to Golang to CSharp, even binaries like ffmpeg or ImageMagick. Try OpenFaaS and the world of functions in 60 seconds or less with this 1-Click setup.","spans":[{"start":0,"end":229,"type":"em"},{"start":5,"end":13,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/openfaas"}}]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/influxdb-tick-stack"}}]},{"type":"image","url":"https://images.prismic.io/www-static/30b7584519a4b4509994ad432e32c68990e713b7_influxdb20-20digitalocean20marketplace202019-03-012022-49-59.png?auto=compress,format","alt":"drawing","copyright":null,"dimensions":{"width":590,"height":670}},{"type":"paragraph","text":"The InfluxData Platform is the leading modern time series platform designed from the ground up for metrics and events. The open source TICK Stack 1-Click includes everything needed to quickly make beautiful dashboards, observe Kubernetes clusters, store syslog messages, and even monitor your smart home.","spans":[{"start":0,"end":304,"type":"em"},{"start":4,"end":23,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/influxdb-tick-stack"}}]},{"type":"paragraph","text":"","spans":[{"start":0,"end":0,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/hasura"}}]},{"type":"image","url":"https://images.prismic.io/www-static/eef76032b9688ddb83e64c0a942cd4ea9b8579e3_hasura20graphql20-20digitalocean20marketplace202019-03-012022-51-03.png?auto=compress,format","alt":"drawing","copyright":null,"dimensions":{"width":590,"height":668}},{"type":"paragraph","text":"Hasura GraphQL Engine lets you make powerful queries with built-in filtering, pagination, pattern search, bulk insert, update, delete mutations, and subscriptions. This 1-Click setup also includes an empty Postgres database and automatic HTTPS from Let’s Encrypt using Caddy web server.","spans":[{"start":0,"end":21,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/apps/hasura"}},{"start":0,"end":286,"type":"em"}]},{"type":"paragraph","text":"As you can see, we have focused on the tools most useful to innovators: founders, entrepreneurs, and early-stage business leaders. Over the next few months, we will be thoughtfully adding more 1-Click Apps and categories based on feedback from the DigitalOcean community, and we plan to integrate 1-Click Apps with DigitalOcean Kubernetes.","spans":[]},{"type":"heading3","text":"The Marketplace Ecosystem is Continually Expanding","spans":[]},{"type":"paragraph","text":"Just like our community is growing by the day, so too is the list of Marketplace partners providing top-notch apps and services.","spans":[]},{"type":"paragraph","text":"We’ve been delighted with the enthusiasm and caliber of our partners – they’ve been a delight to work with.","spans":[]},{"type":"paragraph","text":"If you or your team has built something delightful, and you’d love to get it in front of a discerning developer community, then Marketplace is open for you. You’ll find everything you need to know about joining DigitalOcean Marketplace and unleashing your product to the world at our Marketplace Vendor page.","spans":[{"start":284,"end":307,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/vendors"}}]},{"type":"heading3","text":"Dive in!","spans":[]},{"type":"paragraph","text":"I can’t wait for you to try out Marketplace and hear your thoughts. We’ll be rolling out continuous improvements to this platform over the next few weeks, including ongoing upgrades to the Control Panel, API, and CLI, as well as supporting features such as Tags, Teams, and Projects. As always, we will be guided by your feedback and ideas – so I’d love to hear from you in the comments below!","spans":[{"start":24,"end":43,"type":"hyperlink","data":{"link_type":"Web","url":"https://marketplace.digitalocean.com/"}}]},{"type":"paragraph","text":"Happy Coding,","spans":[]},{"type":"paragraph","text":"Nick Wade","spans":[]},{"type":"paragraph","text":"Head of Ecosystem & Marketplace","spans":[]}],"blog_post_date":"2019-03-05","tags":[{"tag1":{"tag":"Product Updates","_linkType":"Link.document","_meta":{"uid":"product-updates"}}},{"tag1":{"tag":"Marketplace","_linkType":"Link.document","_meta":{"uid":"marketplace"}}}],"_meta":{"uid":"introducing-digitalocean-marketplace"}}}]}}}