From 2da063c73c5d70640a54c63571492671bc22b13d Mon Sep 17 00:00:00 2001 From: EwenQuim Date: Fri, 29 Nov 2024 23:35:39 +0100 Subject: [PATCH] Cleans up the documentation, puts data flow on its own page --- documentation/docs/guides/01-data-flow.mdx | 9 +++ .../{controllers.mdx => controllers.md} | 3 - .../docs/guides/{errors.mdx => errors.md} | 7 +-- documentation/docs/guides/middlewares.md | 13 +++-- documentation/docs/guides/openapi.md | 31 +++++++++- .../{serialization.mdx => serialization.md} | 4 -- .../{transformation.mdx => transformation.md} | 6 +- documentation/docs/guides/validation.md | 58 +++++++++++++++++++ documentation/docs/guides/validation.mdx | 13 ----- documentation/docs/index.md | 14 +++-- documentation/docs/tutorials/02-crud.md | 6 +- documentation/docs/tutorials/03-hot-reload.md | 8 +-- .../docs/tutorials/rendering/index.md | 28 +++++++++ documentation/docusaurus.config.ts | 4 +- 14 files changed, 154 insertions(+), 50 deletions(-) create mode 100644 documentation/docs/guides/01-data-flow.mdx rename documentation/docs/guides/{controllers.mdx => controllers.md} (95%) rename documentation/docs/guides/{errors.mdx => errors.md} (86%) rename documentation/docs/guides/{serialization.mdx => serialization.md} (97%) rename documentation/docs/guides/{transformation.mdx => transformation.md} (94%) create mode 100644 documentation/docs/guides/validation.md delete mode 100644 documentation/docs/guides/validation.mdx diff --git a/documentation/docs/guides/01-data-flow.mdx b/documentation/docs/guides/01-data-flow.mdx new file mode 100644 index 00000000..669d2227 --- /dev/null +++ b/documentation/docs/guides/01-data-flow.mdx @@ -0,0 +1,9 @@ +import FlowChart from '@site/src/components/FlowChart'; + +# Data Flow + +Here's a high-level overview of how data flows through a Fuego application. + +Please note that every step can send an error to the error handler. + + diff --git a/documentation/docs/guides/controllers.mdx b/documentation/docs/guides/controllers.md similarity index 95% rename from documentation/docs/guides/controllers.mdx rename to documentation/docs/guides/controllers.md index 2e96a75b..57164277 100644 --- a/documentation/docs/guides/controllers.mdx +++ b/documentation/docs/guides/controllers.md @@ -1,10 +1,7 @@ -import FlowChart from '@site/src/components/FlowChart'; - # Controllers Controllers are the main way to interact with the application. They are responsible for handling the requests and responses. - ## Controller types diff --git a/documentation/docs/guides/errors.mdx b/documentation/docs/guides/errors.md similarity index 86% rename from documentation/docs/guides/errors.mdx rename to documentation/docs/guides/errors.md index e686db6c..98eb8e85 100644 --- a/documentation/docs/guides/errors.mdx +++ b/documentation/docs/guides/errors.md @@ -1,16 +1,13 @@ -import FlowChart from '@site/src/components/FlowChart'; - # Error handling Error handling is a crucial part of any application. It is important to handle errors gracefully and provide meaningful feedback to the user. In this guide, we will cover how to handle errors in a Fuego application. - - - ## Error handling in Fuego Fuego [controllers](./controllers) returns a value and an error. If the error is not `nil`, it means that an error occurred while processing the request. The error will be returned to the client as a JSON response. +By default, Fuego implements [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457), which defines a standard error format for HTTP APIs. We strongly recommend following this standard, but you can also use your own errors. + The error type returned as JSON is `fuego.HTTPError`. It has a `Status` and a `Info` field. The `Status` field is an integer that represents the error code. The `Info` field is a string that contains a human-readable error message. If your error implements `Status() int` and `Info()` methods, the error will include the status code and the error message in the `fuego.HTTPError` response. diff --git a/documentation/docs/guides/middlewares.md b/documentation/docs/guides/middlewares.md index c0acfdb0..0c200e69 100644 --- a/documentation/docs/guides/middlewares.md +++ b/documentation/docs/guides/middlewares.md @@ -12,7 +12,7 @@ authentication, etc. You can add middlewares to the whole server using the `Use` method: -```go +```go title="main.go" showLineNumbers package main import ( @@ -45,7 +45,7 @@ func main() { You can also add middlewares to a group of routes using the `Group` method: -```go +```go title="main.go" showLineNumbers package main import ( @@ -82,9 +82,10 @@ func main() { ## Route middlewares You can also add middlewares to a single route. +They are treated as an option to the route handler. Simply add the middlewares as the last arguments of the route handler: -```go +```go title="main.go" showLineNumbers {13-14} package main import ( @@ -95,7 +96,11 @@ func main() { s := fuego.NewServer() // Declare the middlewares after the route handler - fuego.Get(s, "/", myController, middleware1, middleware2, middleware3) + fuego.Get(s, "/", myController, + option.QueryInt("page", "The page number"), + option.Middleware(middleware1), + option.Middleware(middleware2, middleware3), + ) s.Run() } diff --git a/documentation/docs/guides/openapi.md b/documentation/docs/guides/openapi.md index b1c21f8c..d81c4595 100644 --- a/documentation/docs/guides/openapi.md +++ b/documentation/docs/guides/openapi.md @@ -106,7 +106,7 @@ func main() { Fuego `Server` exposes a `UIHandler` field that enables you to implement your custom UI. -Example with http-swagger: +Example with `http-swagger`: ```go import ( @@ -146,7 +146,34 @@ by more than 10Mb. | | StopLight Elements | Swagger | Disabled | | ------------- | ------------------ | -------------- | -------- | | Works offline | No ❌ | Yes ✅ | - | -| Binary Size | Smaller | Larger (+10Mb) | Smaller | +| Binary Size | Smaller | Larger (+10Mb) | Smallest | + +## Get OpenAPI Spec at build time + +With Go, you cannot generate things at build time, but you can separate the +OpenAPI generation from the server, by using the +`(Server).OutputOpenAPISpec()` function. + +```go title="main.go" showLineNumbers +package main + +import ( + "github.com/go-fuego/fuego" +) + +func main() { + s := fuego.NewServer() + + fuego.Get(s, "/", helloWorld) + + if justGenerateOpenAPI { // A CLI flag, an env variable, etc. + s.OutputOpenAPISpec() + return + } + + s.Run() +} +``` ## Hide From OpenAPI Spec diff --git a/documentation/docs/guides/serialization.mdx b/documentation/docs/guides/serialization.md similarity index 97% rename from documentation/docs/guides/serialization.mdx rename to documentation/docs/guides/serialization.md index 3c3ab21b..0af9f404 100644 --- a/documentation/docs/guides/serialization.mdx +++ b/documentation/docs/guides/serialization.md @@ -1,5 +1,3 @@ -import FlowChart from '@site/src/components/FlowChart'; - # Serialization / Deserialization Serialization is the process of converting Go data into a format that can be stored or transmitted. Deserialization is the process of converting serialized data back into its original Go form. @@ -8,8 +6,6 @@ The classic example is transforming **Go data into JSON** and back. Fuego automatically serializes and deserializes inputs and outputs with standard `encoding/json` package. - - ## Serialize data To serialize data, just return the data you want to serialize from your controller. It will be automatically serialized into JSON, XML, YAML, or HTML, depending on the `Accept` header in the request. diff --git a/documentation/docs/guides/transformation.mdx b/documentation/docs/guides/transformation.md similarity index 94% rename from documentation/docs/guides/transformation.mdx rename to documentation/docs/guides/transformation.md index 4f80afbc..690904c4 100644 --- a/documentation/docs/guides/transformation.mdx +++ b/documentation/docs/guides/transformation.md @@ -1,5 +1,3 @@ -import FlowChart from '@site/src/components/FlowChart'; - # Transformation With Fuego, you can transform data coming in and out of your application. This is useful for a variety of reasons, such as: @@ -10,8 +8,6 @@ With Fuego, you can transform data coming in and out of your application. This i - Masking sensitive data - And more... - - ## Input Transformation Input transformation is the process of transforming the data coming **into** your application. @@ -38,7 +34,7 @@ func (u *User) InTransform(ctx context.Context) error { } var _ fuego.InTransformer = (*User)(nil) // Ensure *User implements fuego.InTransformer -// This check is a classic example of Go's interface implementation check and we highly recommend to use it +// This check is a classic example of Go's interface implementation check and we highly recommend to use it ``` In the example above, we have a `User` struct with two fields: `FirstName` and `LastName`. We also have a method called `InTransform` that takes a `context.Context` and returns an `error`. This method is called before the data is unmarshaled into the `User` struct. In this method, we are transforming the `FirstName` to uppercase and trimming any whitespace from the `LastName`. diff --git a/documentation/docs/guides/validation.md b/documentation/docs/guides/validation.md new file mode 100644 index 00000000..288c7a78 --- /dev/null +++ b/documentation/docs/guides/validation.md @@ -0,0 +1,58 @@ +# Validation + +Validation is the process of ensuring that the data provided to the application +is correct and meaningful. + +With fuego, you have several options for validating data. + +- struct tags with `go-validator` +- custom validation functions + +## Struct tags + +You can use struct tags to validate the data coming into your application. +This is a common pattern in Go and is used by many libraries, +and we use [`go-playground/validator`](https://github.com/go-playground/validator) to do so. + +```go +type User struct { + FirstName string `json:"first_name" validate:"required"` + LastName string `json:"last_name" validate:"required"` + Age int `json:"age" validate:"gte=0,lte=130"` + Email string `json:"email" validate:"email"` +} +``` + +## Custom validation + +You can also use Fuego's [Transformation](./transformation.md) methods to validate the data. + +```go +package main + +import ( + "context" + "errors" + "strings" + + "github.com/go-fuego/fuego" +) + +type User struct { + FirstName string `json:"first_name"` + LastName string `json:"last_name"` +} + +func (u *User) InTransform(ctx context.Context) error { + u.FirstName = strings.ToUpper(u.FirstName) + u.LastName = strings.TrimSpace(u.LastName) + + if u.FirstName == "" { + return errors.New("first name is required") + } + return nil +} + +var _ fuego.InTransformer = (*User)(nil) // Ensure *User implements fuego.InTransformer +// This check is a classic example of Go's interface implementation check and we highly recommend to use it +``` diff --git a/documentation/docs/guides/validation.mdx b/documentation/docs/guides/validation.mdx deleted file mode 100644 index 8d5361b4..00000000 --- a/documentation/docs/guides/validation.mdx +++ /dev/null @@ -1,13 +0,0 @@ -import FlowChart from '@site/src/components/FlowChart'; - -# Validation - -Validation is the process of ensuring that the data provided to the application is correct and meaningful. This is important because the application will use this data to make decisions and take actions. If the data is incorrect or meaningless, the application will not work as expected. - -With fuego, you have several options for validating data. - -- struct tags with go-validator -- custom validation functions - - - diff --git a/documentation/docs/index.md b/documentation/docs/index.md index dc6af67e..dce2d1e2 100644 --- a/documentation/docs/index.md +++ b/documentation/docs/index.md @@ -18,9 +18,9 @@ Try our [Hello World](./tutorials/01-hello-world.md)! go run github.com/go-fuego/fuego/examples/hello-world@latest ``` -This runs the code for a simple hello world server. -Look at all it generates from a simple code! -You'll get a URL to see the result in your browser. +This simple code snippet runs a 'Hello World' server. +See how much Fuego generates from just a few lines of code! +You'll even get a URL to view the result directly in your browser ```go showLineNumbers package main @@ -42,10 +42,12 @@ func helloWorld(c fuego.ContextNoBody) (string, error) { } ``` -## Try example from real Fuego source code in 3 sec +![Swagger UI](../static/img/hello-world-openapi.jpeg) -Try Fuego immediately by cloning [the repo](https://github.com/go-fuego/fuego) -and running one of our examples. +## Try examples with real source code in 3 sec + +Just copy/paste these commands in your terminal, +you'll be iterating on a real example in no time. ```bash git clone git@github.com:go-fuego/fuego.git diff --git a/documentation/docs/tutorials/02-crud.md b/documentation/docs/tutorials/02-crud.md index 08190381..409f1207 100644 --- a/documentation/docs/tutorials/02-crud.md +++ b/documentation/docs/tutorials/02-crud.md @@ -23,7 +23,7 @@ for the code generator of Fuego, but you can implement it in any way you want. To implement the service, you need to slightly modify the generated `controllers/books.go` and `main.go` files. -```go title="controllers/books.go" {8-9,28-39} showLineNumbers +```go title="controllers/books.go" {8-9,28-42} showLineNumbers package controller import ( @@ -53,7 +53,9 @@ type BooksService interface { // Implement the BooksService interface type RealBooksService struct { - BooksService // Embed the interface to satisfy it -this pattern is just there to make the code compile but you should implement all methods + // Embed the interface to satisfy it. + // This pattern is just there to make the code compile but you should implement all methods. + BooksService } func (s RealBooksService) GetBooks(id string) (Books, error) { diff --git a/documentation/docs/tutorials/03-hot-reload.md b/documentation/docs/tutorials/03-hot-reload.md index 67df9bbd..4d449591 100644 --- a/documentation/docs/tutorials/03-hot-reload.md +++ b/documentation/docs/tutorials/03-hot-reload.md @@ -5,19 +5,19 @@ see the changes in real-time without restarting the server. This is very useful for development, as it allows you to see the changes you make to your code immediately. -To enable hot reload, you need to install the `air` command-line tool: +To enable hot reload, you need to install the `air` command-line tool. ```sh -go install github.com/cosmtrek/air@latest +go install github.com/air-verse/air@latest ``` -Then, create a `.air.toml` file in the root of your project with the following content: +Optionally, create a `.air.toml` configuration file to customize the hot reload behavior. ```sh air init ``` -Finally, simply the following command to start the server with hot reload: +Simply the following command to start the server with hot reload. ```sh air diff --git a/documentation/docs/tutorials/rendering/index.md b/documentation/docs/tutorials/rendering/index.md index 9e7dd2a1..7f0e2c1c 100644 --- a/documentation/docs/tutorials/rendering/index.md +++ b/documentation/docs/tutorials/rendering/index.md @@ -4,3 +4,31 @@ Fuego is not only capable to handle XML and JSON, it can also render HTML. It supports templating with [html/template](https://pkg.go.dev/html/template), [Templ](https://github.com/a-h/templ), and [Gomponents](https://github.com/maragudk/gomponents). + +## Content Negotiation + +Remember that Fuego handles [Content Negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation), +so you can serve different content types based on the `Accept` header of the request. + +Fuego also provides a helper to render HTML or JSON data for a single controller! + +```go +package main + +import ( + "github.com/go-fuego/fuego" +) + +func main() { + s := fuego.NewServer() + + fuego.Get(s, "/", func(c fuego.ContextNoBody) (interface{}, error) { + return fuego.DataOrHTML( + data, // When asking for JSON/XML, this data will be returned + MyTemplateInjectedWithData(data), // When asking for HTML, this template will be rendered + ), nil + }) + + s.Run() +} +``` diff --git a/documentation/docusaurus.config.ts b/documentation/docusaurus.config.ts index a18f9bda..ab1254c0 100644 --- a/documentation/docusaurus.config.ts +++ b/documentation/docusaurus.config.ts @@ -117,8 +117,8 @@ const config: Config = { title: "Community", items: [ { - label: "Twitter", - href: "https://twitter.com/FuegoFramework", + label: "Discord", + href: "https://discord.gg/tdGPnnxf", }, { label: "Youtube",