emicklei/go-restful: building REST APIs in Go with explicit HTTP semantics
package for building REST-style Web Services using Go
At a glance
- What is it?
- go-restful is an MIT-licensed Go package that maps HTTP methods onto handler functions through WebServices, routes and filters. It suits teams that want a declarative route table rather than a full framework, and it expects you to bring your own persistence and middleware.
- Who is it for?
- Adopt go-restful when you want a route table, filters and content negotiation inside an existing net/http server, and when the v3 module path github.com/emicklei/go-restful/v3 fits your toolchain. Skip it if you need an all-in-one framework with an ORM, dependency injection or a large plugin ecosystem; the package has none of those.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 24 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem go-restful solves for Go API authors
Plain net/http gives you a ServeMux and a handler signature. It does not give you a route table that documents itself, path parameters with declared data types, per-route content negotiation, or a filter chain that can run before and after a handler. go-restful fills that gap. The README frames the package around the REST convention that HTTP methods map one-to-one onto CRUD operations: GET retrieves, POST creates, PUT creates or replaces, DELETE removes, PATCH updates partially, OPTIONS reports communication options.
The intended audience is a Go developer who already has an HTTP server and wants structured routing on top of it, without adopting a framework that dictates project layout. The package is a library, not an application skeleton. It ships a WebService type, a Container, Route and Request/Response wrappers, and a set of optional filters. Persistence, authentication backends and deployment are left to you. That is a deliberate boundary, and it is the main reason some teams pick it and others do not.
How WebServices, routes and filters fit together
A WebService groups routes under a common path prefix and declares what it consumes and produces. Each route binds an HTTP method and a path template to a handler function, and can carry documentation metadata and a declared response type. The README example shows a WebService rooted at /users that consumes XML and JSON and produces JSON and XML, with a GET route on /{user-id} whose handler reads the path parameter through request.PathParameter("user-id").
Path templates are more expressive than a simple {id} placeholder. The feature list mentions prefix_{var} and {var}_suffix forms, google custom methods such as /resource/name:customVerb, and dynamic parameters such as /meetings/{id} or /static/{subpath:*}. Two router algorithms are available. The default router handles static elements, custom verbs, regular expressions and dynamic parameters. The alternative follows JSR311 and, per the README, is implemented using regular expressions but does not accept them in route definitions. Choosing between them is a real decision, not a formality: a path that works under one router can be rejected by the other.
Around the handler, filters intercept the request-to-response flow at either Service or Route level. Request-scoped state travels through attributes. Containers let you mount several WebServices on different HTTP endpoints. Compression, CORS handling, automatic OPTIONS responses and panic recovery are all provided as filters or hooks rather than baked into the core, which keeps the default path small. The repository layout reflects that split: cors_filter.go, options_filter.go, compress.go and filter.go sit beside the routing files curly.go, curly_route.go, jsr311.go and route.go.
Installing go-restful v3 and registering a first route
Version 3 uses Go modules. The go.mod file in the repository declares the module path github.com/emicklei/go-restful/v3 and a go directive of 1.13. Add the package to an existing module:
go get github.com/emicklei/go-restful/v3Import it with the version suffix. The README notes that everything up to v2 does not support Go modules and uses the unsuffixed import path, so mixing the two will not compile.
import (
restful "github.com/emicklei/go-restful/v3"
)A minimal service declares its path and content types, then attaches a route. The README's example, trimmed here, shows the shape:
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
ws.Route(ws.GET("/{user-id}").To(u.findUser).
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
Writes(User{}))The handler signature takes a *restful.Request and a *restful.Response. Reading the path parameter inside the handler uses the same name declared in the route:
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
}After registering the WebService on a Container, a GET to /users/42 should reach findUser with id set to "42". The repository's examples directory contains runnable programs for hello, filters, cors, openapi, pathtail and others, and the Makefile builds them all with a single target.
Where go-restful stops and you have to start
The package does not ship a database layer, a session store, a template engine beyond what net/http offers, or a dependency injection container. The examples directory includes a template example, but the library itself is not a web application framework in the Rails or Django sense. If your team expects scaffolding that generates models, migrations and admin screens, go-restful will feel like a lower-level tool.
The router choice is the sharpest edge. The README states plainly that the JSR311 router is implemented using regular expressions but does not accept them, while the default router does accept them along with custom verbs and dynamic parameters. A route written for one algorithm may not behave as expected under the other, and the documentation does not present a compatibility matrix. Trailing slashes are another quiet behaviour: the package variable TrimRightSlashEnabled defaults to true and controls whether routes ending in / match. Teams migrating an existing API where clients send trailing slashes should confirm the setting rather than assume.
Caching is on by default. The feature list mentions SetPathTokenCacheEnabled and SetCustomVerbCacheEnabled, both defaulting to true, added so that regexp caching can be turned off. If you generate routes dynamically at runtime, that default is worth knowing about. Error handling is configurable through RecoverHandler and ServiceErrorHandler, and route errors produce 404, 405, 406 or 415 responses. The README does not document rollback or versioning strategy for the API surface itself, so pinning a version in go.mod is the practical safeguard.
go-restful against Gin and the wider Go router field
Gin is the comparison most Go developers reach for. The two take different positions on the same problem. Gin centres on a context object with binding helpers and a large middleware ecosystem, and its router is built around a radix tree tuned for speed. go-restful centres on a WebService abstraction where routes carry documentation metadata, declared parameter types and declared response types, and where content negotiation is part of the route definition through Consumes and Produces.
That difference matters at the edges. If you want automatic Swagger UI generation, go-restful pairs with go-restful-openapi, a separate repository that reads the route metadata. That is a two-package arrangement rather than a feature of the core. If you want a single import that brings routing, validation, rendering and middleware together, Gin is the shorter path. If you want the route table itself to be the source of truth for an OpenAPI document, go-restful's metadata-first design is the reason to choose it.
Standard library routing is the third option. Since ServeMux gained method and wildcard patterns, some services no longer need a third-party router at all. go-restful still adds filters, parameter declarations and content negotiation on top of that baseline.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-09-07. The most recent release listed is v3.13.0 on 2025-08-15, described as speed improvements for regex based routing. Earlier entries in the release list go back to v1.1.2 in 2014 and v1.2 in 2015, which shows a long-lived project rather than a new one. The v3 branch is the module-aware line; the README directs readers to the v3 examples and states that v2 and earlier do not support Go modules.
Licensing is MIT, per the README footer and the LICENSE file at the repository root. That is a permissive licence, but the implications for your product depend on how you distribute and what you bundle, so review it with whoever handles compliance rather than treating the label as sufficient.
Upgrade cost is concentrated in two places. Moving from v2 to v3 changes the import path to include /v3, which touches every file that imports the package. Within v3, the router algorithm, the slash-trimming variable and the two caching toggles are the settings most likely to change behaviour between versions. The CHANGES.md file at the repository root is where release notes live; the README itself does not enumerate breaking changes, so that file is the one to read before bumping a version.
Editorial conclusion
Adopt go-restful when you want a route table, filters and content negotiation inside an existing net/http server, and when the v3 module path github.com/emicklei/go-restful/v3 fits your toolchain. Skip it if you need an all-in-one framework with an ORM, dependency injection or a large plugin ecosystem; the package has none of those. Before committing, verify the router algorithm you intend to use, because the default router and the JSR311 router accept different path syntax, and check whether TrimRightSlashEnabled (default true) matches how your clients send trailing slashes.
Frequently asked questions
How do I install emicklei/go-restful in a Go module?
Run go get github.com/emicklei/go-restful/v3 and import it as github.com/emicklei/go-restful/v3. The v3 line supports Go modules; versions up to v2 use the unsuffixed import path and do not.
Does emicklei/go-restful generate OpenAPI documentation?
Not by itself. The README points to go-restful-openapi as the companion package for API declaration for Swagger UI, and the repository includes an openapi example under examples/.
Can emicklei/go-restful handle CORS and OPTIONS requests?
Yes, through filters. The feature list includes automatic CORS request handling and automatic responses on OPTIONS, both implemented as filters, with cors_filter.go and options_filter.go in the repository root.
What is meant by RESTful?
The README describes REST as asking developers to use HTTP methods explicitly and consistently with the protocol, mapping GET to retrieve, POST to create, PUT to create or update, DELETE to delete, PATCH to partial update and OPTIONS to communication options for the URI.
Is Gin a framework for Go?
Gin is not part of go-restful; it is a separate Go web framework. The comparison is worth making because Gin bundles routing, middleware and binding helpers in one import, while go-restful keeps routing metadata in the WebService and leaves OpenAPI generation to go-restful-openapi.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/emicklei-go-restful)