WASM MySQL
Hi Guys,
Pulling my hair out here. So I though I would share and ask for help.
I am currently building a Database Browser application that would allow a user to browse a database and read data from their respective tables all within the browser using my app.
You can see more at https://dbctrl.com (Be nice, it's a work in progress).
Now, I have been able to build the application in such a way that the user can open a local database and view the contents of the server. That's not what I am going for because their database is not on my server. Given the constraints of opening a local network database through the browser, I want to use WebAssembly for this to avoid any complications. I do not know WebAssembly at all.
So to show you what I have so far, here is the view for a user to add a database connection:
@extends('theme::layouts.main')
@section('content')
<div x-data="{ modalOpen: false, connectionType: 'remote' }" class="flex flex-col w-full p-4">
<div x-show="modalOpen" class="fixed top-0 left-0 z-[99] flex items-center justify-center w-screen h-screen"
x-cloak>
<div x-show="modalOpen" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-300"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black bg-opacity-40"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
class="relative w-full py-6 bg-white px-7 sm:max-w-lg sm:rounded-lg">
<div class="flex items-center justify-between pb-2">
<h3 class="text-lg font-semibold">Add MySQL Connection</h3>
<button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
<!-- Form for connection details -->
<form>
<!-- ... -->
<label for="connection_type">Connection Type</label>
<select id="connection_type" name="connection_type" @change="updateConnectionType($event)">
<option value="remote" selected>Remote</option>
<option value="local">Local</option>
</select>
<label
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
for="name">Name</label>
<input type="text" id="name" name="name" placeholder="E.g. My App DB"
class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
required>
<label
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
for="hostname">Host</label>
<input type="text" id="hostname" name="hostname" placeholder="E.g. 10.10.10.10"
class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
required>
<label
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
for="username">Username</label>
<input type="text" id="username" name="username" placeholder="Username"
class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50">
<label
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Password"
class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50">
<label
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
for="database_name">Database Name</label>
<input type="text" id="database_name" name="database_name" placeholder="E.g. MainDB"
class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
required>
<button type="submit" onclick="submitForm()"
class="mt-2 inline-flex items-center justify-center h-10 w-28 px-4 py-2 text-sm font-medium tracking-wide text-black shadow-lg transition-colors duration-100 bg-white border-2 border-yellow-500 rounded-md hover:text-black hover:bg-yellow-200">Save</button>
</form>
</div>
</div>
<button @click="modalOpen=true"
class="mt-2 mb-4 inline-flex items-center justify-center h-10 w-28 px-4 py-2 text-sm font-medium tracking-wide text-black shadow-lg transition-colors duration-100 bg-white border-2 border-yellow-500 rounded-md hover:text-black hover:bg-yellow-200">Add
</button>
@if($databaseConnections->isEmpty())
<div class="flex flex-col items-center justify-center mt-10">
<img src="https://cdn.devdojo.com/tails/images/5aw7TwTWRPf7OixApMpGvN5rYsajsI1xQ7AK1WlC.png"
alt="No connections image" class="mb-6 h-60">
<h2 class="text-xl font-bold text-gray-700">No Database Connections</h2>
<p class="text-gray-600">You currently have no database connections. Click "Add" to create one.</p>
</div>
@else
@foreach($databaseConnections as $databaseConnection)
<a href="{{ route('database.show', $databaseConnection->name) }}"
class="max-w-sm border-yellow-500 border-2 bg-white rounded-lg shadow-lg p-7 mb-4 hover:text-yellow-200 hover:bg-yellow-200 hover:border-yellow-500">
<div class="flex justify-between items-center mb-2">
<h5 class="text-xl font-bold leading-none tracking-tight text-black">
{{ $databaseConnection->name }}
</h5>
<div>
<span
class="rounded-full px-2.5 py-0.5 text-black text-xs font-semibold border-yellow-500 border-2 bg-white">
MySQL
</span>
</div>
<div @click="deleteConnection({{ $databaseConnection->id }})" class="cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="red"
class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5m6 4.125l2.25 2.25m0 0l2.25 2.25M12 13.875l2.25-2.25M12 13.875l-2.25 2.25M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
</div>
</div>
</a>
@endforeach
@endif
<script>
function submitForm() {
const formData = new FormData(document.querySelector('form'));
const connectionType = formData.get('connection_type');
if (connectionType === 'local') {
// Use Wasm library to connect to local MySQL
// This is pseudocode and would depend on the library's API:
const wasmMySQL = new WasmMySQLLibrary();
wasmMySQL.connect(
formData.get('hostname'),
formData.get('username'),
formData.get('password'),
formData.get('database_name')
);
// NOTE: You might need to handle the response from wasmMySQL.connect
// and update your UI or perform other tasks.
} else {
// Existing logic for remote connections
fetch('/dashboard/add-connection', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert(data.message);
location.reload(); // Reload the page
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function deleteConnection(id) {
if (!confirm("Are you sure you want to delete this connection?")) {
return;
}
fetch(`/dashboard/connection/${id}`, {
method: 'DELETE',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert(data.message);
location.reload(); // Reload the page
}
})
.catch(error => {
console.error('Error:', error);
});
}
window.updateConnectionType = function(event) {
this.connectionType = event.target.value;
// You can hide/show or enable/disable certain fields based on this value if needed
}
</script>
@endsection
Does anyone have any clue as to how I can implement this with a WASM MySQL driver or whatever so that the user can open their own local database with my tool and browse the data in the tables?
Hi there,
I've not done this before but I believe that even with WebAssembly, browser-based apps you would not have direct access to OS-level TCP sockets. This means you cannot directly connect to a MySQL instance using the typical TCP/IP protocol from the browser, even with Wasm. You'd need some local bridge/proxy to convert Web API calls (like WebSockets or HTTP) into MySQL protocol calls but this would be a bit of a limitation because your clients will need to set that up.
Maybe a better approach would be to build a native desktop app, now that NativePHP is out:
NativePHP is a new way to build native applications, using the tools you already know.
Another possible approach might be to build a browser extension rather than a desktop app or a web app, here is a video tutorial that shows how you might be able to do that:
And this is the article that comes with the video:
https://anobjectisa.com/?p=105