Simplifying API Integration with Laravel’s Http Facade

https://hashnode.com/utility/r?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1699585660555%2F4345ca45-14b3-45cd-ac5a-824e7fe8ee60.png%3Fw%3D1200%26h%3D630%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp%26fm%3Dpng

I’ve been working a lot lately integrating third-party APIs. There are several different approaches to this such as using the third-party provided SDK. However, I feel sticking to Laravel’s Http facade is often a better choice. By using the Http facade, all third-party integrations can have a similar structure, and testing and mocking becomes a lot easier. Also, your application will have fewer dependencies. You won’t have to worry about keeping the SDK up to date or figuring out what to do if the SDK is no longer supported.

In this post, we will explore integrating the Google Books API. I will create a reusable client and request class to make using the API very simple. In future posts, I will go into more detail about testing, mocking, as well as creating API resources.

Let’s get started!

Add Google Books Configuration to Laravel

Now that we have an API key, we can add it to the .env along with the API URL.

GOOGLE_BOOKS_API_URL=https://www.googleapis.com/books/v1
GOOGLE_BOOKS_API_KEY=[API KEY FROM GOOGLE]

For this example, I am storing an API key that I obtained from the Google Cloud console, though it is not needed for the parts of the API we will be accessing. For more advanced API usage, you would need to integrate with Google’s OAuth 2.0 server and create a client ID and secret that could also be stored in the .env file. This is beyond the scope of this post.

With the environment variables in place, open the config/services.php file and add a section for Google Books.

'google_books' => [
    
    'base_url' => env('GOOGLE_BOOKS_API_URL'),
    
    'api_key' => env('GOOGLE_BOOKS_API_KEY'),
],

Create an ApiRequest Class

When making requests to the API, I find it easiest to use a simple class to be able to set any request properties I need.

Below is an example of an ApiRequest class that I use to pass in URL information along with the body, headers, and any query parameters. This class can easily be modified or extended to add additional functionality.

<?php

namespace App\Support;


class ApiRequest
{
    
    protected array $headers = [];

    
    protected array $query = [];

    
    protected array $body = [];

    
    public function __construct(protected HttpMethod $method = HttpMethod::GET, protected string $uri = '')
    {
    }

    
    public function setHeaders(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->headers = $key;
        } else {
            $this->headers[$key] = $value;
        }

        return $this;
    }

    
    public function clearHeaders(string $key = null): static
    {
        if ($key) {
            unset($this->headers[$key]);
        } else {
            $this->headers = [];
        }

        return $this;
    }

    
    public function setQuery(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->query = $key;
        } else {
            $this->query[$key] = $value;
        }

        return $this;
    }

    
    public function clearQuery(string $key = null): static
    {
        if ($key) {
            unset($this->query[$key]);
        } else {
            $this->query = [];
        }

        return $this;
    }

    
    public function setBody(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->body = $key;
        } else {
            $this->body[$key] = $value;
        }

        return $this;
    }

    
    public function clearBody(string $key = null): static
    {
        if ($key) {
            unset($this->body[$key]);
        } else {
            $this->body = [];
        }

        return $this;
    }

    
    public function getHeaders(): array
    {
        return $this->headers;
    }

    
    public function getQuery(): array
    {
        return $this->query;
    }

    
    public function getBody(): array
    {
        return $this->body;
    }

    
    public function getUri(): string
    {
        if (empty($this->query) || $this->method === HttpMethod::GET) {
            return $this->uri;
        }

        return $this->uri.'?'.http_build_query($this->query);
    }

    
    public function getMethod(): HttpMethod
    {
        return $this->method;
    }

    
    

    public static function get(string $uri = ''): static
    {
        return new static(HttpMethod::GET, $uri);
    }

    public static function post(string $uri = ''): static
    {
        return new static(HttpMethod::POST, $uri);
    }

    public static function put(string $uri = ''): static
    {
        return new static(HttpMethod::PUT, $uri);
    }

    public static function delete(string $uri = ''): static
    {
        return new static(HttpMethod::DELETE, $uri);
    }
}

The class constructor takes an HttpMethod, which is just a simple enum with the various HTTP methods, and a URI.

enum HttpMethod: string
{
    case GET = 'get';
    case POST = 'post';
    case PUT = 'put';
    case DELETE = 'delete';
}

There are helper methods to create the request using the HTTP method name and passing a URI. Finally, there are methods to add and clear headers, query parameters, and body data.

Create an API Client

Now that we have the request, we need an API client to send it. This is where we can use the Http facade.

Abstract ApiClient

First, we’ll create an abstract ApiClient class that will be extended by our various APIs.

<?php

namespace App\Support;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;


abstract class ApiClient
{
    
    public function send(ApiRequest $request): Response
    {
        return $this->getBaseRequest()
            ->withHeaders($request->getHeaders())
            ->{$request->getMethod()->value}(
                $request->getUri(),
                $request->getMethod() === HttpMethod::GET
                    ? $request->getQuery()
                    : $request->getBody()
            );
    }

    
    protected function getBaseRequest(): PendingRequest
    {
        $request = Http::acceptJson()
            ->contentType('application/json')
            ->throw()
            ->baseUrl($this->baseUrl());

        return $this->authorize($request);
    }

    
    protected function authorize(PendingRequest $request): PendingRequest
    {
        return $request;
    }

    
    abstract protected function baseUrl(): string;
}

This class has a getBaseRequest method that sets up some sane defaults using the Http facade to create a PendingRequest. It calls the authorize method which we can override in our Google Books implementation to set our API key.

The baseUrl method is just a simple abstract method that our Google Books class will set to use the Google Books API URL we set earlier.

Finally, the send method is what sends the request to the API. It takes an ApiRequest parameter to build up the request, then returns the response.

GoogleBooksApiClient

With the abstract client created, we can now create a GoogleBooksApiClient to extend it.

<?php

namespace App\Support;

use Illuminate\Http\Client\PendingRequest;


class GoogleBooksApiClient extends ApiClient
{
    
    protected function baseUrl(): string
    {
        return config('services.google_books.base_url');
    }

    
    protected function authorize(PendingRequest $request): PendingRequest
    {
        return $request->withQueryParameters([
            'key' => config('services.google_books.api_key'),
        ]);
    }
}

In this class, we just need to set the base URL and configure the authorization. For the Google Books API, that means passing the API key as a URL parameter and setting an empty Authorization header.

If we had an API that used a bearer authorization, we could have an authorize method like the following:

protected function authorize(PendingRequest $request): PendingRequest
{
    return $request->withToken(config(services.someApi.token));
}

The nice part about having this authorize method is the flexibility it offers to support a variety of API authorization methods.

Query Books By Title

Now that we have our ApiRequest class and GoogleBooksApiClient, we can create an action to query books by title. It would look something like this:

<?php

namespace App\Actions;

use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Http\Client\Response;


class QueryBooksByTitle
{
    
    public function __invoke(string $title): Response
    {
        $client = app(GoogleBooksApiClient::class);

        $request = ApiRequest::get('volumes')
            ->setQuery('q', 'intitle:'.$title)
            ->setQuery('printType', 'books');

        return $client->send($request);
    }
}

Then, to call the action, if I wanted to find information about the book The Ferryman, which I just read and highly recommend, use the following snippet:

use App\Actions\QueryBooksByTitle;

$response = app(QueryBooksByTitle::class)("The Ferryman");

$response->json();

Bonus: Tests

Below, I added some examples for testing the request and client classes. For the tests, I am using Pest PHP which provides a clean syntax and additional features on top of PHPUnit.

ApiRequest

<?php

use App\Support\ApiRequest;
use App\Support\HttpMethod;

it('sets request data properly', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders(['foo' => 'bar'])
        ->setQuery(['baz' => 'qux'])
        ->setBody(['quux' => 'quuz']);

    expect($request)
        ->getHeaders()->toBe(['foo' => 'bar'])
        ->getQuery()->toBe(['baz' => 'qux'])
        ->getBody()->toBe(['quux' => 'quuz'])
        ->getMethod()->toBe(HttpMethod::GET)
        ->getUri()->toBe('/');
});

it('sets request data properly with a key->value', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders('foo', 'bar')
        ->setQuery('baz', 'qux')
        ->setBody('quux', 'quuz');

    expect($request)
        ->getHeaders()->toBe(['foo' => 'bar'])
        ->getQuery()->toBe(['baz' => 'qux'])
        ->getBody()->toBe(['quux' => 'quuz'])
        ->getMethod()->toBe(HttpMethod::GET)
        ->getUri()->toBe('/');
});

it('clears request data properly', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders(['foo' => 'bar'])
        ->setQuery(['baz' => 'qux'])
        ->setBody(['quux' => 'quuz']);

    $request->clearHeaders()
        ->clearQuery()
        ->clearBody();

    expect($request)
        ->getHeaders()->toBe([])
        ->getQuery()->toBe([])
        ->getBody()->toBe([])
        ->getUri()->toBe('/');
});

it('clears request data properly with a key', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders('foo', 'bar')
        ->setQuery('baz', 'qux')
        ->setBody('quux', 'quuz');

    $request->clearHeaders('foo')
        ->clearQuery('baz')
        ->clearBody('quux');

    expect($request)
        ->getHeaders()->toBe([])
        ->getQuery()->toBe([])
        ->getBody()->toBe([])
        ->getUri()->toBe('/');
});

it('creates instance with correct method', function (HttpMethod $method) {
    $request = ApiRequest::{$method->value}('/');

    expect($request->getMethod())->toBe($method);
})->with([
    [HttpMethod::GET],
    [HttpMethod::POST],
    [HttpMethod::PUT],
    [HttpMethod::DELETE],
]);

The ApiRequest tests check that the correct request data is being set and the correct methods are being used.

ApiClient

Testing for the ApiClient will be a little more complex. Since it is an abstract class, we will use an anonymous class in the beforeEach function to create a client to use that extends ApiClient.

Notice, that we also use the Http::fake() method. This creates mocks on the Http facade that we can make assertions against and prevent making API requests in the tests.

<?php

use App\Support\ApiClient;
use App\Support\ApiRequest;
use App\Support\HttpMethod;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;

beforeEach(function () {
    Http::fake();

    $this->client = new class extends ApiClient
    {
        protected function baseUrl(): string
        {
            return 'https://example.com';
        }
    };
});

it('sends a get request', function () {
    $request = ApiRequest::get('foo')
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::GET->name)
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a post request', function () {
    $request = ApiRequest::post('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::POST->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a put request', function () {
    $request = ApiRequest::put('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::PUT->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a delete request', function () {
    $request = ApiRequest::delete('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::DELETE->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('handles authorization', function () {
    $client = new class extends ApiClient
    {
        protected function baseUrl(): string
        {
            return 'https://example.com';
        }

        protected function authorize(PendingRequest $request): PendingRequest
        {
            return $request->withHeaders(['Authorization' => 'Bearer foo']);
        }
    };

    $request = ApiRequest::get('foo');

    $client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->header('Authorization')->toBe(['Bearer foo']);

        return true;
    });
});

For the tests, we are confirming that the request properties are being set correctly on the various request methods. We also confirm the baseUrl and authorize methods are being called correctly. To make these assertions, we are using the Http::assertSent method which expects a callback with a $request that we can test against. Notice that I am using the PestPHP expectations and then returning true. We could just use a normal comparison and return that, but by using the expectations, we get much cleaner error messages when the tests fail. Read this excellent article for more information.

GoogleBooksApiClientTest

The test for the GoogleBooksApiClient is similar to the ApiClient test where we just want to make sure our custom implementation details are being handled properly, like setting the base URL and adding a query parameter with the API key.

Also, not the config helper in the beforeEach method. By using the helper, we can set test values for the Google Books service config that will be used in each of our tests.

<?php

use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\Request;

beforeEach(function () {
    Http::fake();
    config([
        'services.google_books.base_url' => 'https://example.com',
        'services.google_books.api_key' => 'foo',
    ]);
});

it('sets the base url', function () {
    $request = ApiRequest::get('foo');

    app(GoogleBooksApiClient::class)->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->url()->toStartWith('https://example.com/foo');

        return true;
    });
});

it('sets the api key as a query parameter', function () {
    $request = ApiRequest::get('foo');

    app(GoogleBooksApiClient::class)->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->url()->toContain('key=foo');

        return true;
    });
});

Summary

In this article, we covered some helpful steps for integrating third-party APIs in Laravel. By using these simple custom classes, along with the Http facade, we can ensure all integrations function similarly, are easier to test, and don’t require any project dependencies. In a later post, I will expand on these integration tips by covering DTOs, testing with mock responses, and using API resources.

Thanks for reading!

Laravel News Links

How to use the Basilisk II System 7 emulator on macOS

https://photos5.appleinsider.com/gallery/56786-116140-lede-xl.jpg

Article Hero Image

The Basilisk II emulator can be used to run old versions of Mac OS 8 and System 7 on a modern Mac. Here’s how to get started using it.

Before macOS and OS X, there was Mac OS 9, and before that were its precursors: Mac OS 8, and System 7.x.

System 7 was released in 1991 and was the first version of Mac OS to feature color. Prior to that, the Mac was black and white only.

Early Macs used a CPU from Motorola called the 68000, better known simply as the 68K.

Emulating Mac OS 8 and System 7

Today there are a number of emulators you can run on your modern Mac to emulate Mac OS 8 and System 7 running on a vintage 68K Mac. Among these are Basilisk II, Sheepshaver, macintosh.js, and others.

There are also many online Mac OS 9 emulators.

In this article, we’ll cover Basilisk II written by Christian Bauer.

Getting Basilisk II

To download Basilisk II, head over to its page which has links to acquire the executable. There is also a GitHub repo which contains source code written in C++.

Note that the download links are on the Basilisk II forums and can be a bit confusing. The most recent post from the forums page as of this writing reads:

“SDL2 port, 10 August 2023, universal (x86_64 and arm64) from github.com/kanjitalk755/macemu source Universal, will run natively on both Intel and Apple Silicon

Recommended for macOS 10.13 (High Sierra) through 13 (Ventura)

Download: https://www.emaculation.com/basilisk/Ba … 230810.zip”

SDL2 is the Simple DirectMedia Layer – a code library framework that handles graphics, sound, and user input. You can install SDL2 from its website as a macOS framework in /Library/Frameworks, but Basilisk II has a copy of the framework built in.

Basilisk II has been ported to run on macOS, older versions of Windows, most X11-based versions of Unix, and some older vintage operating systems.

Once Basilisk II has been downloaded, you’ll need to do some configuration first. Along with Basilisk II you’ll also need copies of original Macintosh ROMs from a 512K or 1MB classic Mac, Mac II, and the original version of Mac OS you wish to run (System 7.1, 7.5.3, Mac OS 8, or 8.1).

Also from the forums page:

“For a new BasiliskII setup, a BasiliskIIGUI application and a keycodes file are needed”

Keycode files are only needed if you’re not using the US-English keyboard.

To get the original Mac ROM files you’ll need to own one of the mentioned original Macs and dump its ROM to a file. For copyright reasons, we won’t get into how to do that here.

Apple does provide a free download of MacOS 7.5.3, one of the last commercially released versions of System 7, online.

The original Macs had much of their OS and APIs in hardware in ROMs on the motherboards. The OS could be extended later by installing software but all the old Mac OS versions and apps made calls to the ROM APIs to standardize many software functions.

Be sure to read the Basilisk II README file for additional details before you continue. You’ll also want to read the Technical Manual which is linked at the top of the main Basilisk II page.

The Basilisk II GitHub page also has a Mac OS 9 emulator called SheepShaver which emulates later PowerPC CPU-based Macs. Mac OS 9.0, 9.1, and 9.2.1 which ran on PowerPC Macs were the final versions of Apple’s classic OS before Apple released Mac OS X in 2000.

Note that when Apple released new revolutionary Macs in 1998 (iMac, PowerMac G3, and the first iBook), it switched to a new ROM format called “NewWorld” ROMs. Depending on whether you want to emulate a pre-1998 or post-1998 Mac, you’ll need either “Old-world” or “New-World” ROMs.

Macs from 1998 or later also switched to new firmware called Open Firmware. You can boot any 1998 or later classic PowerPC-based Mac into Open Firmware by holding down Command-Option-O-F on the Mac’s keyboard after restarting.

Installing Basilisk II

Basilisk II emulates either a Macintosh Classic, Classic II (the last of the black and white “compact Macs”), or a Macintosh II running MacOS 7.x, 8.0, or 8.1, or the successor to the Mac II line called Quadra. The Macintosh II was Apple’s first color Macintosh and came in a large, wide desktop form factor without a built-in display.

The original Mac II models were, in order:

  1. Mac II
  2. Mac IIcx
  3. Mac IIci
  4. Mac IIvx
  5. Mac IIsi

Original Mac II. Note the two 3.5-inch floppy drives at the right, top.

The IIcx and IIvx were short-lived models. By far the most popular of the Mac II models was the Mac Iici which was much more compact and easier to deal with than the original Mac II.

Later Apple replaced the Mac II line with the new Quadra line because it was based on the next evolution of the 68000 – the 68040. These models included the Quadra 700 which looked like the IIci, and two huge desktop tower models: the 900 and 950.

The 700 became one of Apple’s most popular models.

The IIsi was a low-end, more compact version of the Mac IIci with more limited features and only one expansion slot.

Macintosh IIsi – utilizing what Apple called the “Snow White” design language.

For Basilisk II installation instructions, you’ll need to clone the macemu GitHub repository on Christian’s GitHub page, dive into the /macemu/BasiliskII/ folder, and read the file INSTALL.

If you are running on a non-macOS UNIX system, you will need to compile Basilisk II from source on that platform, which is detailed in the INSTALL file.

As mentioned in the INSTALL file:

“The ROM file has to be named “ROM” and put in the same directory as the Basilisk II executable but you can specify a different location for the ROM file with the “rom” option in the preferences file (or with the preferences GUI).”

If you try to run Basilisk II without the ROM file, nothing happens.

According to that page, you’ll need to create folder in your user’s home folder named “BasiliskII”, and put the app, any keyboard keycode files, the ROM file, and other mentioned associated files into it.

You’ll also need the mentioned Basilisk II GUI beta application which is used to set up and configure the emulator.

Note that the GUI config app can only be downloaded from another linked forum post. Current version as of this writing is 0.20.

There is also another utility mentioned from a third party called “Basilisk II Disk Image Chooser” which helps select and configure disk images.

Basilisk II GUI has a popup menu for setting the Mac model, but currently only two models are enabled from the popup: Mac Iici (System 7.x) and Quadra 900 (for Mac OS 8.x).

The Basilisk II GUI also has a Browse button for selecting the ROM file using a standard macOS file Open pane, which allows you to choose a ROM file located in another location.

Select a Mac model, ROM file, and CPU type, then click “Save”.

The first time you run Basilisk II, it will create an invisible prefs file at the root of your user folder named “.basilisk_ii_prefs”. Although this file doesn’t have any file extension, it’s a plain text file which you can open with any text editor to change some of Basilisk II’s settings.

Some important values in the prefs file are:

  1. displaycolordepth
  2. ramsize
  3. fpu (Floating point unit)
  4. nosound
  5. rom
  6. disk

The ‘rom’ value is critical – it points to the Mac ROM file to use. But we’ll get to that in a moment below.

You can also set or add classic 1.44MB floppy disk drives to the prefs file from the GUI or by adding “floppy” keys with paths to the floppy diskette images you want to use.

Original Macs used standard 3.5-inch floppy diskettes designed by Sony which were in a sealed plastic shell with a sliding metal door to expose the disk media when a diskette was inserted into a floppy disk drive. You can make images of these disks and load them into Basilisk II.

They were called “floppies” because the disk media itself was just a thin plastic film with a magnetic recording surface bonded to it. If you removed the outer hard plastic shell, the disk would “flop” over if you picked it up.

A stack of 3.5-inch floppy diskettes.

You will also need to set a disk (volume) to boot from. This is different than setting the ROM file.

The ROM file contains only the Mac OS ROM code. You’ll still need a bootable disk containing the actual Mac OS operating system in order to boot the emulator.

If you haven’t set both, the emulator won’t start. Back in Basilisk II GUI, you can set up your disk volumes in the Volumes tab:

Set disks, volumes, and a shared folder in the Volumes tab.

If you’re not sure what to add here, add the DiskTools_MacOS8.image as stated on the forums setup page.

A better way is to run the third-party Basilisk II Disk Image Chooser app. This app detects if you don’t have any startup disk set in your emulator configuration – and if you don’t it will prompt you with options to set one up:

Set a startup disk in Disk Image Chooser.

You may want to quit Basilisk II GUI first before running Basilisk II Disk Image Chooser, then restart it after you’ve set a startup disk.

If you click the Select button in Basilisk II Disk Image Chooser, you can choose a disk image to use and the utility will set it for you in the emulator’s prefs file.

Once you try to exit Basilisk II Disk Image Chooser, it will ask you if you want to write the prefs file. If you do, it will then prompt whether you want to launch Basilisk II, return to the main menu, or exit.

Select Launch Basilisk II to start the emulator, or Exit to quit Basilisk II Disk Image Chooser so you can go back to Basilisk II GUI to finish configuration.

If you reopen the .basilisk_ii_prefs file, you’ll note several entries have been added. Most notably the ‘rom’ and ‘disk’ entries which point to the ROM file and startup disk to use when the emulator starts.

rom /Volumes/Virtualization/BasiliskII/9779D2C4 – MacII (800k v2).ROM

disk /Volumes/Virtualization/BasiliskII/DiskTools_MacOS8.image

Be sure the startup disk image file you select matches which Mac model (ROM) you’ve chosen to use. Some older Macs can’t run newer versions of the Mac OS, and vice versa.

If you reopen Basilisk II GUI, under the Volumes tab you’ll now see the startup disk image Basilisk II Disk Image Chooser set for you:

Shared files

As the instructions note, the UNIX root under the Volumes tab in Basilisk II GUI can be set to point to a shared folder on your modern Mac’s filesystem – any files you put in the UNIX root folder will appear on the emulated Mac OS desktop as a “UNIX” disk volume.

You can copy files from your modern Mac to the emulated Mac’s desktop by placing them in this folder. Note you need to restart the emulator for any changes to be noticed.

Starting the emulator

Once you’re satisfied with all the settings in Basilisk II GUI, click the Save button, then click Start. This starts the emulator from the startup disk you set.

When you’re done using Basilisk II and want to quit the emulator, select the Shut Down menu item from the Finder’s Special menu in the old (emulated) Mac OS you are running.

Unlike in modern macOS, the Shut Down menu item is on the right side of the menu bar in the old Mac OS under the Special menu.

While Basilisk II is a bit confusing to set up and configure at first, it’s a nice piece of software. Once you get everything installed, it’s easy to use.

Being able to run both System 7.5.3 and Mac OS 8 on modern Macs opens your computer up to a huge library of vintage software used on the Mac in decades past.

AppleInsider News

OpenAI’s GPT Builder: A Game Changer for Exponential Entrepreneurs

https://blog.finxter.com/wp-content/uploads/2023/11/image-35.png

5/5 – (1 vote)

The digital landscape is on the brink of a revolutionary shift, thanks to OpenAI’s latest offering: the GPT Builder.

Anyone can leverage GPT Builder to create personalized AI models and potentially generate significant revenue with the launch of the OpenAI GPT store!

Step 1: Accessing GPT Builder

To begin with, access to GPT Builder is exclusive to ChatGPT Plus subscribers. Once you’re in, navigate to the Explore tab and select “Create a GPT”. This is your gateway to entering the world of custom AI creation.

Step 2: Conversational Drafting

The process starts with a simple conversation within the “Create” tab to create your desired AI model through a casual chat with ChatGPT. This interaction forms the foundation of your custom GPT. Simply describe the behavior you want your GPT to have in natural language. Focus on value creation for the user or fun projects!

Step 3: Configuring Your GPT

Now click the “Configure” tab to adjust and modify your GPT. Here, you can also connect with custom “Actions”, i.e., APIs that call external services. You can also set whether your GPT will allow web browsing or DALL-E Image Generation.

You can also fine-tune your GPT such as renaming, changing the profile photo, and most importantly, infusing the GPT with specific knowledge by uploading relevant data.

Step 4: Bringing Your GPT to Life

Now you can click “Save” and publish your GPT to the world — or just keep it private for now. Make sure to test your GPT properly.

Here’s an example of my first GPT:

????‍???? Recommended: I Made a Dan Peña GPT with OpenAI and Learned This

The Upcoming GPT Store: A World of Opportunities

The launch of the GPT Store is imminent, signaling a significant opportunity for early adopters. To put this in perspective, consider the revenue generated by platforms like Shopify Apps and the App Store. The potential for the ChatGPT Store is vast and largely untapped.

Here’s the App Store revenue (source):

Similarly, developers made $320 billion from the App Store:

This revenue was shared by roughly 34 million Apple developers (cumulative). In other words, each App Store developer made $320 billion / 34 million = $10,000 on average. If you belong to the top 10% of developers, i.e., you didn’t only create an account but actually submitted an app, you can see why many early App Store developers became millionaires.

Conclusion

OpenAI’s GPT Builder is more than just a tool; it’s a gateway to innovation and entrepreneurship in the AI space.

With the ability to create and customize AI models easily, the possibilities are endless. There are at least as many possible apps as there are in the App Store — probably many more groundbreaking apps!

Be on the right side of change, stay ahead of the curve, and start experimenting with this transformative GPT Builder technology today!


Prompt Engineering with Python and OpenAI

You can check out the whole course on OpenAI Prompt Engineering using Python on the Finxter academy. We cover topics such as:

  • Embeddings
  • Semantic search
  • Web scraping
  • Query embeddings
  • Movie recommendation
  • Sentiment analysis

????‍???? Academy: Prompt Engineering with Python and OpenAI

Be on the Right Side of Change

How to use MySQL 8.2 read/write splitting with Connector/Python

https://i0.wp.com/lefred.be/wp-content/uploads/2023/11/Router-ReadWrite.drawio-1.png?resize=917%2C781&ssl=1

As you know, one of the most eagerly waited features was released with MySQL 8.2: the transparent read/write splitting.

In this post, we’ll look at how to use it with MySQL-Connector/Python.

Architecture

To play with our Python program, we will use an InnoDB Cluster.

This is an overview of the cluster in MySQL Shell:

JS > cluster.status()
{
    "clusterName": "fred", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "127.0.0.1:3310", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "127.0.0.1:3310": {
                "address": "127.0.0.1:3310", 
                "memberRole": "PRIMARY", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.2.0"
            }, 
            "127.0.0.1:3320": {
                "address": "127.0.0.1:3320", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.2.0"
            }, 
            "127.0.0.1:3330": {
                "address": "127.0.0.1:3330", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.2.0"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "127.0.0.1:3310"
}

JS > cluster.listRouters()
{
    "clusterName": "fred", 
    "routers": {
        "dynabook::system": {
            "hostname": "dynabook", 
            "lastCheckIn": "2023-11-09 17:57:59", 
            "roPort": "6447", 
            "roXPort": "6449", 
            "rwPort": "6446", 
            "rwSplitPort": "6450", 
            "rwXPort": "6448", 
            "version": "8.2.0"
        }
    }
}

MySQL Connector/Python

The Python program uses MySQL-Connector/Python 8.2.0.

This is the initial code:

import mysql.connector

cnx = mysql.connector.connect(user='python',
                              passowrd='Passw0rd!Python',
                              host='127.0.0.1',
                              port='6450')

cursor = cnx.cursor()

query = ("""select member_role, @@port port 
            from performance_schema.replication_group_members 
            where member_id=@@server_uuid""")

for (role, port) in cursor:
    print("{} - {}".format(role, port))

cursor.close()
cnx.close()

We can already test it:

$ python test_router.py 
PRIMARY - 3310

Good, we can connect to the cluster using the read/write splitting port (6540) and execute the query…. oh ?! But why are we reaching the Primary instance ?

Shouldn’t we access a Read/Only instance (one of the Secondaries) ?

autocommit

Connector/Python disables autocommit by default (see MySQLConnection.autocommit Property). And the Read/Write Splitting functionality must have autocommit disabled to work properly.

Add the following code above line 8:

cnx.autocommit = True

Then we can run the program again:

$ python test_router.py 
SECONDARY - 3320
$ python test_router.py 
SECONDARY - 3330

Great, it works as expected !

query attributes

Now let’s see how to force execution of the query on the Primary node.

The MySQL Router offers the possibility of using a query attribute to force the Read/Write Split decision: router.access_mode.

Add the following line just before executing the query (cursor.execute(query)):

 cursor.add_attribute("router.access_mode", "read_write")

Let’s execute it one more time:

$ python test_router.py 
PRIMARY - 3310

The accepted values for the router.access_mode are:

  • auto
  • read_only
  • read_write

Test with DML

Let’s try something different, we’re going to insert rows into a table.

We’ll use the following table:

CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `port` int DEFAULT NULL,
  `role` varchar(15) DEFAULT NULL,
  `timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB 

We’ll use the following python script:

import mysql.connector

cnx = mysql.connector.connect(user='python', 
                              password='Passw0rd!Python',
                              host='127.0.0.1',
                              port='6450',
                              database='test')
cnx.autocommit = True
cursor = cnx.cursor()

for i in range(3):
    query = ("""insert into t1 values(0, @@port, (
          select member_role
            from performance_schema.replication_group_members 
            where member_id=@@server_uuid), now())""")
    cursor.execute(query)

cursor.close()
cnx.close()

for i in range(3):
    cnx = mysql.connector.connect(user='python', 
                              password='Passw0rd!Python',
                              host='127.0.0.1',
                              port='6450',
                              database='test')
    cnx.autocommit = True
    cursor = cnx.cursor()
    query = ("""select *, @@port port_read from t1""") 
    cursor.execute(query)
    for (id, port, role, timestamp, port_read) in cursor:
             print("{} : {}, {}, {} : read from {}".format(id, 
                                             port, 
                                             role, 
                                             timestamp, 
                                             port_read))

    cursor.close()
    cnx.close()

Let’s execute it :

$ python test_router2.py 
1 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330
2 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330
3 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330
1 : 3310, PRIMARY, 2023-11-09 18:44:00 : read from 3320
2 : 3310, PRIMARY, 2023-11-09 18:44:00 : read from 3320
3 : 3310, PRIMARY, 2023-11-09 18:44:00 : read from 3320
1 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330
2 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330
3 : 3310, PRIMARY, 2023-11-09 17:44:00 : read from 3330

We can see that there were no errors and that we wrote to the Primary node and read from all Secondaries.

Be careful if you set the query attribute for router.access_mode to read_only just before writing (line 16), you’ll get an error as writes are not allowed on a secondary:

_mysql_connector.MySQLInterfaceError: The MySQL server is running with the --super-read-only option so it cannot execute this statement

Transactions

Now we’re going to play with transactions. We create a new script that will perform several transactions:

  1. a read operation in autocommit
  2. a read operation in a transaction (by default, this is a read/write transaction)
  3. a read operation in a read only transaction
  4. a transaction with several inserts and rollback

This is the source of the program:

import mysql.connector

cnx = mysql.connector.connect(user='python', 
                              password='Passw0rd!Python',
                              host='127.0.0.1',
                              port='6450',
                              database='test')
cnx.autocommit = True
cursor = cnx.cursor()
query = ("""select member_role, @@port port 
            from performance_schema.replication_group_members 
            where member_id=@@server_uuid""")
cursor.execute(query)

for (role, port) in cursor:
    print("{} - {}".format(role, port))


cnx.start_transaction()
query = ("""select member_role, @@port port 
            from performance_schema.replication_group_members 
            where member_id=@@server_uuid""")
cursor.execute(query)

for (role, port) in cursor:
    print("{} - {}".format(role, port))

cnx.commit()

cnx.start_transaction(readonly=True)
query = ("""select member_role, @@port port 
            from performance_schema.replication_group_members 
            where member_id=@@server_uuid""")
cursor.execute(query)

for (role, port) in cursor:
    print("{} - {}".format(role, port))
    

cnx.commit()

cnx.start_transaction()

for i in range(3):
    query = ("""insert into t1 values(0, @@port, (
          select member_role
            from performance_schema.replication_group_members
            where member_id=@@server_uuid), now())""")
    cursor.execute(query)

cnx.rollback()
cursor.close()

cnx.close()

Let’s execute the script:

$ python test_router3.py 
SECONDARY - 3320
PRIMARY - 3310
SECONDARY - 3320

We can see that the first operation (1) reached a secondary instance, the second operation (2), which was a transaction, reached the primary node.

The read-only transaction (3) reached a secondary node.

We didn’t get any errors for the multiple writes that were part of the transaction we rolled back.

Conclusion

We’ve seen how easy it is to use MySQL Connector/Python with MySQL 8.2 Read/Write Splitting for an InnoDB Cluster.

Enjoy using MySQL Read / Write Splitting with MySQL Connector Python !

Planet MySQL

Rethinking the Form Factor of the Wind Turbine

https://s3files.core77.com/blog/images/1457511_81_126227_x5sYCJRUD.jpg

Wind turbines are like skyscrapers: We’re just using engineering to make them bigger and bigger, and no one has re-thought the form factor with any concern for economics. Building a 500-foot tower and adding 400-foot blades can generate lots of megawatts, but they’re incredibly expensive to manufacture, difficult to transport, difficult to install, difficult to maintain, and limited in where they can be placed.

A company called Airloom Energy has rethought the form factor. Their clever alternative design for a utility-scale wind-energy-capturing system features "unique geometry [that] generates the same amount of electricity as conventional turbines at a fraction of the cost."

Rather than a tower, Airloom has developed a sort of suspended NASCAR track. This track is mounted to 25-meter (82′) poles, and has a series of vertical 10-meter (33′) blades hanging from it. The blades are not attached directly to the track, but to a cable that runs the perimeter of the track. As the wind blows, the blades start to move around the track. The cable gets pulled through power take-off units that harvest the resultant energy.

Airloom’s system can be built using readily available and conventional materials; no fancy composites are required. They cost 1/10th of a turbine that generates the same amount of energy, and are far easier to transport; broken down, the system fits onto a standard tractor-trailer.

The company also says an Airloom wind farm costs less than 25% of the price of a turbine-based farm, and that the end result is an electricity price of $0.013/kWh—which is about a third of the $0.038/kWh price offered by a turbine wind farm.

Most interestingly, the Airloom system could be set up over actual farmland, or directly next to power lines to decrease transmission distance.

The company has raised seed funding and plans to launch a pilot project in 2025.

Core77

Why This Medical Saw Can’t Cut Your Skin

https://theawesomer.com/photos/2023/11/saw_blade_vs_human_skin_t.jpg

Why This Medical Saw Can’t Cut Your Skin

Link

Medical professionals use a special power saw to cut through casts when it’s time to remove them. While the high-speed saw blade slices effortlessly through a hardened cast, it does nothing to your body if it makes contact with your skin. Steve Mould investigates the physics that allows this ingenious device to work without causing bodily harm.

The Awesomer

2024 Honda Transalp 750 Review: Value-Packed, Confidence-Inspiring Middleweight Adventure Bike Contender

https://s3.us-east-1.amazonaws.com/images.gearjunkie.com/uploads/2023/11/2024_HONDA_TRANSALP_MEDIA_INTRO_ALIGN_MEDIA_AL8_6031-1.jpg

Seiji Ishii on the 2024 Honda Transalp 750

Honda announced the release of the Transalp 750 in the U.S. market last month. It then invited us to test the much-anticipated ADV bike on the Backcountry Discovery Routes (BDR) PA Wilds BDR-X route.

Transalp is a lauded monicker to motorcyclists who like to venture on the rougher surfaces of dirt and gravel back roads. The original Honda Transalp hit these less-traveled venues in 1986, and the 2024 Honda XL750R Transalp evoked nostalgia as soon as I saw it. Visually, the bike looked to possess the double personality of highway cruiser and dirt bomber that the original version introduced.

I rode the 2024 Honda Transalp 750 over 2 days on varied terrain in rural central Pennsylvania, luckily catching the incredible fall hues. I rode smooth highways, windy blacktops, and dirt that ranged from tame two-track to challenging rocky sections made slick with puddles and mud. The Transalp and I covered nearly 300 miles of the 500-mile BDR-X route.

In short: The 2024 Honda Transalp 750’s dual purpose matched its dual personality. It was extremely easy to ride, allowing me to traverse more challenging sections without hesitation. The pairing of these alter egos made the Transalp a bike suitable to a wide range of abilities and styles, making the bike an appealing choice for the ADV masses.

2024 Honda Transalp 750

Specs

  • Engine 755cc liquid-cooled 24.5º inline-two-cylinder four-stroke w/270º crank
  • Valve train OHC Unicam, 4 valves per cylinder, 35.5mm inlet valves, 29mm exhaust valves
  • Induction PGM-FI; 46mm throttle bodies
  • Transmission Manual 6-speed
  • Clutch Multiplate wet
  • Front suspension 43mm Showa SFF-CATM telescopic inverted fork w/ spring-preload adjustment; 7.9" travel
  • Rear suspension Pro-Link system w/ single Showa remote-reservoir shock; 7.5" travel
  • Front brake Dual 310mm "wave" discs w/ hydraulic two-piston calipers
  • Rear brake Single 256mm "wave" disc w/ hydraulic single-piston caliper
  • Fuel capacity 4.5 gal.
  • Wet weight 459 lbs.
  • Ground clearance 8.3"
  • Tires 90/90-21 (front), 150/70R-18 (rear)
  • Wheelbase 61.5"
  • Seat height 33.7"

Pros

  • Extremely linear and tractable engine response
  • Suspension effective over most surfaces at "adventuring" speeds
  • Excellent electronic rider aids package
  • Great brakes

Cons

  • Low exhaust could be prone to damage
  • No cruise control
  • Small, uncomfortable stock footpegs
  • USB-C port is under the seat

2024 Honda XL750R Transalp Review

Seiji Ishii on the 2024 Honda Transalp 750
The 2024 Honda Transalp 750 had impeccable street manners; (photo/American Honda Motor Co.)

2024 Transalp 750 Engine Characteristics

The heart of any bike is the engine, and Honda graced the 2024 Transalp 750 with one of the most agreeable motors I’ve ever ridden on an ADV bike.

The 755cc liquid-cooled, parallel twin powerplant amicably delivered torque and power. Twisting the fly-by-wire throttle elected a linear power curve that never surprised me, regardless of which of the four factory-set drive modes (Sport, Standard, Rain, Gravel) I chose. This amazingly tractable nature was partly due to the influence of the electronic traction control on the rear wheel.

Seiji Ishii on the 2024 Honda Transalp 750
The extremely tractable engine made riding in the wet and mud manageable; (photo/American Honda Motor Co.)

The single-cam engine (Unicam, borrowed from Honda’s MX lineup) produced a familiar feeling akin to the 1084cc Honda Africa Twin, which I’ve ridden extensively in Baja. The Transalp felt like I thought it should, given the roughly 250cc smaller displacement. And just like on the bigger cousin, the Transalp’s motor and EFI induction were smooth in all rpm ranges. I never felt any annoying vibrations or blips in power delivery.

This extremely linear and predictable motor characteristic made the 2024 Honda Transalp 750 easy to manage, but it did lack the excitement of other current ADV powerplants. There was no rpm to target for a power surge, even while nursing the clutch.

It was very “Honda-like,” in its delivery of 92 horsepower at 9,500 rpm and 53 foot-pounds of torque at 7,250 rpm, which some riders appreciated over the potential rush of riding a surge in power delivery.

Transalp 750 Suspension Performance

Seiji Ishii on the 2024 Honda Transalp 750
No appreciable fork dive with the stock suspension, it was good all-around; (photo/American Honda Motor Co.)

The Showa suspension on the 2024 Honda Transalp 750 felt on point for my 168-pound frame in off-road riding gear, without bags or luggage, while on the tarmac. I didn’t hesitate to push into turns, nor did I go out of my way to avoid potholes or wrecked pavement. I didn’t feel any bothersome fork dive upon braking, nor did I feel like I ate through much of my rear shock travel while accelerating.

Much like the engine, the suspension quietly and predictably managed the chassis. This allowed relaxed riding while cruising or on lean during spirited cornering. Again, it was very “Honda-like.”

I felt the same on winding two-track and tamer dirt and gravel on most of the first day along the PA Wilds BDR-X route. I didn’t notice any blaring shortcomings in damping or spring rate while standing on the pegs, which I do exclusively when off-road.

The front-to-rear balance felt fine, and the damping rate was appropriate for most terrains and speeds encountered that day. The off-road surfaces ranged from hardpacked and relatively smooth dirt to looser gravel roads and some sections of embedded and loose rocks up to tennis-ball size.

On the second day, there was an optional “expert” section of mud and wet rocks and roots of significant size. The rear shock was harsh, and the front fork blew through the middle of its travel too quickly. But it wasn’t anything I considered a shortcoming for an ADV bike.

Overall, I felt the Showa 43mm inverted fork and Pro-Link rear shock handled their respective 7.9″ and 7.5″ of travel well for the all-around usage pattern of an ADV bike.

2024 Honda Transalp 750 Electronics Package

A standout element of the Transalp 750 was the electronic rider assistance functions, especially given the four-digit MSRP.

2024 Honda Transalp 750 display
The 5″ TFT display was easily readable regardless of light or dust conditions; (photo/Seiji Ishii)

Riding Modes

In addition to the four factory-programmed riding modes, there is a user-programmable mode. Every imaginable aspect can be molded to personal preference: engine output, engine braking, rear ABS on/off, and traction control (aided by a slipper clutch). A surprising OEM inclusion is a programmable quick shifter.

For the streets and tamer dirt and gravel roads, the stock riding modes worked fine for me. But anytime it got a bit rocky or loose, the traction control and ABS were intrusive, so I switched them to the lowest settings in the User mode, and all was well.

Speaking of ABS and braking, I found the dual front and single rear caliper brakes excellent from the start. I didn’t have to modulate my naturally occurring braking efforts in any way, and they were always met with predictable bite and progression, both on the road and off.

Quick Shifter & TFT Information Panel

I really enjoyed the programmable quick shifter. After I got over the ingrained habit of tapping the clutch on shifts, I really appreciated the stock addition. Others should follow suit, in my opinion. It added a lot to my riding experience and now that I’m back on a bike without one, I miss it. I found the factory setting to be fine for me, but the “softness” of engagement and other factors is user-modifiable.

The 5-inch color TFT screen was a winner. No matter the lighting conditions, the characters on the screen were clear and easily readable while riding. And as a notable side note, the windscreen was perfect for my 6-foot frame. I experienced little buffeting even at 80 mph.

With the appropriate riding mode chosen for road and gravel, and then the user mode when things got rough, the 2024 Honda Transalp got 53 mpg over the 2 days of riding the PA Wilds BDR-X route.

XL750 R Transalp Nitpicks

Seiji Ishii on the 2024 Honda Transalp 750
The MSRP of $9,999 is remarkable for what you get; (photo/Seiji Ishii)

It was hard for me to find notable negatives on the 2024 Honda Transalp 750, given the $9,999 price. But, alas, it is my job.

Transalp 750 Chassis

The first and foremost thing I wanted right away was clickers on top of the forks. The forks have 15 levels of preload adjustment, and the shock has seven, but compression and rebound adjusters are not part of the stock package. Given that the forks are Separate Fork Function (compression and rebound handled by one leg each), it even makes more sense to include them.

2024 Honda XL750R Transalp
The programmable quick shifter was a welcome addition, but the diminutive stock footpegs were lacking; (photo/Seiji Ishii)

The next big thing to me was the stock footpegs. I know OEM pegs can be horrible, but I found the Honda ones especially so. They are tiny in every dimension and seem like they came off a kid’s bike. The tank is wider than motocross or single-cylinder dual sport machines.

So, combined with the narrow pegs, standing while squeezing the knees was awkward and uncomfortable. And, even with new Alpinestars Tech 10s, I had obvious pressure points on the bottom of my foot.

I stand almost 100% of the time on dirt. So, the lack of surface area was noticeable, especially on longer, rougher sections. Come on, Honda, give us some decent pegs!

Other Nitpicks

Another exclusion that is often found on ADV bikes is cruise control. I never use it, but others prefer to have it on long highway stints.

I would also prefer a higher exhaust routing. The muffler and mid-pipe felt and looked low. Although I didn’t damage them, they are more prone than a system with a higher exit point.

Lastly, there is a USB-C plug under the seat — not exactly a convenient location. It seems like putting it on the dash would be a default for an ADV bike, but not so on the Transalp 750.

And, the only color is the one you see here.

2024 Honda Transalp 750 Review: Conclusions

2024 Honda Transalp XL750R
The Transalp looks so much better with a bit of dirt on it; (photo/Seiji Ishii)

I’ve had the incredible luxury of riding many modern ADV bikes. And I want a 2024 Honda Transalp 750 of my own.

For ADV riders that cruise tarmac most of the time to link dirt and gravel sections, lightly loaded, it’s hard to fault this bike at this price. The bike responds predictably to rider and terrain inputs, and the electronics package allows for optimizing these responses. The result is the ability to ride in these conditions with confidence.

I’m more of a 75% off-road guy, and the Transalp would still shine with a few minor tweaks. I would change the stock rubber to a more off-road-worthy set, which we did for our PA Wilds BDR-X route.

And, if I were carrying a camping load, I would increase the spring and damping rates. I have taken both steps on every ADV bike I’ve ever owned. So, this isn’t a shortfall of Honda on the 2024 Transalp 750.

But, what makes the 2024 Honda Transalp 750 irresistible is the price and legendary Honda reliability. A KTM 890 Adventure lists for just under $14,000, while an 890 Adventure R sits at over $15,000. The KTMs may be more off-road-ready in stock format, but I’d trade that for the reliability of a Honda. Then, use the extra $4-5K for upgrades if required.

I’ve had relatively substantial electronic and minor mechanical issues with every other ADV bike I’ve owned. But I’ve had zero issues with any Honda bike. And, I predict the same excellent reliability from the $9,999 2024 Honda XL750R Transalp.

The post 2024 Honda Transalp 750 Review: Value-Packed, Confidence-Inspiring Middleweight Adventure Bike Contender appeared first on GearJunkie.

GearJunkie

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

https://www.alloutdoor.com/wp-content/uploads/2023/10/atf-eforms-wait-times.png

Suppressors are awesome. They make shooting way more enjoyable. Sometimes, depending on the cartridge, you don’t even need ear pro. Suppressors can even improve accuracy and, where legal, they’re great for hunting. But unless the Hearing Protection Act – which would deregulate suppressors – is ever passed, you’ll have to go through some hoops to get your hands on a muzzle muffler. Here’s a quick n’ dirty guide on the steps you need to take to become the proud owner of a new suppressor. Of note, though: some states have simply banned the ownership of suppressors – the steps below cover the steps you need to take under federal law. You’ll also need to check if your state even allows for the ownership of suppressors, and whether there are extra steps you need to take.

Suppressor Coverage on AllOutdoor

“How Long Does the Suppressor Process Take?”

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

According to the ATF, the current lead time on processing the paperwork to buy a suppressor is about 270 days. That’s 38 weeks, or almost 10 months. Ouch. Indeed, buying a suppressor is a matter of patience. But, thankfully, the paperwork you need to fill out to own a suppressor is once-and-done. Once your application to buy a suppressor’s approved, you never need to renew any application, or pay any fees ever again (for that specific suppressor).

“Can I Do All the Suppressor Paperwork Online?”

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

Yes! And you definitely eForms should fill out your application to buy your suppressor online. Gone are the days of having to physically mail documents to some ATF field office – the feds have an online system that allows you to fill everything out on your phone or PC. It’s called , but we’ll talk about that more later.

“What’s a Gun Trust? Should I File as an Individual?”

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

There’s a lot of hot talk about using a gun trust to purchase an NFA item, especially suppressors. In truth, all a trust does is provide for easier transfer of ownership of your NFA items to a family member or designated beneficiary. There’s a matter of convenience, too. A gun trust allows any trustee to possess, transport, and use the NFA items within the trust at any point in time – with or without your physical presence.

Essentially, a trust allows multiple individuals to “stack on” to the application for your suppressor. Each trustee is considered an applicant, though – and that means they all have to provide personal information on your ATF application, including photos and fingerprints. Adding trustees and filing as a trust (instead of an individual) can also lead to delays if any one trustee’s background check or personal information gets caught up in any bureaucratic hurdles (like an incorrect social security number, a wrong home address, or your uncle’s old, unsightly DUI rearing its head).

To file as a trust, you have to actually make a trust, too. That means retaining an attorney to draft the paperwork, which costs extra. Personally, this writer always filed his ATF eForms applications as an individual. It’s quicker, more affordable, and less of a hassle trying to wrangle any would-be trustees to get me their requisite fingerprints, mugshots, and personal information. If you do want a gun trust, I highly recommend National Gun Trusts. They’re cheap – around $60 to $100 – and they’ve done thousands of trusts for gun owners, ensuring it’s quick and easy.

Step 1: Buy your Future Suppressor

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

Here’s the great news: You technically get to buy and “own” your suppressor before your application’s even approved. The only caveat is that you can’t actually take possession of it yet – your new can has to stay at the FFL you’re purchasing it from. But if your FFL happens to operate a shooting range, you can fire your new muzzle can all day long – or a rental that the FFL leases out for a fee. On that note, it’s a great idea to test out a few different models on your chosen firearm before you decide to purchase.

This is particularly important because once your application begins processing, you can’t back out: There is no option to change your chosen make or model, because the unique serial number of your suppressor is what ties your eForms application to the approval and ownership.

Step 2: Make an eForms Account

Once you’ve selected and paid for your suppressor, you’ll need to record the serial and register an account on the ATF’s eForms website. While registering, it’s important you accurately input your legal name and home address – it should match your home of record, not any business nor PO box. You can start filling out your application for your suppressor, but we recommend holding off until after step 3. You need to get your fingerprints ready, first. We’ll come back to the application process later.

Step 3: Roll your Fingerprints (Now Digital!)

So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It

Every eForms application for a suppressor requires a set of your fingerprints. This used to be a painful, mail-only process requiring the use of archaic FD-258 Fingerprint Cards. But now, you can get a digital set of fingerprints taken – and you can use that digital set for every eForms application you’ll ever submit!

To get digital prints for your eForms application, I recommend using PrintScan. They do digital printing for the FBI and various federal agencies, so they know what they’re doing. The cost is $45 (at the time of this publication). You can find a PrintScan location here, then schedule an appointment. After your appointment’s finished, simply download your prints from PrintScan, and then you’re ready to complete your eForms application.

Alternatively, you can also use the Silencer Shop Kiosk to get your fingerprints and other necessary affairs in order for your eForms application (more on that next). Just look for any “Powered By Silencer Shop” dealer in your area.

Step 4: Complete eForms “ATF Form 4”

ATF Form 4, “Application for Tax Paid Transfer and Registration of Firearm,” is the paperwork you must fill out on eForms. Many just call this a “form 4 application.” The process is pretty straightforward: Enter your personal information, the information pertaining to your suppressor – including the manufacturer, make, model, serial number, and caliber – and upload a picture of yourself, with your fingerprints.

The self-portrait is simple: Get someone to take a head-and-shoulders shot of you against a plain white background. You can even use your cellphone. Just make sure you’re not wearing glasses, jewelry, or any hats. If your photo looks like a passport or driver’s license portrait, you’re good to go.

Oh, you’ll also need to provide information about your local “chief law enforcement officer.” This is usually your local sheriff. You’ll want to record the name of the CLEO, and the address wherein he or she resides – usually the sheriff’s precinct. Have this info handy before filling out your application.

Step 5: Submit Application and Pay the $200 Tax

Once your application’s filled out, you’ll need to certify all the information contained therein is accurate, submit the application, and, sadly, pay our wonderful government $200 in the form of a tax. Yes, it’s dumb, but at least it’s a one-time fee. The eForms website accepts debit and credit card, which at least makes the humiliation a tad more convenient.

Step 6: Wait… and then Wait some more

Once you’ve submitted your Form 4 application, you’ll receive an email letting you know it’s in the pipeline for processing. At this point, comms will go dark for… quite a while. It’ possible your application could get approved more quickly than the advertised wait times, but expect to wait at least 8 to 9 months.

Step 7: Receive your Stamp, then Run to your FFL

Have faith, gunner: That fateful day will come. We’re talking about the day that you receive an email letting you know your ATF Form 4 application was approved, complete with your tax stamp showing you’re the proud, legal owner of a new suppressor. Print out your tax stamp and bring it to your FFL posthaste, so you can take ownership of your shiny (or matte) new muzzle muffler! There are loads of suppressor makers, and tons of models to pick from. Feeling overwhelmed? Start here, with our review of Dead Air’s new Mojave 9mm Suppressor.

The post So, You Want to Purchase a Suppressor?… Sweet! Here is How You Do It appeared first on AllOutdoor.com.

AllOutdoor.com

Review: Heckler & Koch HK416 .22 Pistol — The Tactical Plinker

https://i0.wp.com/blog.cheaperthandirt.com/wp-content/uploads/2023/10/HK-416-11.jpg?fit=648%2C382&ssl=1

The HK416 .22 LR is a Heckler & Koch firearm with the look and feel of the HK416 used by many of the world’s most elite fighting forces — including Seal Team VI. In fact, Seal Team VI members were carrying HK416s when they killed Osama Bin Laden.

HK partnered with Umarex to design and build a .22 version of the HK416 that is realistic in size and feel. It is made with the same quality standards that H&K is known for. Although the inner workings are obviously different, the externals of the .22 LR version are true to its big brother.

HK416 .22 LR pistol, left profile
Controls, except for the slide lock, mimic those on the .556 HK416.

The HK416 .22 has an aluminum receiver — no plastic like so many other .22 lookalikes. It is equipped with the same M-LOK rail interface system that’s on the HK416 5.56 model. There’s a 90-degree manual safety, functional dust cover, threaded muzzle, and it’s compatible with most AR-style arm braces. The barrel is 8.5 inches long, and the total weight with a magazine is about 6 pounds.

The trigger pull is 7 pounds with less than .25-inch take-up and a crisp break. The sights are flip-up with the rear one being fully adjustable. They flip down (out of the way) should you want to mount an optic on the top rail. There is also the HK AR pistol grip that a lot of guys like to install on their ARs because of its angle, texture, and the built-in storage compartment.

There was a little tool packet with the gun. In it was a wrench for removing the flash hider. Off it went. In its place, I screwed on a Tactical Innovations suppressor. Now my grandkids and I had a tactical-looking machine pistol. Although it is a .22 LR and isn’t fully automatic, our imaginations make up the difference for fun afternoons at the range.

 Shooting Fun for All Ages

When shooting a semi-automatic .22 for the first time, I choose my ammunition carefully. You can’t go wrong with CCI, so long as you choose some of its high-velocity rounds. I chose a box marked “Suppressor” along with some Mini-Mags, Stingers, and the new CCI clean rounds with the polymer coating. There is a bolt-speed adjustment screw that can be used to compensate between standard and high-velocity ammunition. In all my shooting, which must be more than 1,000 rounds by now, I’ve never had to make an adjustment.

The first crew to shoot with me included my middle son, his youngest son, and my grandson’s best friend. While they were getting guns laid out on the bench, I loaded the magazine for the HK416 with .22 Suppressor rounds. I handed the magazine to my son and waited for him to start shooting. I wondered if something was wrong because he seemed to be hesitating.

HK416 .22 LR pistol with a red dot sight mounted and a supressor
A red dot sight makes the .22 HK416 seem even more like a real combat weapon for training or fun.

Then, I noticed there were holes in the target. Sitting right behind him with hearing protection on, I was not hearing that gun go off due to the suppressor. It was just like in the movies… Phhht! Phhht! Now that was fun. Unfortunately, we only had one box of those rounds.

CCI Stingers are notoriously loud for a .22, but even they were pretty quiet through the HK416 with the suppressor. We had to adjust the sights a little, which was very easy with the twist knob on the sight. Our shots were going to the left before I made a quick adjustment.

Shooting first at 7 and then at 15 yards, none of us had any trouble putting our shots within a reasonable group. There was no shortage of smiles on faces, including mine. Maybe playing army could be just as much fun as playing cowboy.

HK416 .22 LR pistol with a Bushnell red dot sight mounted and a box of Federal Champion .22 LR ammunition
With a Bushnell Red Dot sight installed, it was easier to put rounds on target. However, the HK416 was accurate regardless.

Cleaning the gun was simple. It has the standard AR pins for removing the upper from the lower, but that’s as far as the similarity goes. First off, the bolt lock on the side of the receiver is a dummy, and just there for looks. The only way to lock the bolt back is to insert an empty magazine and pull the charging handle back. You can then remove the magazine, and the bolt will stay back. By the way, the gun will shoot with the magazine removed. To put the gun into battery, pull the charging handle back and let it go.

The cleaning procedures (as described in the manual) require removing just the rear pin and pivoting the upper forward. This provides access to the barrel, which can be cleaned from the breech end using a rod or pull cord that allows a brush to be attached after inserting it in the barrel. The bolt is not removed for cleaning. In fact, I’m still trying to figure out how to get it out.

After cleaning the barrel, spray the bolt area with a good gun cleaner, brush away any dirt or grime, apply two drops of oil on the main spring, one drop of oil on the extractor, and you’re ready to pop the upper down. Then, reinstall the rear pin and you’re done. I like to cycle the bolt a few times to spread what little oil is in the receiver area.

HK416 .22 LR pistol with the rear takedown bolt removed
Standard AR takedown bolts are there for cleaning, but the process is much simpler than that of the 5.56 version.

A Very Realistic “Little Brother”

I began reading everything I could on the HK416 5.56 version to understand its history. Noted firearms trainer and former Delta Force operator Larry Vickers happened to be at HK headquarters working on another project when he saw blueprints for the HK416 in the president’s office. He was able to get in on the project in its early days. I watched a video explanation he did on the differences in the short gas-stroke piston of the HK416 versus the gas operating system of the M4.

The HK416 runs cooler and cleaner, making it a favorite with military and law enforcement units around the world. Of course, this .22 version is not the same gun. However, it does make the same size holes in paper and handles the same. That makes it a good, inexpensive option for training tactical types — as well as a ton of fun for those of us who don’t clear houses and bust bad guys for a living.

I dug around and found a Bushnell Trophy Red Dot sight, which I installed on my gun. I did have to remove the existing sights to make this change, but that was no problem. I simply loosened a screw on each and slid them off the back of the rail.

HK Parts supplies a pistol brace adapter for this gun, and I found a stabilizing brace at Cheaper Than Dirt. There is also a 30-round magazine available. With the CCI suppressor ammo loaded in the HK416, my crew is ready to ping steel plates, pop Tannerite targets, and just have fun in general — with safety always the number one priority.

HK416 .22 LR Specifications

Action: Semi-automatic
Barrel Length: 16.1inches
Caliber: .22 LR
Capacity: 10 rounds
Frame Finish: Black
Stock: Collapsible

A .22 For Home Defense?

I’ve been watching the introduction of new .22 guns, especially ones that are models of higher caliber firearms, and thinking not only about their use for training, but for defense. For years, I’ve heard stories about how hitmen preferred .22s. I’ve found documentation about the OSS using suppressed High Standard and Colt Woodsman pistols in .22 caliber for close-up elimination of enemies of state.

Thumb tabs on the 20-round magazine for the HK416 .22 LR
Thumb tabs on the 20-round magazine make it easy to load.

There’s no doubt a .22 with effective shot placement could be an effective defensive round. Of course, we all want bigger calibers because we aren’t all that good with a gun, are we? Up through my 50s, I was a .45 ACP man. In my 60s, I was still talking .45 ACP but shooting more 9mm. Now that I’m in my 70s, for some reason I’m shooting a lot of .22s, though still carrying a 9mm.

Will I ever carry a .22 for self-defense? I don’t think so, but I’m not making any promises. I do know that for home defense, the HK416 with .22 High-Velocity Hollow Points loaded would do the job. Knowing that gives me a reason to practice with it. Never mind that practicing with it is more fun than I could have imagined.

The HK416 is priced reasonably and uses cheap ammo. It might make a good addition to your .22 caliber gun arsenal; it sure did to mine. With a MSRP of $449, you’ll find it under $400.

What piques your interest in the HK416 .22 LR the most — plinking, militaria collecting, tactical training or something else? Share your answer in the Comment section.

  • HK416 .22 LR pistol with a box of CCI ammunition
  • HK416 .22 LR pistol, right profile, with a accessory tools
  • Faux flash hider on a gun barrel
  • HK416 .22 LR pistol grip with storage compartment
  • HK416 .22 LR pistol with a Bushnell red dot sight mounted and a box of Federal Champion .22 LR ammunition
  • Flip up front sight on a rifle
  • HK416 .22 LR pistol with the rear takedown bolt removed
  • David Freeman wearing a Vietnam Veteran hat shooting the HK416 pistol
  • HK416 .22 LR pistol with a red dot sight mounted and a supressor
  • HK416 .22 LR pistol, left profile
  • Thumb tabs on the 20-round magazine for the HK416 .22 LR

The Shooter’s Log

Can You Fly on a Plane with a Firearm?… Yes! Here is How You Do It

https://www.alloutdoor.com/wp-content/uploads/2023/10/pelican-vault-v200-06.jpg

Before we jump into this, it’s important to clear something up: federal regulations say that you can check a firearm in your baggage by following certain steps – which we’ll cover here – but some state laws varyThe Firearm Owners Protection Act is supposed to allow gun owners to travel through states with a legally possessed firearm, even if that firearm is illegal in that state, under a provision called the Safe Passage Act. But in spite of this, You could still potentially face penalties, or be temporarily detained and questioned by some unfriendly authorities, if you fly into a state with restrictions on the guns you’re traveling with. That includes connecting flights. Yes, it’s terrible, but some gun owners have faced hurdles for flying with perfectly legal firearms and making connecting flights in restrictive states like New York.

Travel and Firearm Coverage on AllOutdoor

In short, always check the local and state laws of all locales you plan on visiting – even those wherein you may simply sit in the airport waiting for your next flight. With that out of the way, let’s answer the common questions and bust some myths. Then we’ll go over how to properly store your guns and declare them at your departure for air travel.

  • Can I Really Fly with a firearm? – Yes, but under normal circumstances, only in the U.S. It is possible to fly internationally with guns, and plenty of hunters do it. But you need to comply with the Arms Export Control Act, and deal with the legalities of strict laws overseas. We’re only going over flying with your firearms in betwixt our 50 great states.
  • Can a Firearm be Carried-On a Plane? – Absolutely not and, if you try to stow your gun in a carry-on, you will most certainly not be going anywhere (besides a locked room, in handcuffs). All firearms must be transported in checked baggage and stored in the cargo hold. Importantly, though, firearm optics can be carried onboard any flight. Scopes and night vision devices can be delicate, and they’re expensive, so you might want to tuck yours in your overhead bag instead.
  • What if I’m Law Enforcement? – No go. All firearms must always be checked with TSA before you go through security. Only air marshals on duty can carry firearms aboard an aircraft.
  • Can I Fly with Ammo, too? – Yes, but you can’t travel with loose rounds. All live ammo must be stored inside a box or container, or magazines (more on that next), then stored inside a locking box.

How Exactly Do I Store my Firearm and Ammo?

Easy! You can store your firearms inside a locking hard case. Of course, your case needs to be able to withstand the rigors of air travel and it must be able to withstand basic handling. That means no flimsy, $20 gun cases that you could easily pry open, or break the latches.

Can You Fly on a Plane with a Firearm?... Yes! Here is How You Do It

A Pelican case, like the Vault V200 Handgun Case, or any similar locking hard case sets the standard for what is considered air travel-worthy.

Can You Fly on a Plane with a Firearm?... Yes! Here is How You Do It

Rifle cases are no different. Condition1’s Hard-Shell Long Gun Cases are great options. The TSA spells out some firearm and ammo transportation guidelines, but here’s the gist:

  • Your gun case needs to be hard-sided. No soft cases are allowed.
  • You case needs to be lockable, either with a key lock or padlock.
  • Your case needs to be impact- and weather-resistant.
  • Ideally, your case should have a pressure relief valve.
  • Ammo and firearms must be stored separately.

The TSA takes these guidelines seriously. If you show up and have even a single loose round bouncing around in your gun case, you’re probably going to miss your flight and you could even be on the hook for a $10,000 fine, if not jail time. Besides storing ammo and guns in separate locking containers, there are some extra guidelines to follow when it comes to ammo, too:

  • Ammo can’t be larger than .75 caliber. So, no mortars or rockets. Sorry.
  • Ammo can’t be left in magazines or stripper clips unless they’re capped.
  • Ammo can be stored inside a firearm case only if the ammo is first stored inside its own separate, locking hard case. That smaller case can then be placed inside the firearm case if there’s room.

Beyond these requirements, here are some tips and tricks you should consider when traveling with guns and ammo. This can make your travel easier and worry-free.

  • Contact info. Place it inside your gun case. If your case gets lost, TSA can more easily contact you if you get separated from your checked luggage.
  • TSA-approved locks. Some advocate for not using TSA-approved locks (they can be opened by TSA agents). But if your gun case does happen to get misplaced, the only choice agents have is to either 1. cut your locks, or 2. open them if possible.
  • Tell security you’re traveling with firearms at the curb. Virtually every single airport in the U.S. has security or law enforcement at the departure terminal curbside. Approaching security can make your check-in process easier. Usually, they’ll be happy to escort you to the separate screening area for checking your firearms. They’ll ensure your guns and ammo are stored correctly before entering the airport, helping you to avoid any issues later.
  • Don’t open your cases at your destination. Of course you want to check your firearms and kit after you land, but wait until you exit the airport. If you’re seen opening your cases after you collect them, you’re bound to get swarmed by security.
  • Call ahead and speak to your airline and airports. It’s a good idea to confirm your airline’s policy concerning checking firearms. Some are easier to deal with than others. The same goes for airport security: Call your departure and arrival points and confirm where you need to go for screening before the day you fly.

Did you know Pelican makes rugged carry-on cases for your other gear? Check out our review of the Pelican 1535 Rolling Carry-On Case.

Can You Fly on a Plane with a Firearm?... Yes! Here is How You Do It

The post Can You Fly on a Plane with a Firearm?… Yes! Here is How You Do It appeared first on AllOutdoor.com.

AllOutdoor.com