Ben Lobaugh Online

I think, therefore I am. I am, therefore I sail

How to Use the Maxmind Javascript API to Control Content by City, State, or Country

Controlling what a website visitor sees, based on their geo location, is a fairly common activity today. Most often, this happens on the server, before the content is generated for the visitor to see, but what if you do not have access to manipulate server side code and can only update the javascript that the site is using? Using the MaxMind Javascript API makes this much easier than you might think.

In this article I am going to show you how I accomplished this, in a way that you can easily replicate. What I am not going to do is show you how to build a website or teach Javascript principles.

Let’s consider this scenario:

  • HTML has already been generated by the server
  • There is no access to change the server side code
  • We do have that ability to add Javascript to the site
  • All elements that must be hidden have the class attribute of “geo-hide”
  • Visitors from Washington State, USA must not see the specified content

Prerequisites:

  • Working website with edit access for javascript
  • MaxMind account. The free trial will suffice

We will be using the MaxMind GeoIP2 Javascript Client API and the GeoIP2 Precision Service.

Architecting the solution

  • When a page loads, determine what state the visitor is making the request from
  • If the state is not Washington, allow them to see all the content
  • If the state is Washington, remove all HTML elements with the class attribute of “geo-hide” from the DOM

Note: This solution does require javascript be enabled on the visitor’s browser. It is rare that javascript is disabled these days. If you are concerned, look up any of the methods of requiring javascript to be enabled in a browser.

Another Note: This solution does not provide any protection against bots. MaxMind charges per query to their API- to save money, be sure you only query Maxmind on legitimate visits.

Final Note: This solution will query MaxMind on every page load. It is advised to use some caching method to prevent unnecessary calls. For example, you could cache the geolocation response as a session cookie. If the cookie is set, do not call Maxmind, if not, call Maxmind.

Include the GeoIP2 Javascript client library

MaxMind has already built a Javascript library that contains the functionality we need. All that needs to be done is to include it.

Add the following script include:

<script src=”//geoip-js.com/js/apis/geoip2/v2.1/geoip2.js” type=”text/javascript”></script>

Set up the content control code

The Javascript API has a single object that lets us easily retrieve the visitor location. It is

geoip2.city( onSuccess, onError );

The two parameters are callbacks, the first contains the geolocation data, the second contains an error, in the event that a geolocation could not be determined.

Plug that into a simple object and we have the following.

var geoipcheck = (function () {
var onSuccess = function (geoipResponse) {
var state = geoipResponse.subdivisions[0].iso_code; if ( WA == state ) {
var elements = document.getElementsByClassName(‘geo-hide’);
while(elements.length > 0){
elements[0].parentNode.removeChild(elements[0]);
}

}
}; var onError = function (error) {
// Error control code here
}; return function () {
geoip2.city( onSuccess, onError );
};
}());geoipcheck();

As you can see in the onSuccess function, if the visitor’s state is listed as WA, or Washington, the HTML elements with the class of “geo-hide” will be removed from the DOM.

This is by no means a foolproof, or complete, solution, but it will get you up and running with the ability to control content via geolocation. This is particularly useful on services such as Shopify which do not allow you to alter what is rendered on the server side of things.

Lumen

JWT User Authentication API with Lumen

Lumen is a great framework to build an API off of, but it does not come with user authentication or authorization. I needed to create a small API that allowed users to create an account and access the service with a JWT. Quality information on how to pull that off with Lumen is not very well available- this article will provide a single reference point on building a simple user authentication and authorization system with JWTs on Lumen.

In this article I will teach you how to set up user authentication and authorization in Lumen. I will not teach you what Lumen or JWT is. I am going to assume you know what a they are or you would not be reading this article.

If you are following along, the prerequisite for what follows is:

  • Running Lumen project

To see the complete code from this article, visit https://github.com/blobaugh/lumen-api-jwt-auth-example

This example includes a docker-compose.yml file that will get you up and running quickly.

For the JWT portion, we will be utilizing the excellent library from https://github.com/tymondesigns/jwt-auth

Install the JWT Library

We will be utilizing the JWT library by Sean Tymon. The library is installable as a composer package, and can be installed with the following command:

composer require tymon/jwt-auth

A secret needs to be generated to configure the JWT library, and added to the .env file. It can be generated with the following artisan command.

php artisan jwt:secret

The .env file was automatically updated with the key. The key will be used to sign all the JWT tokens.

Prep Lumen

There are now some steps we need to take to prep Lumen, before we can implement the user authentication portion.

To begin, open up the file bootstrap/app.php, then add or uncomment the following:

$app->withFacades();$app->withEloquent();$app->register(App\Providers\AuthServiceProvider::class);$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

Add the JWT Service Provider

$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);

Set up the Auth Config

This part bit me at first- Lumen does not come with the config directory like Laravel does. You will need to create it and a file called auth.php.

Create the file config/auth.php and add the following:

<?phpreturn [
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class
]
]
];

Create the user table

Lumen does not come with the user tables out of the box, so we will need to create them ourselves. We are going to create the same tables that Laravel uses.

Run the following two artisan commands to generate the migration files:

php artisan make:migration create_users_tablephp artisan make:migration create_password_resets_table

Place the following code in the up() method of the create_users_table migration:

Schema::create('users', function (Blueprint $table) {  $table->increments('id');  $table->string('name');  $table->string('email')->unique();  $table->timestamp('email_verified_at')->nullable();  $table->string('password');  $table->rememberToken();  $table->timestamps();});

Place the following code in the up() method of the create_password_resets_table migration:

Schema::create(‘password_resets’, function (Blueprint $table) {

Schema::create('password_resets', function (Blueprint $table) {  $table->string('email')->index();  $table->string('token');  $table->timestamp('created_at')->nullable();});

Finally, run the migrate command to create the tables!

php artisan migrate

Set up the User Model

The User model will allow us to create a representation of the user that can manipulate the database table, and manage a user’s JWT tokens.

Open your User model and add the following use statement:

use Tymon\JWTAuth\Contracts\JWTSubject;

Add JWTSubject to the class implements clause, which will make it similar to:

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject

Now add the following methods for JWT handling

/**
* Retrieve the identifier for the JWT key.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}

Create Authentication Controller

The Authentication controller will handle both the registration of new users and creation/refreshing of the JWT tokens.

I am not going to go through the AuthController line by line. It will be similar to any other auth controller, with a few tweaks for the JWTs.

Create the app/Http/Controllers/AuthController.php file and place in the following code:

<?php
namespace App\Http\Controllers;use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth;class AuthController extends Controller
{ public function __construct() {
$this->middleware(‘auth’, [‘except’ => [‘login’, ‘register’, ]]);
} /**
* Attempt to register a new user to the API.
*
* @param Request $request
* @return Response
*/ public function register(Request $request)
{
// Are the proper fields present?
$this->validate($request, [
‘name’ => ‘required|string|between:2,100’,
‘email’ => ‘required|string|email|max:100|unique:users’,
‘password’ => ‘required|string|min:6’,
]); try {
$user = new User;
$user->name = $request->input(‘name’);
$user->email = $request->input(‘email’);
$plainPassword = $request->input(‘password’);
$user->password = app(‘hash’)->make($plainPassword);
$user->save(); return response()->json([‘user’ => $user, ‘message’ => ‘CREATED’], 201);
} catch (\Exception $e) {
return response()->json([‘message’ => ‘User Registration Failed!’], 409);
}
} /**
* Attempt to authenticate the user and retrieve a JWT.
* Note: The API is stateless. This method _only_ returns a JWT. There is not an
* indicator that a user is logged in otherwise (no sessions).
*
* @param Request $request
* @return Response
*/
public function login(Request $request)
{
// Are the proper fields present?
$this->validate($request, [
‘email’ => ‘required|string’,
‘password’ => ‘required|string’,
]); $credentials = $request->only([‘email’, ‘password’]); if (! $token = Auth::attempt($credentials)) {
// Login has failed
return response()->json([‘message’ => ‘Unauthorized’], 401);
} return $this->respondWithToken($token);
} /**
* Log the user out (Invalidate the token). Requires a login to use as the
* JWT in the Authorization header is what is invalidated
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout() {
auth()->logout();
return response()->json([‘message’ => ‘User successfully signed out’]);
} /**
* Refresh the current token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh() {
return $this->respondWithToken( auth()->refresh() );
} /**
* Helper function to format the response with the token.
*
* @return \Illuminate\Http\JsonResponse
*/
private function respondWithToken($token)
{
return response()->json([
‘token’ => $token,
‘token_type’ => ‘bearer’,
‘expires_in’ => Auth::factory()->getTTL() * 60
], 200);}
}

Set up the Routes

Next up is some routing! We are almost done!

Open up the routes/web.php file and add the following to it:

/*
*
* UNAUTHENTICATED ROUTES
*
*/
$router->post( ‘/login’, ‘AuthController@login’);
$router->post( ‘/register’, ‘AuthController@register’ );/*
*
* AUTHENTICATED ROUTES
*
*/
$router->group(
[
‘middleware’ => ‘auth’,
], function( $router ) {
$router->post( ‘/logout’, ‘AuthController@logout’ );
$router->get( ‘/refresh’, ‘AuthController@refresh’ );
$router->post( ‘/refresh’, ‘AuthController@refresh’ );
});

Note: You can do this in routes/api.php if you would like. That will cause a prefix of `api/` in the URL

Congrats

That is it! You now have user authentication and authorization set up on your Lumen API. Congrats!

Boat Delivery – San Pedro, CA to Tacoma, WA – trip log

Day One – Aug 15, 2020

Long day today. Woke up at 3 am and hopped on a flight from SeaTac, WA to Long Beach, WA. Planned to sleep on the first flight, then on the layover in Oakland, CA to get coffee and breakfast. All went to plan until Oakland. The airport had significant portions physically closed off, including all the restaurants and coffee shops…

Finally arrived in Long Beach and went right to the boat.

42’ Uniflite. The boat had been working as a commercial dive boat, taking groups out to Catalina Island. It was somewhat modified. Customers dropped off over the last couple of years and the boat was retired. The owner is moving it up to Tacoma, WA, and converting it back into a pleasure craft for cruising Puget Sound. There was still a lot on it from it’s working days. We hauled a lot off the boat.

Later met a great guy named Gary. Friend of the owner. He had a beautiful 75’ Chris Craft. We ran hard today. I was ready to drop at midnight. Fell asleep before my head hit the pillow.

I never did get any coffee…

Provisioning Day – Aug 16, 2020

Fiddled with the AIS this morning. Something wonky going on. It is transmitting, but also throwing an error. Turned out that the antenna was being shared by the VHF and it did not like that. Acquired a new antenna to install tomorrow.

Jesse and I went on a provisioning run. The amount of food we purchased is astonishing. I lived on a 33’ powerboat with my wife, and we cruised. 36’ sailboat. We always had plenty of food,  but it was tight. This boat is 42’ and has a full-sized fridge and freezer! There is also a large chest style fridge for drinks. It is provisioned to the gills with so. Much. Food. 

Tomorrow is the final prep day. We need to install then antenna, retrieve spare parts from a storage shed, finish resealing one window, and secure loose items, then we are ready to head out! Santa Barbara will be the first stop. Short hop/shakedown cruise.

Final Prep Day! – Aug 17, 2020

All the last minute projects got completed today. 8 person self-inflating life raft was installed, and Jesse did a stellar job of cleaning the carpets. 

My good childhood friend, Josh, moved down in this area a couple of years ago. Had not seen him since. He was able to stop by after work for a couple of hours. I missed that guy!

Missing Alix and Clara too. Clara gets excited to see me on FaceTime. She is 16 months old and adorable as heck.

All systems go to cast off in the morning!

Cast Off! – Aug 18, 2020

Left the dock at 7:31 AM

Big fog rolled into the harbor, but between the remaining visibility and our radar, we were not to be stopped.

Running at about 15 knots on fairly smooth seas. Engines running strong. They are brand new Cummins Diesels with only about 500 hours on them.

We got into Santa Barbara marina around 2 pm. Today was intentionally short. About 80 miles. We can run 150 without issue. Shakedown cruise for the owner. 

Every boat in this marina is nice. A big change from San Pedro, where most boats looked like derelicts. Walked passed an amazing sailboat, Moody DS54. Would be a fantastic offshore boat. Perfect for this area. The cabin was massive. Good for entertaining and cruising. On sailboats with that big of a hull, the sail-ability of the boat usually suffers, but this boat had a massive rig. It looks like it could be a good performer.

Getting up early in the morning and making a fast run to either San Lius Abisbo or Morro Bay, depending on the weather. There is a big northerly coming in in the afternoon that we want to avoid. The boat can handle it, but it would be a very uncomfortable ride.

Day Two – Santa Barbara to San Luis Obispo – Aug 19, 2020

Up at 6 am, left the dock at 7am. The seas here are very calm.

Passed an oil rig that was stinking up the whole area. It leaked something and the water around for miles had a smelly sheen on it. 

Weather is looking good for a run to San Luis Obispo. When we get close we will reevaluate Morro bay and head there, depending on the weather. I would prefer to get further north myself and do not mind bumpy conditions for a couple hours.

We headed to Morro bay. Had a few interesting moments. This boat is capable of 24 knots, which is pretty quick. We had been cruising at 17 knots when the waves started building. I was using the head (boat speak for toilet) and just pulling my shorts back up when we launched off a big wave. Suddenly I was floating weightless! It felt like minutes, but it was likely 5-10 seconds. We hit the bottom hard and I crumpled to the floor. No damage done to me or the boat.

Winds whipped up shortly after that. The storm that was coming in arrived a couple hours early and made life uncomfortable. 

Whales! There were a lot of humpback whales playing around us, spread out for miles. Blowing spouts, slapping tails, and a bunch of full body breaches as they jumped out of the water. It was fun to watch and a nice distraction from the lumpy water.

I have only been seasick once that I can recall. It was just off Washington, as the sailboat I was on turned South. The water was all mixed up and it was like being in a washing machine. In those conditions being inside the cabin is one of the worst places you can be. I stayed up on the flybridge, but even so, the stomach felt a little funny.

Once we got around the point the water calmed down and we were able to punch up to 17 knots again. During the slop we had slowed to 12 knots to prevent slamming into the waves.

Morro Bay Layover Day – August 20, 2020

The storm out on the coast is blowing gale force (25 mph+) winds and there are 8-foot waves at 5 seconds apart. That would make for a very uncomfortable and dangerous ride. We are sticking in Morro Bay today.

Harold and I walked the town. 20,000 steps in! Founds some nice gifts for ourselves and our ladies.

The forest fires are filling the area with smoke, blocking the sun and lowering the temperature. It was chilly. I wound up in my sleeping bag with pants and a sweatshirt on!

Morro Bay Layover Day Part Two – August 21, 2020

Today is the 5th year anniversary of the wedding with my lovely and wonderful wife, Alix Lobaugh. The last 5 years have been some of the most amazing and formative years of my life. Thank you, Alix. I love you!

We are sticking to Morro Bay for one more day. Conditions are not horrible out there, but would not be comfortable. Tomorrow looks promising, with the following days forecast to be quite pleasant. It hope they are and we can make some good headway. I would like to be able to finish the trip and not have to fly home. California is a long state! It takes several days to traverse. Once we get to Oregon and Washington the trip will move much faster.

Onward! A dash to Monterey Bay – August 22, 2020

We boogied out of Morro Bay around 7 am. Waters were initially calm. All was calm and boring in the beginning, but we knew that somewhere near Point Sur the water conditions would go to crap, and boy did they. The waters pile up from two directions around the point and collide together, making for some very confused and lumpy seas. We had to slow quite a bit to keep from slamming down. Ran at 10 knots. It was a lumpy and uncomfortable ride till we got around the point. To add to the misery, a thick fog rolled in.

Just past Point Sur a 15 foot Bayliner flagged us down. Three men were fishing and had become lost in the fog. We stopped to help them. Only one could speak English, and that barely. They followed us into the Monterey marina. The direction they were heading before was taking them AWAY from land. That would not have made for a happy day.

Cruised past the Monterey Bay Aquarium where my sister-in-law works. She saw us through the binoculars.

Monterey Bay to Bodega Bay – August 23, 2020

Pulled away from the dock about 7am. Headed from Monterey Bay to Bodega Bay.

This will be our longest single push. About 120 nautical miles. The seas started calm and we have been making 17 knots. There was a thick fog in Monterey Bay that dropped visibility to 150 feet or so. 

Made it safely to Bodega! Little fishing marina. I like the people here. Some neat looking fishing boats. I think If I lived on the ocean I would be happy to have a commercial fishing vessel.

The fog lifted some out of Monterey Bay, then came back with a vengeance and stayed with us till Bogeda Bay, where it magically lifted. 

No heater in the boat. I was freezing. Could not find my jacket. Looked all over my bunk and suitcase. Guess what…. after shivering for hours, we get to the dock and the jacket was hanging right above my head. What a dummy I am!

There was some debate about Fort Bragg or Eureka tomorrow. Eureka would be a long slog. We could make it, but Cape Mendocino is right before Eureka and we may take a beating there. After most of a day out there it would be rough. So Fort Bragg it is.

Getting near the end of my work vacation time. Did some future scoping- I think that I may make it, but the weather has to 100% cooperate with us. I would like to finish the trip!

Bodega Bay to Fort Bragg – Aug 24, 2020

Left the dock around 7am again. The weather is looking ok early in the day. 

Mostly easy going for the morning. Saw a pod of Blue Whales and Fin Whales traveling together. That was pretty neat! The Blue Whale is the largest mammal on earth, coming in  nearly 100 feet long. That is more than twice the length of the boat!

We ran into a fog wall. Amazing how one second you are in clear skies and the next a murky white soup. As soon as we entered we were hit by two very steep eight-foot tall waves. Of course, they came from different directions too… gave us quite the knocking about. There were a lot of fishing vessels in the fog. We passed nearby them and never saw another boat the whole time, the fog was that thick. 

Fort Bragg is crazy. Whoever thought of putting a marina and building a fishing village here was nuts. The entrance is between rocks and has lots of wave action. Once you get inside that, there is barely enough room for two boats side by side. The channel is dredged to about 20 feet, though there were many times it was a lot shallower.  If you miss the marina entrance you will almost immediately go aground! 

We ran into a snag with fuel. There is no fuel dock. You have to call a fuel truck and they will come to you. Unfortunately there is no truck till 1pm tomorrow. 

Cape Mendocino is the next leap. It has a bad reputation, justly received. When I brought the sailboat down we nearly got stuck there from a strong north-flowing current. 

The weather window to get around the cape is early in the morning tomorrow. Miss it and the wait is almost a week before it is safe again. We missed it. 

I am unfortunately throwing in the towel. My journey with Second Stage ends here. It will not be possible to make it home inside my time table. I am bummed about it. Even if we were able to leave, there are several other stops along the trip that we would get stuck at due to weather. They will not be home for at least another week, maybe two.  

As luck has it, there is one car rental place in the area and it is two miles from the marina. I put in a reservation to get a car tomorrow. Will fly home from Sacramento. 

The journeys end – Aug 25, 2020

Renting a car at Fort Bragg and driving it to the Sacramento airport. 

There is one rental car agency in the area. I made the two-mile walk and their lot was empty! Wanted to leave at 10 am. Turns out that most people rent cars one way, away from here. There is a truck on the way, carrying cars back. 

11:30 am the cars arrived! The lady here was extra nice and offered me my pick of any car at no additional cost. I picked the brand new Dodge Challenger. Looks fun.

Back to the boat to pick up my bags, then heading toward the airport!

The drive from Fort Bragg to Sacramento airport took four hours. There are lots of curves on the 20. That Challenger was super fun to drive. It swept around the corners with style and grace. Smoke from the forest fires in California was intense. It was like driving through a thick brown fog for 4 hours. Luckily the A/C system in the Challenger had a recirculation mode that worked excellently.

Was delayed by some construction. Flight at 5:20 pm, arrived at the rental drop off at 4 pm. I was sweating for time, but everything went smoothly from here. Walked to the shuttle area and one was there- stepped aboard and the door shut. Nobody was in the bag drop off line. Took the escalator up to the train to TSA and it left right as I stepped on. Walked right through TSA. Made it to my gate with 15 minutes to spare before boarding! Just enough time to grab some chicken nuggets. 4:30 pm. I had not eaten all day!

Nano Reef 13 gallon cleaned up

Nano Reef cleaned up!

The tank is doing great now! It has been several weeks without new issues cropping.

I did lose the Watchman Goby, everybody else is quite healthy though 🙂

The fish and invertebrates are still in the tank, just hiding in this photo.

Using An iPad For Web Development

My obsession with using an iPad for web development started all the way back with the first generation iPad. Tools then were sparse, but I made it work by jumping through some hoops. Today’s iPad is a much superior machine, and the available apps have come a long way toward making the iPad comfortable enough, and capable of being a web development machine.

Long have I thought about writing an article on how I use my iPad for web development. I think I just needed the right impetus, which came about today.

A long time business friend texted me a challenge, to gather a list of URLs for all images uploaded to a particular website. The kicker- I had to do it faster, and more elegantly, than another mutual friend we have. An iPad was already in my hands, all I needed was a stand to hold it up, and a Bluetooth keyboard for convenience. While our mutual friend has an impressive desktop machine, in less than 10 minutes, I was able to come up with a solution, code it, and publish it as a generic utility.

There are really only three tools that I need to enable web development on my iPad. I will describe those, but first, a bit about the iPad I use.

The Platform — 2018 iPad

You might think the latest, greatest, most powerful iPad it needed, but that is not the case. My iPad is from 2018, a couple years old as of the writing of this article. It is also the base model iPad. Nothing fancy to it.

I would love to have the latest iPad Pro, but since I am not doing hardcore graphics or video editing, it is not necessary.

Now let’s talk about the tools I use for web development from an iPad.

The Server — 100% Remote

The development server is fully remote. It is possible to run a local server, but it is a lot of work, and not worth the hassle. I have a Droplet running on Digital Ocean that hosts all work in progress. Code is edited via ssh.

Security is paramount- the server is locked down and will only respond to my IP address.

For an in-depth look at this set up, read my article at https://medium.com/@benlobaugh/the-unchained-developer-free-your-workflow-and-become-device-agnostic-ce0450e238de

SSH Client — Terminus

The key to making the iPad a useable web development platform for me is SSH. No code is created locally. In fact, all of the resources related to a project live remotely. This means the choice of an SSH client is critical. A few years ago, SSH app choices were limited, and none were what I would consider to be quality enough to spend hours using. They were for quick commands only. Then, I used web based SSH clients, which have their own set of quirks. Today, the quality and selection of apps has improved.

My SSH client app of choice is Terminus. Terminus works across several platforms. It has built in sftp, mosh, and telnet clients; will run fully in the background without disconnecting; and has a lot of other great features. It really shines as an SSH client though. I am able to define hosts and reconnect with a quick tap.

Once connected, there are a plethora of tools at my disposal. The most critical tools to my success are:

  • Docker
  • Vim
  • Tmux
  • Git

With them, I have been able to set up any project. When other tools are called for, such as composer or npm, I will often run them inside of a Docker container.

Another critical need is the ability to transfer files between machines. File transfers are a breeze, with common Linux tools.

Web Browser

The last, and possibly most critical, piece for web development on an iPad is, of course, a web browser. Apple updated Safari to handle desktop versions of sites, freeing the iPad from a mobile only experience.

It seems that everyone has a browser they are devoted to, for better or worse. I happen to find Safari to be acceptable, and with the integrations Apple has created it was a no-brainer choice. Safari handles nearly all my needs. Combine it with a tool such as Cross Browser Testing and anything can be accomplished.

Helpful Hardware

The apps get me running from the software side, and are all I need, but there are two pieces of hardware that I find extremely valuable from a comfort and efficiency standpoint: a tablet stand and a Bluetooth keyboard. Really any will do. Specifically I use the following:

  • Amazon Basics Adjustable Tablet Holder — I like how flexible it is for sizing and angles. Folds up compactly as well.
  • Logitech multidevice backlit keyboard. Unfortunately Logitech no longer makes this keyboard. A bummer since I believe it is the best keyboard they ever made. Logitech has other Bluetooth keyboards that will work also, such as this Bluetooth keyboard, that also has a tablet stand built in.

Fin

That’s it. Those three tools; remote server, ssh, web browser; provide 99% of my needs as a web developer. In fact, if you look at my laptop, you will notice my workflow there is very similar. I have even used the same setup on my iPhone to code while riding a bus!

There are a couple tools I use on the laptop that have good iPad equivalents as well.

  • Postman -> HTTPbot for all query testing needs
  • Browser debugger — this is an area that is still weak for the iPad. There are some promising tools though, such as MIHTool
  • Of course, Slack for accessing developer chat communities

I hope this helps you kick start your own iPad web development journey. Let me know in the comments if there is any way I can help you.

If you already used your iPad for web development, what tools and techniques do you have?

Page 3 of 168

Powered by WordPress & Beards