Skip to content

Commit da99ea4

Browse files
AnalogJleaanthony
authored andcommitted
Update docs + examples.
Serve runtime from assetserver if requested. Add gin guide, fix asset server merge, add gin example adding http.CloseNotifier and http.Flusher interface to assetserver.contentTypeSniffer, for Gin (and other framework) compatibility.
1 parent b72782c commit da99ea4

File tree

13 files changed

+767
-41
lines changed

13 files changed

+767
-41
lines changed

docs/src/content/docs/guides/gin-routing.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Using Gin with Wails
2+
title: Using Gin for Routing
33
description: A comprehensive guide to integrating Gin web framework with Wails v3 applications
44
---
55

docs/src/content/docs/guides/gin-services.mdx

+176-33
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11
---
2-
title: Using Gin in Services
2+
title: Using Gin for Services
33
description: A guide to integrating the Gin web framework with Wails v3 Services
44
---
55

6-
Wails v3 Services provide a powerful way to organize your application logic into reusable, modular components. By implementing the `http.Handler` interface in your services, you can mount them at specific routes in your application, allowing for a clean separation of concerns and more maintainable code.
6+
# Using Gin for Services
77

8-
Integrating Gin with Wails Services enables you to create modular, mountable HTTP APIs using Gin's powerful routing and middleware capabilities. You can organize your application into domain-specific services, mount multiple Gin-based services at different routes, leverage Gin's extensive feature set while maintaining the benefits of Wails Services, and seamlessly integrate with the Wails event system for real-time communication.
8+
The Gin web framework is a popular choice for building HTTP services in Go. With Wails v3, you can easily integrate Gin-based services into your application, providing a powerful way to handle HTTP requests, implement RESTful APIs, and serve web content.
99

10-
## Creating a Gin-based Service
10+
This guide will walk you through creating a Gin-based service that can be mounted at a specific route in your Wails application. We'll build a complete example that demonstrates how to:
1111

12-
To create a Wails Service that uses Gin for HTTP handling, you need to implement both the Wails Service interface and the `http.Handler` interface. This combination allows your service to be managed by the Wails application lifecycle and handle HTTP requests. Let's walk through each step of the process:
12+
1. Create a Gin-based service
13+
2. Implement the Wails Service interface
14+
3. Set up routes and middleware
15+
4. Integrate with the Wails event system
16+
5. Interact with the service from the frontend
1317

14-
### 1. Define Your Service Structure
18+
## Prerequisites
1519

16-
First, create a new service structure that will hold your Gin router and any state your service needs. This structure serves as the foundation of your service, encapsulating both the HTTP handling capabilities and any business logic or data your service requires. The use of a mutex ensures thread-safe access to shared resources, which is essential for concurrent request handling.
20+
Before you begin, make sure you have:
21+
22+
- Wails v3 installed
23+
- Basic knowledge of Go and the Gin framework
24+
- Familiarity with HTTP concepts and RESTful APIs
25+
26+
You'll need to add the Gin framework to your project:
27+
28+
```bash
29+
go get github.com/gin-gonic/gin
30+
```
31+
32+
## Creating a Gin-Based Service
33+
34+
Let's start by creating a Gin service that implements the Wails Service interface. Our service will manage a collection of users and provide API endpoints for retrieving and creating user records.
35+
36+
### 1. Define Your Data Models
37+
38+
First, define the data structures your service will work with:
1739

1840
```go
1941
package services
2042

2143
import (
2244
"context"
2345
"net/http"
46+
"strconv"
2447
"sync"
2548
"time"
2649

@@ -36,6 +59,18 @@ type User struct {
3659
CreatedAt time.Time `json:"createdAt"`
3760
}
3861

62+
// EventData represents data sent in events
63+
type EventData struct {
64+
Message string `json:"message"`
65+
Timestamp string `json:"timestamp"`
66+
}
67+
```
68+
69+
### 2. Create Your Service Structure
70+
71+
Next, define the service structure that will hold your Gin router and any state your service needs to maintain:
72+
73+
```go
3974
// GinService implements a Wails service that uses Gin for HTTP handling
4075
type GinService struct {
4176
ginEngine *gin.Engine
@@ -71,9 +106,9 @@ func NewGinService() *GinService {
71106
}
72107
```
73108

74-
### 2. Implement the Wails Service Interface
109+
### 3. Implement the Service Interface
75110

76-
Your service needs to implement the Wails Service interface with the required methods. The `ServiceName` method provides a human-readable identifier for your service. The `ServiceStartup` method is called when the service starts and gives you access to the Wails application instance, which you can use to register event handlers and access other Wails features. The `ServiceShutdown` method allows you to clean up resources when the service is shutting down.
111+
Implement the required methods for the Wails Service interface:
77112

78113
```go
79114
// ServiceName returns the name of the service
@@ -122,7 +157,7 @@ func (s *GinService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
122157

123158
### 4. Set Up Your Routes
124159

125-
Define your API routes in a separate method for better organization. This approach keeps your code clean and makes it easier to understand the structure of your API. The Gin router provides a fluent API for defining routes, including support for route groups, which help organize related endpoints.
160+
Define your API routes in a separate method for better organisation. This approach keeps your code clean and makes it easier to understand the structure of your API. The Gin router provides a fluent API for defining routes, including support for route groups, which help organise related endpoints.
126161

127162
```go
128163
// setupRoutes configures the API routes
@@ -191,6 +226,29 @@ func (s *GinService) setupRoutes() {
191226
// Emit an event to notify about the new user
192227
s.app.EmitEvent("user-created", newUser)
193228
})
229+
230+
// Delete a user
231+
users.DELETE("/:id", func(c *gin.Context) {
232+
id, err := strconv.Atoi(c.Param("id"))
233+
if err != nil {
234+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
235+
return
236+
}
237+
238+
s.mu.Lock()
239+
defer s.mu.Unlock()
240+
241+
for i, user := range s.users {
242+
if user.ID == id {
243+
// Remove the user from the slice
244+
s.users = append(s.users[:i], s.users[i+1:]...)
245+
c.JSON(http.StatusOK, gin.H{"message": "User deleted"})
246+
return
247+
}
248+
}
249+
250+
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
251+
})
194252
}
195253
}
196254
```
@@ -241,7 +299,7 @@ app := application.New(application.Options{
241299
})
242300
```
243301

244-
In this example, the Gin service is mounted at the `/api` route. This means that if your Gin router has an endpoint defined as `/info`, it will be accessible at `/api/info` in your application. This approach allows you to organize your API endpoints logically and avoid conflicts with other parts of your application.
302+
In this example, the Gin service is mounted at the `/api` route. This means that if your Gin router has an endpoint defined as `/info`, it will be accessible at `/api/info` in your application. This approach allows you to organise your API endpoints logically and avoid conflicts with other parts of your application.
245303

246304
## Integrating with the Wails Event System
247305

@@ -285,7 +343,7 @@ Then use it in your code:
285343
import * as wails from '@wailsio/runtime';
286344

287345
// Event emission
288-
wails.Events.Emit({name: 'gin-api-event', data: eventData});
346+
wails.Events.Emit('gin-api-event', eventData);
289347
```
290348

291349
Here's an example of how to set up frontend integration:
@@ -310,7 +368,8 @@ Here's an example of how to set up frontend integration:
310368
<button id="getUsers">Get All Users</button>
311369
<button id="getUser">Get User by ID</button>
312370
<button id="createUser">Create User</button>
313-
371+
<button id="deleteUser">Delete User</button>
372+
314373
<div id="apiResult">
315374
<pre id="apiResponse">Results will appear here...</pre>
316375
</div>
@@ -327,8 +386,23 @@ Here's an example of how to set up frontend integration:
327386
</div>
328387
</div>
329388

330-
<!-- User creation form omitted for brevity -->
331-
389+
<div class="card" id="createUserForm" style="display: none; border: 2px solid #0078d7;">
390+
<h2>Create New User</h2>
391+
392+
<div>
393+
<label for="userName">Name:</label>
394+
<input type="text" id="userName" placeholder="Enter name">
395+
</div>
396+
397+
<div>
398+
<label for="userEmail">Email:</label>
399+
<input type="email" id="userEmail" placeholder="Enter email">
400+
</div>
401+
402+
<button id="submitUser">Submit</button>
403+
<button id="cancelCreate">Cancel</button>
404+
</div>
405+
332406
<script type="module">
333407
// Import the Wails runtime
334408
// Note: In production, use '@wailsio/runtime' instead
@@ -353,8 +427,85 @@ Here's an example of how to set up frontend integration:
353427
fetchAPI('/info');
354428
});
355429
356-
// Other API button handlers omitted for brevity
357-
430+
document.getElementById('getUsers').addEventListener('click', () => {
431+
fetchAPI('/users');
432+
});
433+
434+
document.getElementById('getUser').addEventListener('click', async () => {
435+
const userId = prompt('Enter user ID:');
436+
if (userId) {
437+
await fetchAPI(`/users/${userId}`);
438+
}
439+
});
440+
441+
document.getElementById('createUser').addEventListener('click', () => {
442+
const form = document.getElementById('createUserForm');
443+
form.style.display = 'block';
444+
form.scrollIntoView({ behavior: 'smooth' });
445+
});
446+
447+
document.getElementById('cancelCreate').addEventListener('click', () => {
448+
document.getElementById('createUserForm').style.display = 'none';
449+
});
450+
451+
document.getElementById('submitUser').addEventListener('click', async () => {
452+
const name = document.getElementById('userName').value;
453+
const email = document.getElementById('userEmail').value;
454+
455+
if (!name || !email) {
456+
alert('Please enter both name and email');
457+
return;
458+
}
459+
460+
try {
461+
await fetchAPI('/users', {
462+
method: 'POST',
463+
headers: {
464+
'Content-Type': 'application/json'
465+
},
466+
body: JSON.stringify({ name, email })
467+
});
468+
469+
document.getElementById('createUserForm').style.display = 'none';
470+
document.getElementById('userName').value = '';
471+
document.getElementById('userEmail').value = '';
472+
473+
// Automatically fetch the updated user list
474+
await fetchAPI('/users');
475+
476+
// Show a success message
477+
const apiResponse = document.getElementById('apiResponse');
478+
const currentData = JSON.parse(apiResponse.textContent);
479+
apiResponse.textContent = JSON.stringify({
480+
message: "User created successfully!",
481+
users: currentData
482+
}, null, 2);
483+
} catch (error) {
484+
console.error('Error creating user:', error);
485+
}
486+
});
487+
488+
document.getElementById('deleteUser').addEventListener('click', async () => {
489+
const userId = prompt('Enter user ID to delete:');
490+
if (userId) {
491+
try {
492+
await fetchAPI(`/users/${userId}`, {
493+
method: 'DELETE'
494+
});
495+
496+
// Show success message
497+
document.getElementById('apiResponse').textContent = JSON.stringify({
498+
message: `User with ID ${userId} deleted successfully`
499+
}, null, 2);
500+
501+
// Refresh the user list
502+
setTimeout(() => fetchAPI('/users'), 1000);
503+
} catch (error) {
504+
console.error('Error deleting user:', error);
505+
}
506+
}
507+
});
508+
358509
// Using Wails Events API for event communication
359510
document.getElementById('triggerEvent').addEventListener('click', async () => {
360511
// Display the event being sent
@@ -378,6 +529,14 @@ Here's an example of how to set up frontend integration:
378529
document.getElementById('eventResponse').textContent = JSON.stringify(data, null, 2);
379530
});
380531
532+
// Also listen for user-created events
533+
wails.Events.On("user-created", (data) => {
534+
document.getElementById('eventResponse').textContent = JSON.stringify({
535+
event: "user-created",
536+
user: data
537+
}, null, 2);
538+
});
539+
381540
// Initial API call to get service info
382541
fetchAPI('/info');
383542
});
@@ -386,22 +545,6 @@ Here's an example of how to set up frontend integration:
386545
</html>
387546
```
388547

389-
## Best Practices
390-
391-
When using Gin with Wails Services, consider these best practices for creating maintainable and efficient applications:
392-
393-
- Modular Design: Organize your API endpoints into logical groups using Gin's router groups. This makes your code more readable and easier to maintain.
394-
395-
- Concurrency Safety: Use mutexes or other synchronization primitives when accessing shared state. This prevents race conditions and ensures data integrity.
396-
397-
- Error Handling: Implement consistent error handling across your API endpoints. This provides a better user experience and makes debugging easier.
398-
399-
- Logging: Use the Wails logger for consistent logging throughout your application. This helps with debugging and monitoring.
400-
401-
- Event-Driven Communication: Use the Wails event system for real-time updates and notifications. This creates a more responsive and interactive user experience.
402-
403-
- Security: Implement proper authentication and authorization for your API endpoints. This protects your application and user data.
404-
405548
## Closing Thoughts
406549

407550
Integrating the Gin web framework with Wails v3 Services provides a powerful and flexible approach to building modular, maintainable web applications. By leveraging Gin's routing and middleware capabilities alongside the Wails event system, you can create rich, interactive applications with clean separation of concerns.

v3/examples/events/assets/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<h1>Events Demo</h1>
1010
<br/>
1111
The main program emits an event every 10s which will be displayed in the section below.
12-
To send an event from this window, click here: <button onclick="wails.Events.Emit({name:'myevent', data:'hello!'})">Send Event</button>
12+
To send an event from this window, click here: <button onclick="wails.Events.Emit('myevent', 'hello!')">Send Event</button>
1313
<div id="results"></div>
1414
</body>
1515

v3/examples/gin-routing/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Gin Routing Example
2+
3+
This example demonstrates how to use the [Gin web framework](https://github.com/gin-gonic/gin) as a router with Wails.
4+
5+
## Overview
6+
7+
This example shows how to:
8+
9+
- Set up Gin as the asset handler for a Wails application
10+
- Create a middleware that routes requests between Wails and Gin
11+
- Define API endpoints with Gin
12+
- Communicate between the Gin-served frontend and Wails backend
13+
- Implement custom Gin middleware
14+
15+
## Running the Example
16+
17+
```bash
18+
cd v3/examples/gin-routing
19+
go mod tidy
20+
go run .
21+
```
22+
23+
## Documentation
24+
25+
Please consult the [Using Gin for Routing](https://v3.wails.io/guides/gin-routing/) guide for a detailed explanation of the example.
26+

0 commit comments

Comments
 (0)