{"componentChunkName":"component---src-templates-page-tsx","path":"/page/4","result":{"pageContext":{"searchData":[{"id":"cjjg9ui0t303s010375m42b9w","title":"Turn a Browser Window into a Notepad with This One-Liner","slug":"/turn-a-browser-window-into-a-notepad-with-this-one-liner","avatar":"https://s3.amazonaws.com/contentkit/static/cjjg9ui0t303s010375m42b9w/OYIaJ1KK_400x400(1).png","date":"July 1, 2020"},{"id":"cjiy7xl9o17w701039gvl18a5","title":"Creating a Collaborative Editor with Draftjs for Fun","slug":"/draft-js-collaborative-editor","avatar":"https://s3.amazonaws.com/contentkit/static/cjiy7xl9o17w701039gvl18a5/draft-js.png","date":"June 28, 2020"},{"id":"cjeqgfsdsnmx90167n7j290kt","title":"How To Include SASS In Your React Project","slug":"/sass-react-webpack","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfsdsnmx90167n7j290kt/parcel-bundler.png","date":"May 1, 2020"},{"id":"cjiy4tseq0tnw0103bc5s7sxj","title":"How to Add a Loading Indicator to Material Ui's Component","slug":"/creating-a-material-ui-button-with-spinner-that-reflects-loading-state","avatar":"https://s3.amazonaws.com/contentkit/static/cjiy4tseq0tnw0103bc5s7sxj/material-ui.png","date":"January 28, 2020"},{"id":"cjkq4u7470ihr0157ad175b12","title":"Connecting to AWS Lambda via WebSockets","slug":"/connecting-to-aws-lambda-via-websockets","avatar":"https://s3.amazonaws.com/contentkit/static/cjkq4u7470ihr0157ad175b12/MQTT.js.png","date":"January 12, 2020"},{"id":"cjeqgfnhknnpe0199zbfwwkd8","title":"6 Really Cool APIs to Have Fun With","slug":"/cool-apis-to-have-fun-with","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfnhknnpe0199zbfwwkd8/scale-api.png","date":"January 1, 2020"},{"id":"cjeqgftvknnr30199dvw266qh","title":"Installing Node Canvas in AWS Lambda","slug":"/node-canvas-aws-lambda","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgftvknnr30199dvw266qh/aws-lambda.jpg","date":"December 31, 2019"},{"id":"cjn5fv8kl0pfj0124wwebufms","title":"GoLang Cheatsheet","slug":"/golang-cheatsheet","avatar":"https://s3.amazonaws.com/contentkit/static/cjn5fv8kl0pfj0124wwebufms/golang.png","date":"October 13, 2019"},{"id":"cjn15y8740liw0175u3s707eh","title":"Scheduled Jobs & Work Queues With Postgresql","slug":"/scheduled-jobs-work-queues-with-postgresql","avatar":"https://s3.amazonaws.com/contentkit/static/cjn15y8740liw0175u3s707eh/kxHkAenZ_400x400.jpg","date":"October 8, 2019"},{"id":"cjmym86fh0jy1013398nfzuqu","title":"Creating a Bot to Refill Parking Meters Using AWS Lambda","slug":"/creating-a-bot-to-refill-parking-meters-using-aws-lambda","avatar":"https://s3.amazonaws.com/contentkit/static/cjmym86fh0jy1013398nfzuqu/prwHMlRn_400x400.jpg","date":"October 7, 2019"},{"id":"cjmsaqt6l09le01046qeukpp9","title":"The USPS Tracking API: How To Track Packages","slug":"/usps-tracking-api","avatar":"https://s3.amazonaws.com/contentkit/static/cjmsaqt6l09le01046qeukpp9/usps.png","date":"October 3, 2019"},{"id":"cjeqgfuyenmy20167rycnmiql","title":"Stormpath vs Firebase - A Side-By-Side Comparison","slug":"/stormpath-vs-firebase-a-side-by-side-comparison","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfuyenmy20167rycnmiql/m3cEA33V_400x400.jpg","date":"September 2, 2019"},{"id":"cjeqgfv88nnrt01997wxd4rnl","title":"Sending emails with Firebase Cloud Functions","slug":"/firebase-functions-sending-emails","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfv88nnrt01997wxd4rnl/firebase.jpg","date":"September 2, 2019"},{"id":"cjeqgfqjknnq20199oe3zwi37","title":"Deploying To DigitalOcean From Travis","slug":"/deploying-to-digitalocean-travis","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfqjknnq20199oe3zwi37/digitalocean.jpeg","date":"August 25, 2019"},{"id":"cjkvpbr6p06z50181fg7xvsc3","title":"Circumventing AWS Lambda's Bundle Size Limit","slug":"/circumventing-aws-lambdas-bundle-size-limit","avatar":"https://s3.amazonaws.com/contentkit/static/cjkvpbr6p06z50181fg7xvsc3/b2lWK7c0_400x400.png","date":"August 15, 2019"},{"id":"cjiy45h7m0iba0111f5imt5em","title":"A Quick Way to List All Unicode Characters (Javascript)","slug":"/unicode-characters-javascript","avatar":"https://s3.amazonaws.com/contentkit/static/cjiy45h7m0iba0111f5imt5em/unicode.png","date":"July 29, 2019"},{"id":"ci1rxc41tm8yi7nd156pz9dom","title":"Kibana Rest API","slug":"/kibana-rest-api","avatar":"https://s3.amazonaws.com/contentkit/static/ci1rxc41tm8yi7nd156pz9dom/elastic.png","date":"July 24, 2019"},{"id":"cjeqgfjgennmw0199wha3j6u0","title":"Airtable As A Database For Middleman [Tutorial]","slug":"/airtable-middleman-database","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfjgennmw0199wha3j6u0/airtable-books.png","date":"June 1, 2019"},{"id":"cjeqgfj5qnnmr0199vzd85xuo","title":"Building A Multiplayer Game With Three.Js + WebSockets","slug":"/multiplayer-game-threejs","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfj5qnnmr0199vzd85xuo/three.jpg","date":"June 1, 2019"},{"id":"cjeqgfi8hnnml0199f1jyo1p5","title":"The Best Sketch Plugins","slug":"/the-best-sketch-plugins","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfi8hnnml0199f1jyo1p5/sketch.png","date":"June 1, 2019"},{"id":"d9920hsu8y7365frf2c6fxrx2","title":"Configuring Vault by Hashicorp in AWS EC2","slug":"/configuring-vault-by-hashicorp-in-aws-ec2","avatar":"https://s3.amazonaws.com/contentkit/static/d9920hsu8y7365frf2c6fxrx2/vault.png","date":"April 15, 2019"},{"id":"cjeqgfvsfnns00199mlbff48i","title":"Best Zapier Alternatives","slug":"/best-zapier-alternatives","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfvsfnns00199mlbff48i/zapier-alternative-workato.png","date":"January 31, 2019"},{"id":"cjn3l18390lyt0158i8qxs5dc","title":"Running A Simple Node Web Server On AWS EC2","slug":"/running-a-simple-node-web-server-on-aws-ec2","avatar":"https://s3.amazonaws.com/contentkit/static/cjn3l18390lyt0158i8qxs5dc/aws.png","date":"October 11, 2018"},{"id":"cjeqgfr4snmwz01673m8l4i51","title":"Graph.Cool vs Firebase","slug":"/graphcool-vs-firebase","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfr4snmwz01673m8l4i51/graphcool.png","date":"October 7, 2018"},{"id":"cjklurup00k0201739mflg9sz","title":"Accessing Redis on an Aws EC2 Instance from the Outside\t","slug":"/accessing-redis-on-an-aws-ec2-instance-from-the-outside","avatar":"https://s3.amazonaws.com/contentkit/static/cjklurup00k0201739mflg9sz/logo.jpeg","date":"August 9, 2018"},{"id":"cjkfzvvxt08up0191l38aadq4","title":"Using PDFtk in AWS Lamba","slug":"/using-pdftk-in-aws-lamba","avatar":"https://s3.amazonaws.com/contentkit/static/cjkfzvvxt08up0191l38aadq4/pdftk.png","date":"August 4, 2018"},{"id":"cjeqgfqsgnnq70199l4vc6vim","title":"Configuring WebSockets on Elastic Beanstalk/EC2","slug":"/websockets-aws-elasticbeanstalk-ec2","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfqsgnnq70199l4vc6vim/aws.png","date":"July 15, 2018"},{"id":"cjit96q2yk0is0111qpbmw66s","title":"Getting Started With Gmail API","slug":"/gmail-api-quickstart","avatar":"https://s3.amazonaws.com/contentkit/static/cjit96q2yk0is0111qpbmw66s/gmail.png","date":"June 28, 2018"},{"id":"cjeqgfo23nmwd0167ynqe7xi8","title":"5 Tips For Using NextJs","slug":"/5-tips-for-using-nextjs","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfo23nmwd0167ynqe7xi8/bHjpwZem_400x400.png","date":"June 1, 2018"},{"id":"cjhxixcyhw4ze01035butoukz","title":"React unstable_deferredUpdates","slug":"/react-unstable_deferredupdates","avatar":"https://s3.amazonaws.com/contentkit/static/cjhxixcyhw4ze01035butoukz/OYIaJ1KK_400x400(1).png","date":"June 1, 2018"},{"id":"cjeqgflpnnnoy01993y7aez8a","title":"Simple Web Scraping With Javascript","slug":"/simple-web-scraping","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgflpnnnoy01993y7aez8a/developer-tools-scraping.png","date":"May 1, 2018"},{"id":"cjeqgfk9lnnn101994ll4ursr","title":"Easy: Add Firebase Facebook Login To Your React App","slug":"/firebase-facebook-login-react","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfk9lnnn101994ll4ursr/firebase.png","date":"May 1, 2018"},{"id":"cjeqgfiqrnmtj0167dmz5g5dg","title":"The 5 Best Static Site Web Hosts","slug":"/the-5-best-static-site-web-hosts","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfiqrnmtj0167dmz5g5dg/digital-ocean.png","date":"May 1, 2018"},{"id":"cjeqgfmi2nmvs01670pgebekn","title":"Tips and Tricks For Using NightmareJs","slug":"/nightmare-js","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfmi2nmvs01670pgebekn/nightmare.png","date":"May 1, 2018"},{"id":"cjeqgfkqennn60199707yp1fb","title":"Why You Should Create Your Next React Web App With Firebase","slug":"/firebase-react-tutorial","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfkqennn60199707yp1fb/firebase.jpg","date":"May 1, 2018"},{"id":"cjeqgfpr6nnpx019978qzmaok","title":"How to Flush Data From Heroku Redis","slug":"/heroku-redis-flushall","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfpr6nnpx019978qzmaok/3wgIDj3j_400x400.png","date":"April 1, 2018"},{"id":"cjeqgfm8xnnp30199vxndhkgh","title":"5 Ways To Style React Components","slug":"/5-ways-to-style-react-components","avatar":null,"date":"April 1, 2018"},{"id":"cjeqgflz7nmvm0167yo7wsjg3","title":"Delete Spreadsheet Rows For Google Sheets","slug":"/delete-rows-google-sheets","avatar":null,"date":"April 1, 2018"},{"id":"cjeqgfp84nnps0199dvru09ll","title":"Make An Uptime Monitoring Microservice In Under 50 Lines of Code","slug":"/twilio-uptime-monitoring-node-tutorial","avatar":null,"date":"April 1, 2018"},{"id":"cjeqgfri4nnqd0199zsv6a9fl","title":"Hexo - The Best Static Site Generator? ","slug":"/deploy-a-hexo-blog","avatar":null,"date":"April 1, 2018"},{"id":"cjeqgfoygnnpn0199f31dgk9m","title":"Airtable As A Minimum Viable Database For Your ReactJs Project","slug":"/airtable-reactjs","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfoygnnpn0199f31dgk9m/airtable-screenshot.png","date":"March 2, 2018"},{"id":"cjeqgfn6pnmw0016793f81hpx","title":"The Advanced Guide To ReactJs Checkboxes","slug":"/reactjs-checkboxes","avatar":null,"date":"January 20, 2018"},{"id":"cjeqgfe8znnly01991jo5xkxx","title":"Minimum Viable GraphQL QuickStart","slug":"/minimum-viable-graphql-quickstart","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfe8znnly01991jo5xkxx/graphcool-schema.png","date":"January 1, 2018"},{"id":"cjeqgfgegnnmb0199jp3hv2xx","title":"How To Add A Contact Form To Your Ghost Blog","slug":"/ghost-blog-contact-form","avatar":null,"date":"November 1, 2017"},{"id":"cjeqgfmvxnnp90199bdti4r8n","title":"PHP Scraping Tutorial - Scrape Reddit With Goutte","slug":"/php-scraping-tutorial-scrape-reddit-with-goutte","avatar":null,"date":"October 7, 2017"},{"id":"cjeqgfpz1nmwp0167c5j46eij","title":"Cannot read property 'loose' of undefined","slug":"/cannot-read-property-loose-of-undefined","avatar":null,"date":"August 25, 2017"},{"id":"cjeqgftlanmxg0167zipvfzp1","title":"Airtable API Example & Tutorial - Generating Charts","slug":"/airtable-api-example-tutorial","avatar":null,"date":"January 31, 2017"},{"id":"cjeqgfphbnmwk0167ldz2mv2q","title":"React onClick Example and Tutorial","slug":"/react-onclick-example-and-tutorial","avatar":null,"date":"January 2, 2017"},{"id":"cjeqgfq8ynmwu0167z6c51ynp","title":"React Tables - How To Render Tables In ReactJS","slug":"/reactjs-tables","avatar":null,"date":"January 2, 2017"},{"id":"cjeqgfuoznmxu0167ayt2zkc3","title":"How To Add A Class in ReactJS","slug":"/reactjs-add-class","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgfu6gnnrb0199nyrddra0","title":"Fetching Github Blame with the GraphQL API V4","slug":"/github-blame-graphql-api-v4","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgfsmsnnqo0199o2x7dl4x","title":"How To Add Meta Descriptions to Middleman Pages","slug":"/middleman-meta-descriptions","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgfs2wnnqi0199rco2vvz2","title":"Zapier Webhook Post Example & Tutorial","slug":"/zapier-webhook-post-example-tutorial","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgfvhunmy90167t6ouqfmb","title":"Setting Up A Job Queue For A Node App [Tutorial]","slug":"/job-queue-node","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgftbcnnqy0199nc1f92te","title":"NextJs vs Create-React-App - A Side-By-Side Comparison","slug":"/nextjs-vs-create-react-app","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgft2pnnqt01990ty8fmeu","title":"How To Create A Modal In ReactJS [Tutorial]","slug":"/reactjs-modal","avatar":null,"date":"March 14, 2018"},{"id":"cjeqgfugpnnrj0199x7anb33t","title":"How To Use Twilio With ReactJS","slug":"/reactjs-twilio-example-tutorial","avatar":null,"date":"March 14, 2018"}],"page":4,"offset":20,"total":57,"nodes":[{"id":"d9920hsu8y7365frf2c6fxrx2","title":"Configuring Vault by Hashicorp in AWS EC2","slug":"configuring-vault-by-hashicorp-in-aws-ec2","published_at":"2019-04-15T02:34:51.791","created_at":"2019-04-14T02:02:41.144159","excerpt":"How to configure Vault on EC2.","image":{"id":"o7jcwfh0wu780bnyrig7yn257","url":"static/d9920hsu8y7365frf2c6fxrx2/vault.png"},"posts_tags":[{"tag":{"id":"3pl7ejawubufz70vjnad","name":"AWS"}},{"tag":{"id":"782rutu758gkyzbpqx1i","name":"Hashicorp"}}],"date":"April 15, 2019","html":"
aws s3 create-bucket --bucket VAULT_BUCKET_NAME
\nCreate two policies and attach them to a user, Vault.
\nAllow access to the newly created S3 bucket:
\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Action\": [\n \"s3:*\"\n ],\n \"Resource\": [\"arn:aws:s3:::VAULT_BUCKET_NAME/*\"]\n }\n ]\n}
\nAllow IAM actions required by Vault:
\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Action\": [\n \"iam:AttachUserPolicy\",\n \"iam:CreateAccessKey\",\n \"iam:CreateUser\",\n \"iam:DeleteAccessKey\",\n \"iam:DeleteUser\",\n \"iam:DeleteUserPolicy\",\n \"iam:DetachUserPolicy\",\n \"iam:ListAccessKeys\",\n \"iam:ListAttachedUserPolicies\",\n \"iam:ListGroupsForUser\",\n \"iam:ListUserPolicies\",\n \"iam:PutUserPolicy\",\n \"iam:RemoveUserFromGroup\"\n ],\n \"Resource\": [\n \"arn:aws:iam::{AWS_ACCOUNT_ID}:user/vault-*\"\n ]\n }\n ]\n}
\naws ec2 create-security-group --group-name vault-sg --description Vault | jq -r '.GroupId'
\naws ec2 authorize-security-group-ingress --group-name vault-sg --protocol tcp --port 22 --cidr '0.0.0.0/0'\naws ec2 authorize-security-group-ingress --group-name vault-sg --protocol tcp --port 8200 --cidr '0.0.0.0/0'
\nCreate an EC2 instance with the latest AMI, using the vault-sg security group and the IAM user created above.
\nWait a moment and SSH into the instance, then install Vault as follows:
\ncd /tmp && wget https://releases.hashicorp.com/vault/1.1.1/vault_1.1.1_linux_amd64.zip\nunzip vault_1.1.1_linux_amd64.zip\ncp vault /usr/bin/vault\nvault --version
\nMake a Vault data directory:
\nmkdir -p /etc/vault.d
\nConfigure Vault:
\ntouch /etc/vault.d/vault.hcl
\nAdd the following configuration to vault.hcl:
\nui = true\nlistener \"tcp\" {\n address = \"0.0.0.0:8200\"\n}\nbackend \"s3\" {\n bucket = \"YOUR_AWS_BUCKET\"\n region = \"us-east-1\"\n}
\nAdd systemd service file for Vault:
\n[Unit]\nDescription=Vault\nRequires=network-online.target\nAfter=network-online.target\n[Service]\nRestart=on-failure\nExecStart=/usr/bin/vault server -config /etc/vault.d/vault.hcl\nExecReload=/bin/kill -HUP $MAINPID\nKillSignal=SIGTERM\n[Install]\nWantedBy=multi-user.target
\nStart Vault:
\nservice vault start
\nFind the public DNS associated with your EC2 instance and visit the url on port 8200:
\nhttps://ec2-xx-xx-xx-xx.compute-1.amazonaws.com:8200
","avatar":"https://s3.amazonaws.com/contentkit/static/d9920hsu8y7365frf2c6fxrx2/vault.png"},{"id":"cjeqgfvsfnns00199mlbff48i","title":"Best Zapier Alternatives","slug":"best-zapier-alternatives","published_at":"2019-01-31T05:00:00","created_at":"2018-03-14T02:16:46","excerpt":"What are the best Zapier alternatives?","image":{"id":"cjfft8fxadabr0192dglufq00","url":"static/cjeqgfvsfnns00199mlbff48i/zapier-alternative-workato.png"},"posts_tags":[],"date":"January 31, 2019","html":"What are the best Zapier alternatives?
\nIn this post, I'll compare alternatives to Zapier that help you integrate apps and automate workflows.
\nYou'll quickly see that most Zapier alternatives are actually courting different markets. For example, IFTT is more casual and consumer-focused, whereas Workato is more oriented for businesses like Zapier.
\nWe're living in an era where it's easy to find yourself subscribed to 10-100 different software-as-a-service (SAAS) products. There are even a few nifty tools to manage all of your subscriptions, like CardLife and TrueBill.
\nZapier allows different apps to talk to one another through their restful API. For example, you could automatically push transaction data from your ecommerce website into Quickbooks. Or you could integrate Salesforce and Mailchimp. The use-cases are truly endless.
\nBut Zapier is not the only game in town. There are a number of Zapier alternatives that you should consider if you're looking to integrate apps or automate workflows.
\nI've used Workato for small business and it has a number of advantages.
\nThe customer service is excellent. They'll hack together an integration for you if you're in a pinch and contact Workato support.
\nWorkato's time machine feature also gives it an edge over Zapier. Zapier does not let you retroactively import or sync data between apps.
\nIFTTT aka If-This-Then-That is really a different product than Zapier or Workato. IFTTT is more casual and consumer-facing.
\nIn almost the complete opposite of Zapier’s fancy and potentially confusing multi-steps, IFTTT (on the surface) can even cut out the set trigger altogether and directly give you the ability to run a desired action through “DO” recipes.\n
The emphasis at IFTT is on ease of use and lifestyle integrations, e.g., smart home, exercise, productivity, and shopping. For example, you can create an IFTTT that will log your gym activity using your phone's GPS into Google Sheets.
\nZapier and Workato by contrast are fundamentally business tools. Here's a highly detailed comparison of IFTTT verses Zapier.
\nBip is an open source app built on node.js with a community of github contributors.
\nOneSAAS is like Zapier, helping you integrate your apps, but with a focus on business apps. They're particularly oriented to online retail, web developers, B2B sales and event organizers. For example, OneSAAS integrates with these ecommerce solutions:
\nIn addition, OneSaaS connects with a number of shipping fulfillment and inventory management apps. I'm talking about services like Shipwire and ShippingEasy.
\nOneSaaS has three pricing tiers $29/month, $49/month and $99/month. However, the lowest tier is fairly restrictive, permitting you to use 1 accounting app, 2 ecommerce or billing & invoicing apps, and 1 fulfillment app.
\nThere are situations where OneSAAS might be preferred over Zapier or Workato for businesses. For example, OneSAAS might integrate with an inventory management product that other services don't connect. Otherwise, I'd stick with services that support more apps and have a larger user base.
","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfvsfnns00199mlbff48i/zapier-alternative-workato.png"},{"id":"cjn3l18390lyt0158i8qxs5dc","title":"Running A Simple Node Web Server On AWS EC2","slug":"running-a-simple-node-web-server-on-aws-ec2","published_at":"2018-10-11T02:35:54.72","created_at":"2018-10-10T20:03:59","excerpt":"","image":{"id":"pum3dk2chwjxbistc2948swe2","url":"static/cjn3l18390lyt0158i8qxs5dc/aws.png"},"posts_tags":[{"tag":{"id":"3pl7ejawubufz70vjnad","name":"AWS"}}],"date":"October 11, 2018","html":"How do you run a web server on EC2?
\nI've used AWS Lambda and/or Cloudformation extensively but was curious to see how difficult it is to spin up a EC2 instance and expose the right ports from the command line. Using AWS' command line interface This is a good approach for testing and playing with tools and has advantages over Cloudformation. Debugging Cloudformation can be a huge pain. Also creating and updating stacks can take a bit longer than simply running an instance from terminal.
\n aws --version.
What parameters do we need to run an instance? We need:
\nBefore running any commands, export the AWS region you want to use so that you're not prompted for every command. I'll be using us-east-1.
\nexport AWS_DEFAULT_REGION=us-east-1
\nThis identifies the instance on Amazon's marketplace. We'll stick with defaults and use the latest Amazon Linux Image.
\nRun:
\naws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --query 'Parameters[0].[Value]' --output text
\nAs of this writing, the latest Amazon Linux AMI is ami-0922553b7b0369273.
\nWhen you create an EC2 instance on aws.amazon.com using the UI you're prompted to use an existing private key or create a new one. The private key is just the label associated with a set of public and private keys allowing SSH access to your instance.
\nThis command lists all of your existing key pairs:
\naws ec2 describe-key-pairs | jq -r '.KeyPairs'
\nIf you don't have any keys, you can create a new one:
\naws ec2 create-key-pair --key-name webserver | jq -r '.KeyMaterial' > ~/.ssh/webserver.pem
\nThis command saves the RSA key material to ~/.ssh/webserver.pem.
\nAWS accounts have a default VPC, subnet, and set of security groups.
\nRather than create a new subnet we'll just use one of the subnets associated with the default VPC.
\nHere's how you list subnets:
\naws ec2 describe-subnets | jq -r '.Subnets'
\nFor a web server, we'll use two security groups: a default security group associated with the default VPC, and a custom security group that exposes ports 22 (SSH), 80 (HTTP), and 443 (HTTPS).
\nGet the default security group ID as follows:
\naws ec2 describe-security-groups --group-name default | jq -r '.SecurityGroups[0].GroupId'
\nAnd create a new security group:
\naws ec2 create-security-group --group-name webserver --description webserver | jq -r '.GroupId'
\nNow we need to run authorize-security-group-ingress three times (once for each port).
\naws ec2 authorize-security-group-ingress --group-name webserver --protocol tcp --port 22 --cidr '0.0.0.0/0'\naws ec2 authorize-security-group-ingress --group-name webserver --protocol tcp --port 80 --cidr '0.0.0.0/0'\naws ec2 authorize-security-group-ingress --group-name webserver --protocol tcp --port 443 --cidr '0.0.0.0/0'
\nWe need to expose port 22 for SSH access, port 80 - the port that our node server will listen to, and port 443 for HTTPS.
\nUser data lets us run scripts when the instance is launched. In this case, we need to install node and npm on the EC2 instance when it's created. Create a new file (e.g., user-data.sh) and add:
\n#!/bin/bash -v\nsu ec2-user -c \"curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash\"\nsu ec2-user -c \"source /home/ec2-user/.nvm/nvm.sh && nvm install node\"\nsu ec2-user -c \"source /home/ec2-user/.nvm/nvm.sh && nvm use node\"\nln -s /home/ec2-user/.nvm/versions/node/v10.11.0/bin/node /usr/bin/node\nln -s /home/ec2-user/.nvm/versions/node/v10.11.0/bin/npm /usr/bin/npm\nmkdir -p /var/www\necho -e \"require('http').createServer((req, res) => {\" > /var/www/server.js\necho -e \" res.setHeader('content-type', 'text/plain')\" > /var/www/server.js\necho -e \" res.end('OK')\" > /var/www/server.js\necho -e \"}).listen(process.env.PORT || 80)\" > /var/www/server.js\nnode /var/www/server.js
\nWe'll supply the path to the user-data.sh script as a parameter when we create the EC2 instance like so:
\naws ec2 run-instances --user-data file://path/to/your/user-data.sh
\nHere's what everything looks like together:
\n","avatar":"https://s3.amazonaws.com/contentkit/static/cjn3l18390lyt0158i8qxs5dc/aws.png"},{"id":"cjeqgfr4snmwz01673m8l4i51","title":"Graph.Cool vs Firebase","slug":"graphcool-vs-firebase","published_at":"2018-10-07T04:00:00","created_at":"2018-03-14T02:16:39","excerpt":"I've used both Graph.Cool and Firebase. Guess what? They're both great products.","image":{"id":"hwusmsqgfojz437se14hn87fr","url":"static/cjeqgfr4snmwz01673m8l4i51/graphcool.png"},"posts_tags":[],"date":"October 7, 2018","html":"I've used both Graph.Cool and Firebase. Guess what? They're both great products.
\nFirebase is a noSQL backend-as-a-service (BAAS). The value proposition of Firebase is that during app development you can plug in Firebase and forget about back-end development.
\nHere's how you might query your Firebase database to get a user's information:
\nconst admin = require('firebase-admin')\nconst getUserData(uid) => admin.database()\n .ref(users/${uid})\n .once('value')\n .then(snap => snap.val()
\nThen you'd call getUserData(uid), supplying the user's id as an argument.
\nMy personal opinion is that Firebase is perfect for prototyping but at scale can become prohibitively expensive.
\nI'm a little biased toward Graph.Cool because it compliments React nicely.
\nFirebase is better than Graph.Cool for prototyping because real-time, and authorization come baked in. With GraphQL, you need to integrate with Auth0 and setup subscriptions. The upside is that you have more control with Graph.Cool and performance is better.
\nFirebase was launched in April 2012, so it's been around for a while. It was acquired by Google in 2014, and isn't going anywhere anytime soon.
\nAdding Firebase to your app solves three big obstacles in one swoop:
\nAdding Firebase to a single-page application (SPA) built with React or Angular is straightforward and well-documented.
\nFirebase supports web sockets, which allows your up to be updated in real time. For example, if you're using React you can just import react-fire and use the syntax firebase.database().ref().on(\"value\").
\nGraph.Cool is a GraphQL backend for your app. If you're not familiar with GraphQL, it was developed by Facebook to compliment React.
\nGraphQL solves the problem of nested requests. Let's say you want to display a user's posts in their profile. To get this information, you'd need to issue a get request to /user/{userId} which might return:
\n{\n name: 'Steve',\n email: 'steve@apple.com',\n uid: 'xxx',\n posts: [\n 'cj5b5nwzl2le601842h3nctbl',\n 'cj5b5ns2s2lcm01846f4fbmtj'\n ]\n}
\nThen, to get Steve's posts, you'd need to make a request to /posts/{postId}.
\nThe point is you might end up making 3-5 GET requests to get the data that your component needs. This is especially true if groups are highly interrelated.
\nWith GraphQL, you explicitly state your components data requirements. But you leave the rest to GraphQL to reconcile all the requests and bundle them together. With GraphQL, the syntax looks like:
\nIn short, GraphQL really shines with nested queries.
\nNew and shiny.
\nLess vendor lock-in. If Graph.Cool goes defunct, you can set up your own GraphQL server to use as a backend without reworking front-end code.
\nSupports both Apollo (simply) and Relay.
\nWorks great with React (Apollo and Relay are kind of like a Redux store connected right to the server).
\nNew and shiny.
\nMore difficult to integrate than Firebase.
","avatar":"https://s3.amazonaws.com/contentkit/static/cjeqgfr4snmwz01673m8l4i51/graphcool.png"},{"id":"cjklurup00k0201739mflg9sz","title":"Accessing Redis on an Aws EC2 Instance from the Outside\t","slug":"accessing-redis-on-an-aws-ec2-instance-from-the-outside","published_at":"2018-08-09T02:38:23.313","created_at":"2018-08-09T00:57:22","excerpt":"","image":{"id":"6ovlljrbmhcymzsr7dofh1k5g","url":"static/cjklurup00k0201739mflg9sz/logo.jpeg"},"posts_tags":[{"tag":{"id":"5276cfewytt6smbp4sc2","name":"Redis"}}],"date":"August 9, 2018","html":"AWS resources are secure by default which is a good thing. However, what happens when you want to open things up for testing and development?
\nConsider the case of making AWS-hosted Redis accessible outside of the AWS network.
\nOne option is elasticache. Elasticache can be configured to be accessible outside of AWS, but not without difficulty.
\nI took the DIY approach and installed Redis on a clean EC2 instance. Exposing Redis on an EC2 instance is relatively straightforward.
\n\nYou can use the command line tool redis-ec2 to spin up a redis instance automatically.
\n\nFind the security group associated with you EC2 instance and add a custom TCP inbound rule exposing whatever port you intend to use (in the screenshot below, port 6379).
\n\nNote that using the CIDR IP 0.0.0.0/0 whitelists all possible IP addresses on port 6379. Therefore you must password protect your Redis instance by uncommenting requirepass in your Redis configuration file.
\nRedis has full access to the filesystem (with the default configuration). If Redis is exposed on a port and no password is set it's trivially easy for malicious actors to co-opt your server by using Redis to write to ~/.ssh/authorized_keys:
\nYes. I successfully gained access as the Redis user, with a proper shell, in like five seconds. Courtesy of a Redis instance unprotected being, basically, an on-demand-write-this-file server, and in this case, by ssh not being conservative enough to deny access to a file which is all composed of corrupted keys but for one single entry. However ssh is not the problem here, once you can write files, even with binary garbage inside, it’s a matter of time and you’ll gain access to the system in one way or the other.\n
Edit your Redis configuration to disable protected mode:
\nsed -i 's/protected-mode yes/protected-mode no/' /path/to/your/redis.conf
\nsed -i 's/bind\\s127.0.0.1/bind 0.0.0.0/' /path/to/your/redis.conf
\nRedis is fast and millions of passwords can be checked per second. Therefore you need a really difficult password.
\nEdit your Redis configuration such that requirepass is uncommented or copy the following line.
\nsed -i \"s/# requirepass.*/requirepass MY_EXTREMELY_DIFFICULT_PASSWORD/\" /path/to/your/redis.conf
\nIf you're having trouble connecting to your EC2-hosted Redis, you may need to disable iptables.
\nservice iptables save\nservice iptables stop\nchkconfig iptables off
\nSome notes on installation:
\nuser-data
argument. user-data
is just a bash script that is run automatically once on the instance after deployment. If you're creating your instance from the command line, AWS expects user-data
to be a string, e.g., '#!/usr/bin/env bash....'. If you're using the AWS SDK, the parameter is named UserData
and must be base64 encoded. For example: ec2.launchInstances({ UserData: fs.readFileSync('script.sh', { encoding: 'base64' })
.