Skip to content

Inline strict middleware typedefs#2271

Merged
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2270
Apr 29, 2026
Merged

Inline strict middleware typedefs#2271
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2270

Conversation

@mromaszewicz

@mromaszewicz mromaszewicz commented Mar 3, 2026

Copy link
Copy Markdown
Member

Replace type aliases to runtime/strictmiddleware/* packages with inline type definitions in all strict server templates. This removes the unnecessary runtime dependency for StrictHandlerFunc and StrictMiddlewareFunc, and updates interface{} to any in the fiber template for consistency.

Closes #2270,#2275

@mromaszewicz mromaszewicz requested a review from a team as a code owner March 3, 2026 19:54
@jinuthankachan

jinuthankachan commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

closes #2275

This PR would be able to fix the above issue aswell.

@mromaszewicz mromaszewicz added this to the v2.7.0 milestone Apr 21, 2026
Replace type aliases to runtime/strictmiddleware/* packages with inline
type definitions in all strict server templates. This removes the
unnecessary runtime dependency for StrictHandlerFunc and
StrictMiddlewareFunc, and updates interface{} to any in the fiber
template for consistency.

Closes oapi-codegen#2270

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mromaszewicz

Copy link
Copy Markdown
Member Author

@greptileai

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces type aliases to the oapi-codegen/runtime/strictmiddleware/* packages with inline type definitions across all strict server templates, removing the runtime dependency for StrictHandlerFunc and StrictMiddlewareFunc. The fiber template also modernizes interface{} to any.

The inline signatures correctly match those in the runtime packages (echo.Context for echo v4, *echo.Context for echo v5, *gin.Context for gin, iris.Context for iris, and the four-argument net/http form). Worth noting: changing from type aliases (type X = pkg.Y) to type definitions (type X func(...)) is a semantic breaking change — middleware code that directly references strictecho.StrictEchoHandlerFunc or its siblings from the runtime package will no longer be assignable to the generated StrictHandlerFunc, and users relying on that interchangeability will need to update their code.

Confidence Score: 4/5

Safe to merge; all inlined signatures correctly match the original runtime package types, and the only finding is a cosmetic blank-line inconsistency in the fiber template.

No logic bugs or security issues. The change from type aliases to type definitions is intentional per the PR description and the inline signatures are verified against the runtime package. The sole comment is a P2 style suggestion, leaving a ceiling of 4/5.

No files require special attention, though reviewers should note the alias→definition semantic shift may require migration notes in the changelog for users who reference the runtime package types directly in their middleware code.

Important Files Changed

Filename Overview
pkg/codegen/configuration.go Removes all strictmiddleware/* package imports from RouterImports() since the type definitions are now inlined in templates; clean and correct removal.
pkg/codegen/templates/strict/strict-echo.tmpl Replaces alias to strictecho.StrictEchoHandlerFunc with inline func(ctx echo.Context, request any) (any, error) — signature matches the runtime package exactly.
pkg/codegen/templates/strict/strict-echo5.tmpl Inlines the echo v5 handler type using *echo.Context (correct — echo v5 Context is a struct, not an interface); consistent with existing pointer usage throughout the template.
pkg/codegen/templates/strict/strict-gin.tmpl Inlines gin handler types with *gin.Context; matches the original StrictGinHandlerFunc signature from the runtime package.
pkg/codegen/templates/strict/strict-http.tmpl Inlines net/http handler type with (ctx context.Context, w http.ResponseWriter, r *http.Request, request any); correct four-argument form matching StrictHTTPHandlerFunc.
pkg/codegen/templates/strict/strict-iris.tmpl Inlines iris handler type with iris.Context (interface, no pointer needed); matches StrictIrisHandlerFunc signature.
pkg/codegen/templates/strict/strict-fiber.tmpl Modernizes interface{} to any and removes a blank line between the two type declarations; fiber was already using inline types (no alias), so this is cosmetic only.

Reviews (1): Last reviewed commit: "Inline strict middleware typedefs" | Re-trigger Greptile

@mromaszewicz mromaszewicz added the ☢️ breaking change This change would break existing users' code label Apr 29, 2026
@mromaszewicz mromaszewicz merged commit 5b562c4 into oapi-codegen:main Apr 29, 2026
19 checks passed
@mromaszewicz mromaszewicz deleted the fix/issue-2270 branch April 29, 2026 16:11
@jay-babu

jay-babu commented May 2, 2026

Copy link
Copy Markdown

My only grip with this change is not being a type alias

type StrictHandlerFunc func(ctx *gin.Context, request any) (any, error)
type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc

if the above was instead

type StrictHandlerFunc = func(ctx *gin.Context, request any) (any, error)
type StrictMiddlewareFunc = func(f StrictHandlerFunc, operationID string) StrictHandlerFunc

now, each server i generate does not need to have to worry about having separate types. I can just define func(f StrictHandlerFunc, operationID string) StrictHandlerFunc once and pass it in in multiple places. instead now, i have to cast it to multiple different package types. i created a github issue for this

@mromaszewicz

Copy link
Copy Markdown
Member Author

Thanks, you're right, that is nicer.

@jay-babu

jay-babu commented May 2, 2026

Copy link
Copy Markdown

@mromaszewicz if we could do the same thing here: https://github.com/oapi-codegen/runtime/blob/main/strictmiddleware/nethttp/main.go, then I could remove the need of having an type alias on my side to share across packages. The same change of having an =. I understand people don't want to import the strictmiddleware package in the generated code so that doesn't change

@mromaszewicz

mromaszewicz commented May 2, 2026

Copy link
Copy Markdown
Member Author

Since we are inlining those functions into the boilerplate now, do we even care about the ones in the runtime package?

@jay-babu

jay-babu commented May 2, 2026

Copy link
Copy Markdown

@mromaszewicz This is more just about the client experience. I have N servers that all generate the code for that in a monorepo.

strictmiddleware.go

package main

import (
	"context"
	"net/http"
)

type strictHandlerFunc = func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error)
type strictMiddlewareFunc = func(f strictHandlerFunc, operationID string) strictHandlerFunc

using my helper, i can do something like this

func NewAuthorizationMiddleware(queries model.Querier, logger *slog.Logger) strictMiddlewareFunc {
	return func(f strictHandlerFunc, operationID string) strictHandlerFunc {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error) {
			if isAuthorizationExemptOperation(operationID) {
				return f(ctx, w, r, request)
			}
			return checkAuthorization(ctx, w, r, request, f, operationID, queries, logger)
		}
	}
}

without the helper, I have to do this.

func NewAuthorizationMiddleware(queries model.Querier, logger *slog.Logger) func(f func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error), operationID string) func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error) {
	return func(f strictHandlerFunc, operationID string) strictHandlerFunc {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error) {
			if isAuthorizationExemptOperation(operationID) {
				return f(ctx, w, r, request)
			}
			return checkAuthorization(ctx, w, r, request, f, operationID, queries, logger)
		}
	}
}

I guess I can just pick one of the generated servers and use the type-alias from that but would be better if I could just import from https://github.com/oapi-codegen/runtime/blob/main/strictmiddleware/nethttp/main.go?rgh-link-date=2026-05-02T21%3A11%3A23Z in my opinion

image

Everything you see here is a separate strict server that is generated.

The main reason for this setup is because we siloed each service instead of having one giant interface. If there is a better way of doing this then please let me know.

type ServerInterface interface {

	// (GET /{entity_id}/campaign/campaigns/{campaign_id}/workflows)
	CampaignsListCampaignWorkflows(w http.ResponseWriter, r *http.Request, entityId int64, campaignId string, params CampaignsListCampaignWorkflowsParams)

	// (GET /{entity_id}/campaign/customers/{loyalty_customer_id}/metrics)
	CampaignsGetCustomerMetrics(w http.ResponseWriter, r *http.Request, entityId int64, loyaltyCustomerId int64)

	// (GET /{entity_id}/campaign/customers/{loyalty_customer_id}/workflows)
	CampaignsGetCustomerWorkflows(w http.ResponseWriter, r *http.Request, entityId int64, loyaltyCustomerId int64, params CampaignsGetCustomerWorkflowsParams)

	// (POST /{entity_id}/campaign/message)
	CampaignsSendMessage(w http.ResponseWriter, r *http.Request, entityId int64)

	// (GET /{entity_id}/campaign/metric-definitions)
	CampaignsListMetricDefinitions(w http.ResponseWriter, r *http.Request, entityId int64, params CampaignsListMetricDefinitionsParams)

	// (GET /{entity_id}/campaign/segments)
	CampaignsListSegments(w http.ResponseWriter, r *http.Request, entityId int64, params CampaignsListSegmentsParams)

	// (POST /{entity_id}/campaign/segments)
	CampaignsCreateSegment(w http.ResponseWriter, r *http.Request, entityId int64)

	// (PATCH /{entity_id}/campaign/segments/{segment_id})
	CampaignsUpdateSegment(w http.ResponseWriter, r *http.Request, entityId int64, segmentId string)

	// (POST /{entity_id}/campaign/segments/{segment_id}/archive)
	CampaignsArchiveSegment(w http.ResponseWriter, r *http.Request, entityId int64, segmentId string)

	// (POST /{entity_id}/campaign/workflows/cancel)
	CampaignsCancelWorkflows(w http.ResponseWriter, r *http.Request, entityId int64)

	// (GET /{entity_id}/campaign/workflows/{workflow_id}/{run_id})
	CampaignsGetWorkflowDetail(w http.ResponseWriter, r *http.Request, entityId int64, workflowId string, runId string)

	// (GET /{entity_id}/campaign/{campaign_id}/condition-tree)
	CampaignsGetConditionTree(w http.ResponseWriter, r *http.Request, entityId int64, campaignId string)
}
type ServerInterface interface {

	// (GET /health)
	HealthCheck(w http.ResponseWriter, r *http.Request)

	// (GET /{entity_id}/job-events)
	JobEventsList(w http.ResponseWriter, r *http.Request, entityId int64, params JobEventsListParams)

	// (GET /{entity_id}/jobs)
	JobsList(w http.ResponseWriter, r *http.Request, entityId int64, params JobsListParams)
}

@mromaszewicz

Copy link
Copy Markdown
Member Author

I have a goal to eventually make the runtime package completely unnecessary:
#2342

But, using the aliases vs typedefs in the generated code would let you just pick one of them, as you said, or just define that function type in your own code somewhere. It rarely changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

☢️ breaking change This change would break existing users' code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Strict server middleware function definitions don't need to be in the runtime package

3 participants