Releases: go-goyave/goyave
Release v5.4.1
- The parse middleware is now skipped if the matched route is the special "not found" or "method not allowed" route.
Release v5.4.0
- Added a
context.Context
field to thevalidation.Options
andvalidation.Context
structures. If not provided, it defaults tocontext.Background()
. - The built-in validation middleware now uses the request context in the validation options.
- The DB instance used in the validation options by the built-in validation middleware was already injected with the request context. You don't need to update your custom validators.
- You may want to update places where you are using manual validation. However, those will continue to work exactly like they used to if you don't change anything.
Full Changelog: v5.3.0...v5.4.0
Release v5.3.0
What's Changed
- Added
slog.DiscardLogger()
, which redirects logs toio.Discard
. (#228 #229) testutil.TestServer
now uses the discard logger by default instead oftestutil.LogWriter
.
This change was made so logs written during tests should usually be silent. To make it more convenient, this is now the default behavior. Sometimes it is still handy to see logs while writing or debugging a test. For this use-case, you can use testutil.LogWriter
:
func TestSomething(t *testing.T) {
opts := goyave.Options{
Logger: slog.New(slog.NewHandler(true, &testutil.LogWriter{t: t})),
}
server := testutil.NewTestServerWithOptions(t, opts)
//...
}
New Contributors
- @SallesCosta made their first contribution in #229
Full Changelog: v5.2.1...v5.3.0
Release v5.2.1
- Fixed a panic when a user service returned
gorm.ErrRecordNotFound
and a zero-value model in theauth.JWTController
'sLogin
handler. (#221 #224)
Full Changelog: v5.2.0...v5.2.1
Release v5.2.0
- Added support for response and chained writer flushing. (#215 #218)
- Added support for the
clickhouse
database dialect. (by @AidanKenney in #219, #195) - Updated the
UniqueArray
/ExistsArray
validators to support ClickHouse. ((by @AidanKenney) - Changed the default status handler for code
400 Bad Request
to the newgoyave.ParseErrorStatusHandler
. This status handler's behavior is identical to the previous one except it returns explicit error messages in the response when the parse middleware fails to parse a user's request. (by @BowlOfSoup in #217, #216) - Added
lang.Default
global variable, which contains the builtin defaulten-US
language. (#214 #213) - Fixed a panic if no
Language
is given in thevalidation.Options
when manually validating. Instead,lang.Default
will be used. (#214 #213) - Added deflate, lzw, br and zstd encoders for the compress middleware. (by @AidanKenney in #220, #197, #198, #199, #200)
New Contributors
- @AidanKenney made their first contribution in #219
Full Changelog: v5.1.1...v5.2.0
Release v5.1.1
- Validation:
- Fixed a panic when validating a property of an object contained by a root array.
- Fixed a panic when adding an error on a root array element.
- FIxed incorrect error path when adding an error on a root array element.
Response.SetWriter()
will nowInit()
the given writer if it implementsgoyave.Composable
.- Fixed status handlers in subrouters not being used. Only the main router's status handlers were effective. For special routes (not found, method not allowed), the main router's status handler will be used.
Full Changelog: v5.1.0...v5.1.1
Release v5.1.0
- Validation:
- Configuration: added a
Required
flag for entries to preventnil
values by @BowlOfSoup in #211 - Compress middleware: the order of the
Encoders
now determines server-side priority, which will help chose an encoder when there are client priority ties. - Optimization: use a
sync.Pool
for requests and responses for a small performance improvement. util/httputil.ParseMultiValuesHeader
:- The input header is now trimmed
- The function returns an empty slice if the input is empty
- Minor optimization
New Contributors
- @saltyFamiliar made their first contribution in #207
- @bangadam made their first contribution in #209
- @BowlOfSoup made their first contribution in #211
Full Changelog: v5.0.1...v5.1.0
Release v5.0.1
- httputil: fixed a race condition when initializing the quality value regex
- util/session: improved support for nested transactions
Release v5.0.0
Introduction
Goyave v5 has been in the oven for over two years. With the first production applications deployed, a ton of issues and flaws revealed themselves, preventing real long-lived projects from cleanly evolving. The initial goals of the framework started to weaken the more the applications were developed and changed: the strong basis it promised to provide wasn't actually that strong. From this invaluable experience, I decided to go back to the drawing board and redesign the framework.
This new major version is an almost entire rewrite of the framework. In order to limit the frequency of releases containing breaking changes, all accumulated ideas for reworks and improvements that would introduce those were grouped in v5. This new major version not only aims at fixing the outstanding issues and design flaws, but also improves on existing features by upgrading them to the latest available technology. Expect v5 to feel modern and to neatly integrate with all the new language features such as generics, file systems, structured logging, and more.
These release notes will be organized in categories. They will explain the overall change of direction for each area of the framework as well as shortly showcase the new or improved features. The list of changes may be incomplete as many features were rewritten entirely.
Motivations
Among the many aspects that needed to be reworked, some of them stood out and fueled the initial drive to rewrite the framework.
Dependency coupling
Every layer and components of a v4 application had strong dependency coupling. The HTTP, business and database layers were all mixed up together. The framework was systematically imposing a dependency to itself, direct or indirect. Its locked-up architecture, entirely based on globals was hindering the more business-oriented applications. Those struggled to detach their domains from the rest of the application, and encountered obstacles every time they needed to handle more complex business logic.
For example: to access the database, you were forced to use the framework, which was loaded from a configuration system that also was handled by the framework. This created a long chain of dependencies that was hard to separate from the rest, even more so when it came to writing tests.
On top of that, the all-global architecture required a ton of synchronization, which were detrimental to the overall performance of the application.
Locked architecture
All components of the framework were strongly linked together and quite opaque, despite an initial effort made to make the framework flexible and hackable. In the end, many non-elegant workarounds had to be made in real-world scenarios. This made it harder to adapt an application to the constraints often encountered by companies developing their ecosystem and trying to solve real-world issues that needed deeper access to the inner-workings.
The validation system was one of the biggest, if not the biggest, culprit. It was very inconvenient to use compared to the new one brought with v5. The hacks required to make some advanced field comparison or business-logic validation were very fragile and hard to maintain. This design made it impossible to re-use code, and forced the hassle of creating a new validator for every single specific use-case.
The framework was also a bit too reliant on magic in some aspects. Many functions were using weak typing (any
) and reflection, or even string identifiers, all for the sake of conciseness. But this came at a cost: no compile-time checks, hard code navigation, no completion, the need to rely on documentation all the time, etc. In the end, by trying to be concise for a better DX (developer experience), the framework sacrificed code cleanliness, reliability, readability, maintainability and actually ruined its DX this way.
Testing
All these issues accumulate and become a huge pain the moment you start trying to add tests to your project. They were very difficult to write, which is the exact opposite of what you want. Tests should be painless and as easy to read and maintain as possible. Also suffering from the locked architecture and mandatory dependency to the framework, they couldn't even be run in parallel. Almost nothing could be mocked because of the dependency coupling. This was in turn forcing you to use the database for your tests, which made tests even slower and complicated.
In short, the design of the framework prior to v5 treated tests as an after-thought despite how important they are.
Streamlining
There were many smaller and non-blocking issues as well. Together they made the entire development flow awkward by moments, like there was a missing piece to make the whole process fluid from start to finish.
The first one was the relation between the user-sent data and the internal data. Making use of the data sent by users was inconvenient and also quite unsafe, requiring a ton of type assertions and map navigation. This is very subpar compared to most Go applications which use structures. It also caused problems when interacting with the database.
Another issue was error handling and debugging. The framework was relying too much on panic
, which is not a very sane nor idiomatic way to handle errors. Although it allowed for a pretty good debugging experience for developers with more precise stacktraces, a much better solution was possible. This solution wouldn't compromise on code quality for the sake of DX, again.
The last major one was the low interoperability and control, notably with the lack of access over the context
API, or some missing configuration options and settings.
Philosophy
The overall philosophy of the framework stays the same. Goyave remains an opinionated framework, focused on DX (developer experience) by enabling quick and enjoyable development by being expressive, reliable and complete. The goal is to make business logic development as painless as possible by handling as many recurring things for developers as possible so they can focus on implementing what actually creates value for their company.
New direction
However, there are some important evolutions in the general direction of the framework:
- v5 takes architecture and utilities one step further. At first glance, a typical application will be less simple to grasp. Therefore, the "progessive" nature of the framework is no more.
- Despite its opinionated nature, the new design tries to be as open as possible. The framework should not be a black box and developers should have no trouble making it their own effortlessly.
- The design should focus its efforts on creating tools that simplify the reduction or elimination of the dependencies between application layers and business layers.
- The design and development of the framework now assumes full focus on catering to businesses and medium to large projects.
- The development of the framework will now consider real-world use-cases and scenarios with more importance. No "quick and easy" solutions, tools or fancy gadgets will be developed from now on.
- The framework and documentation will expand even more on architecture recommendations and good practices to cover more questions developers could have when working on their project. The goal of an opinionated framework is to save time to its users, while giving them all the tools they need to do produce the best possible quality software.
- The open-source process has been revised to make contributions easier. Gathering and taking care of a strong community has always been very important. More efforts will be made in this direction.
Architecture
The framework goes even further than before when it comes to architecture recommendations for your projects. The goal is to answer more of the questions any team will inevitably encounter when setting up their project. By providing tools that will works seemlessly with this architecture, the frameworks aims at saving you a lot of time and make the development process as fluid as possible.
A project build with Goyave v5 will be split in three distinct layers:
- Presentation: HTTP/REST layer, it's your application's facade
- Domain/Business: contains services
- Data: interacts with the database with repositories and contains the models
Each layer doesn't directly depend on the others because they define interfaces representing their own needs. The following chart describes the usual flow of a request into a Goyave application.
This architecture has several advantages:
- Good separation of concerns and no direct dependency
- Easily testable
- The data layer doesn't leak into the business layer even if there are transactions involved
- Lowers the risk of exposing information that is not meant to be public
- Easily readable, explorable and maintainable
In Goyave v5, nothing is global anymore. The costly need for goroutine synchronization is eliminated. The overall design is now interface-focused. On top of that, the entire framework now takes full advantage of the standard context
API and encourages it use.
Components
Because nothing is global, a mechanism is necessary so the server's essential resources (such as the configuration, logger, etc) can be distributed to every component of the server. This mechanism is actually called Components, and described by the interface goyave.Composable
. Most structures in the presentation layer actually are Goyave components: controllers, middleware, validators, etc.
Server
Changing all global resources to non-global required a central element that would hold them all. A server contains all the base resourc...
Pre-release v5.0.0-rc14
- Fixed a panic occurring in validation when the expected type is a slice but the value is not.