tug v0.45.0

Go + Inertia.js v3

Trang React render thẳng từ handler Go, không cần API ở giữa.

tug là một web framework cho các ứng dụng Go có frontend là Inertia.js. Handler render các trang React, Vue hoặc Svelte với props có kiểu, và cả ứng dụng được đóng gói thành một binary duy nhất.

Từ con số không đến một binary
$ go install github.com/cuonggt/tug/cmd/tug@latest
$ tug new blog          # -auth for accounts
$ cd blog
$ tug dev
tug  │ serving http://localhost:8080, built in 268ms
^C
$ tug build
tug build: blog, 9.6 MB, frontend included

Cách mọi thứ ăn khớp

Một struct Go là props của trang, và cũng là kiểu của chúng.

Handler render một trang với một struct, và component của trang nhận struct đó làm props. tug gen sinh TypeScript cho struct, nên component được kiểm tra theo mã Go, và tug dev sinh lại mỗi khi mã Go thay đổi.

main.go
type PostsIndexProps struct {
	Posts []Post                   `json:"posts"`
	Stats inertia.DeferProp[Stats] `json:"stats"`
}

var PostsIndex = tug.Page[PostsIndexProps]("Posts/Index")

func index(c *tug.Ctx) error {
	return PostsIndex.Render(c, PostsIndexProps{
		Posts: store.List(),
		Stats: inertia.Defer(store.Count), // after the page shows
	})
}
resources/js/tug/pages.ts
// Code generated by tug gen from the Go types. DO NOT EDIT.

export interface PostsIndexProps {
  posts: Post[]
  stats?: Stats
}
resources/js/pages/Posts/Index.tsx
import { Deferred, Link } from '@inertiajs/react'
import type { PageProps } from '../../tug/pages'
import { route } from '../../tug/routes'

type Props = PageProps<'Posts/Index'>

export default function Index({ posts, stats }: Props) {
  return (
    <>
      <Deferred data="stats" fallback={<p>Counting…</p>}>
        {stats && <p>{stats.posts} posts</p>}
      </Deferred>
      {posts.map((post) => (
        <Link
          key={post.id}
          href={route('posts.show', { id: post.id })}
        >
          {post.title}
        </Link>
      ))}
    </>
  )
}

Có gì bên trong

Những phần một ứng dụng cần, đã chạy sẵn với Inertia.

Session, thông báo flash, lỗi kiểm tra hợp lệ và CSRF chạy với Inertia mà không cần thiết lập, trên nền thư viện chuẩn.

  • Inertia v3, đầy đủ

    Props trì hoãn, gộp, một lần và tùy chọn, tải lại một phần theo đường dẫn, cuộn vô hạn, cùng tiêu đề và mô tả của trang từ Go.

  • Kiểu từ Go

    tug gen sinh TypeScript cho props của từng trang, các route có tên, những gì mỗi form gửi lên và các khóa của dữ liệu flash.

  • Form tự kiểm tra

    c.BindValid bind một request và kiểm tra nó theo các tag của struct. Form không hợp lệ sẽ quay lại cùng các lỗi của nó, và từng trường được kiểm tra ngay khi người dùng rời khỏi trường đó.

  • Tài khoản, chỉ với một flag

    tug new -auth: đăng ký, xác minh email, mã xác thực hai yếu tố, passkey, API token và thông báo, trên SQLite, Postgres hoặc MySQL.

  • Tác vụ nền

    Tác vụ chạy lại khi thất bại, chạy theo lịch ở múi giờ bất kỳ, chỉ chạy một lần dù được đẩy vào hàng đợi bao nhiêu lần, và có thể đẩy ngay trong transaction của chính ứng dụng.

  • Một binary duy nhất

    tug build tạo ra một binary tĩnh duy nhất chứa cả frontend, được chính ứng dụng nén gzip khi gửi đi, và ứng dụng có sẵn một Dockerfile.

  • Render trên máy chủ, nếu bạn muốn

    tug new -ssr render trang cho lượt truy cập đầu bằng SSR của chính Inertia, trong Node chạy cạnh ứng dụng. Không có Node, các trang được render trong trình duyệt.

  • Kiểm thử không cần trình duyệt

    Gói tugtest là client của Inertia dành cho test viết bằng Go: lượt truy cập, form, tệp tải lên và đăng nhập, với props của trang được đọc vào struct Go của trang.

  • Sự kiện trên mọi instance

    Gói broadcast chuyển sự kiện trên các kênh qua cơ sở dữ liệu của ứng dụng, đến các trang đang mở trên mọi instance, để chúng tải lại phần đã thay đổi.

Vì sao chọn Go

Một binary nhỏ gọn, chạy trên mọi nhân, khởi động trong vài mili giây.

Go biến một ứng dụng web thành một binary tĩnh duy nhất, dùng mọi nhân trong một tiến trình, khởi động trong vài mili giây và chạy với vài chục MB bộ nhớ. tug giữ trọn những điều đó cho ứng dụng Inertia: một binary tĩnh duy nhất, chứa bản build frontend bên trong, được phục vụ bởi chính máy chủ HTTP của Go.

  • Hiệu năng

    Hàng chục nghìn request mỗi giây trên một chiếc laptop, trong một tiến trình dùng mọi nhân, với vài chục MB RAM.

  • Khởi động

    Trong vài mili giây, giúp giữ chi phí thấp trên nền tảng có thể thu nhỏ về 0 instance.

  • Triển khai

    Một tệp để sao chép, không cần cài runtime hay thư viện phụ thuộc, build cho Linux hoặc ARM ngay trên máy Mac.

  • Phát triển

    Một ngôn ngữ nhỏ gọn, biên dịch nhanh, có thư viện chuẩn kèm sẵn một máy chủ HTTP đủ tốt để chạy production.

Nhanh đến đâu

60.061 lượt truy cập mỗi giây, dùng 19,9 MB bộ nhớ.

Một trang Inertia, gồm một bài viết và các bình luận của nó, được phục vụ bởi một ứng dụng đúng như tug new tạo ra, với session, CSRF và Content-Security-Policy, trong một tiến trình dùng mọi nhân.

  • 60.061 lượt truy cập mỗi giây

    Client của Inertia yêu cầu trang và nhận về JSON, 99 trên 100 lượt trong vòng 4,38 ms.

  • 56.146 lượt truy cập đầu mỗi giây

    Trình duyệt yêu cầu trang và nhận về HTML của trang, 99 trên 100 lượt trong vòng 5,09 ms.

  • 19,9 MB bộ nhớ khi chịu tải

    6,4 MB sau khi khởi động, lúc nhàn rỗi.

  • 15 ms để khởi động

    Từ lúc khởi chạy binary đến khi nó trả về trang lần đầu tiên.

  • 9,6 MB để triển khai

    Một binary tĩnh duy nhất, không cần cài thêm gì bên cạnh.

  • 75 ns thêm vào mỗi request

    Thời gian App của tug cộng thêm so với ServeMux, router có sẵn của Go, trong một tiến trình.

Đo ngày trên Apple M1 Max, 10 nhân, macOS 27.0.1. Mọi con số, và cách đo chúng

Hướng dẫn

Từ ứng dụng mới tạo đến khi triển khai.

18 phần, theo thứ tự một ứng dụng cần đến. Tài liệu riêng của từng gói, trên pkg.go.dev, trình bày phần chi tiết còn lại.

  1. Bắt đầu

    cài đặt tug, tạo một ứng dụng, chạy nó, rồi thêm một trang và một form.

  2. Định tuyến và handler

    App, route, liên kết tuyệt đối và liên kết có chữ ký, nhóm route, middleware, trong đó có header bảo mật, route đã trả lời request, địa chỉ của client khi đứng sau proxy, Ctx, tệp, tải xuống, stream và sự kiện, bind request, và lỗi.

  3. Trang

    trang Inertia và props của chúng, template gốc và nonce của nó, props được tính sau, props dùng chung, chuyển hướng, tải xuống và sự kiện trên một trang, trang lỗi, Vite và DevTools của Inertia.

  4. Render phía máy chủ

    trang được render trên máy chủ cho lượt truy cập đầu, bởi Node chạy cạnh ứng dụng, và gói ssr.

  5. Form và session

    kiểm tra hợp lệ, quy tắc riêng của ứng dụng, form kiểm tra từng trường ngay khi người dùng rời khỏi nó, thông báo flash, session và CSRF.

  6. Đa ngôn ngữ

    những gì tug và ứng dụng nói với người dùng, bằng ngôn ngữ của request, từ một tệp cho mỗi ngôn ngữ, gói lang và tug lang.

  7. Tệp

    tệp tải lên, được kiểm tra theo kích thước và loại thực sự của chúng, lưu trên đĩa của ứng dụng hoặc trong S3 bằng gói storage, và liên kết đến chúng, công khai hoặc có chữ ký.

  8. Tài khoản

    starter auth, trên SQLite, Postgres hoặc MySQL, với API token và thông báo của nó, cùng các gói auth và mail, có bản sao, tệp đính kèm và liên kết hủy đăng ký chỉ bằng một cú nhấp.

  9. Migration

    các bảng của cơ sở dữ liệu, được tạo và thay đổi bằng các tệp SQL, mỗi tệp chạy một lần, theo thứ tự, khi ứng dụng khởi động và qua lệnh migrate của ứng dụng, với tug migrate new để tạo migration tiếp theo, và gói migrate.

  10. Phân quyền

    người dùng được làm gì với một đối tượng, các ability của gói auth, lời từ chối thành một lỗi 403 nói rõ lý do, và những gì người dùng của trang được làm, có trong props của trang.

  11. Mã hóa

    khóa của ứng dụng, những gì tug mã hóa và ký bằng khóa đó, gói crypt cho các giá trị riêng của ứng dụng, và việc xoay vòng khóa.

  12. Tác vụ nền

    gói queue, cho những việc kéo dài hơn request, được chạy lại khi thất bại, chạy theo lịch, kể cả theo múi giờ, chỉ chờ chạy một lần dù được yêu cầu bao nhiêu lần, hoặc chỉ chạy một số lượng nhất định cùng lúc hay mỗi giây, trên mọi instance, và báo khi đã thất bại hẳn cũng như từng lần chạy diễn ra thế nào.

  13. Cache

    gói cache, cho những gì tốn thời gian để tính, được lưu ở nơi mọi instance của ứng dụng đều tìm thấy, và lock cho những việc không được chạy hai lần cùng lúc.

  14. Phát sự kiện

    gói broadcast, sự kiện trên các kênh, được cơ sở dữ liệu của ứng dụng chuyển đến các trang đang mở trên mọi instance, để chúng tải lại phần đã thay đổi.

  15. TypeScript

    các kiểu mà tug gen sinh ra từ mã Go.

  16. Kiểm thử

    gói tugtest, client của Inertia cho các test Go kiểm tra trang, form, tệp tải lên và đăng nhập của ứng dụng, mailtest cho email của ứng dụng, và các sự kiện của nó.

  17. Triển khai

    một binary, Dockerfile, biến môi trường, HTTPS do chính ứng dụng phục vụ, với chứng chỉ từ Let's Encrypt, các header cho trình duyệt biết được làm gì với các trang của ứng dụng, trong đó có Content-Security-Policy, log và metric.

  18. CLI

    tug new, tug dev, tug gen, tug routes, tug lang, tug migrate, tug build và tug key, đầy đủ chi tiết.

Tàu kéo tuy nhỏ, nhưng kéo được những con tàu lớn gấp nhiều lần mình.

Tạo ứng dụng đầu tiên
tug