# How to Extend or Modify Gitea Modules: A Complete Developer's Guide

> Learn how to extend or modify Gitea modules. Discover techniques for custom packages, service implementation, and router registration in this developer's guide.

- Repository: [Gitea/gitea](https://github.com/go-gitea/gitea)
- Tags: how-to-guide
- Published: 2026-03-07

---

**You can extend Gitea by creating new packages under `modules/` for reusable utilities, implementing business logic in `services/` with dependency injection, and registering HTTP handlers in `routers/web/`, while modifying existing behavior requires overriding service implementations in the global container or patching handler registrations.**

Gitea’s self-hosted Git service architecture is built around a clean **modular design** that separates concerns into distinct layers, making it straightforward to add custom functionality or alter core behavior without forking the entire codebase. Understanding how to extend or modify Gitea modules allows developers to integrate proprietary workflows, custom validations, or specialized webhook payloads while maintaining compatibility with upstream updates. The codebase follows strict conventions in the `go-gitea/gitea` repository, where components communicate through Go interfaces and a simple global registry enables clean dependency injection.

## Understanding Gitea's Layered Architecture

Gitea organizes code into four primary layers, each with specific responsibilities and entry points. This separation ensures that low-level utilities remain independent from HTTP handling and database operations.

- **Modules** (`modules/`): Re-usable utilities, helpers, and low-level abstractions. Examples include `modules/util` for helper functions, `modules/web` for HTTP routing infrastructure, and `modules/validation` for input checking. The core router implementation lives in [`modules/web/router.go`](https://github.com/go-gitea/gitea/blob/main/modules/web/router.go).

- **Services** (`services/`): Business-logic components that operate on models and expose higher-level APIs. For instance, [`services/webhook/webhook.go`](https://github.com/go-gitea/gitea/blob/main/services/webhook/webhook.go) handles webhook delivery logic, while issue and repository management live in their respective service packages.

- **Routers** (`routers/web/` and `routers/api/`): HTTP handlers that map URLs to service calls, enforce middleware chains, and render templates. Handler files like [`routers/web/user/setting/webhooks.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/user/setting/webhooks.go) extract parameters and invoke the appropriate service methods.

- **Models** (`models/`): Database entities and ORM helpers defining structs like `User` in [`models/user/user.go`](https://github.com/go-gitea/gitea/blob/main/models/user/user.go) and repository metadata in `models/repo/`.

The application bootstrap occurs in [`main.go`](https://github.com/go-gitea/gitea/blob/main/main.go) at the repository root, where all modules, services, and routers initialize and wire together.

## Request Flow Through the System

When a request hits the Gitea server, it flows through a predictable pipeline that demonstrates how these layers interact:

1. The **router** in [`modules/web/router.go`](https://github.com/go-gitea/gitea/blob/main/modules/web/router.go) matches the request path and dispatches to a handler function in `routers/web/`.

2. The **handler** executes **middleware** from `modules/web/middleware/` for logging, authentication, CSRF protection, and locale detection.

3. The handler invokes a **service** method (e.g., `webhook.CreateWebhook`), passing a context object.

4. The **service** manipulates **models** and may call other modules for utility functions, completing the business operation.

Because each layer communicates through well-defined Go interfaces, you can inject alternative implementations at any stage without modifying upstream code.

## How to Extend Gitea with New Modules

Adding functionality follows a bottom-up approach: build utilities, wrap them in services, expose via HTTP, and register globally.

### Create a New Module

For code reusable across multiple features, create a package under `modules/yourmodule/`. Modules should be pure libraries with no dependency on database models or HTTP contexts. Expose a public API through clearly named functions and structs.

### Implement Business Logic as a Service

Encapsulate domain operations in `services/yourservice/`. Define a Go interface describing the operations (e.g., `type YourService interface { DoSomething(ctx context.Context, ...) error }`), then create an unexported struct implementing it. This pattern, visible throughout `services/webhook/` and `services/issue/`, enables easy mocking and replacement.

### Register Your Service

Gitea uses a simple global container initialized at startup. In [`services/init.go`](https://github.com/go-gitea/gitea/blob/main/services/init.go) (or a dedicated [`init.go`](https://github.com/go-gitea/gitea/blob/main/init.go) within your package), call `Register()` with your service implementation:

```go
func init() {
    Register(myservice.New())
}

```

Retrieve the service elsewhere via the global getter, typically `service.GetYourService()` or similar patterns found in the existing codebase.

### Add HTTP Endpoints

Create handler files in `routers/web/yourfeature/` that accept `*context.Context` (Gitea's extended context wrapping `http.ResponseWriter` and `*http.Request`). Extract parameters, call your service, and render JSON or HTML. Register routes in [`routers/web/init.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/init.go) or the feature's own `init()` function using the router group pattern:

```go
web.Group("/api/v1").GET("/yourfeature/:id", yourhandler.GetItem)

```

### Wire Custom Middleware

Reuse existing middleware from `modules/web/middleware/` or create new middleware functions that accept and return `http.Handler`. Apply these at the route group level or individual handler level to enforce cross-cutting concerns like custom authentication headers or rate limiting.

## How to Modify Existing Gitea Modules

When you need to alter existing behavior rather than add new features, three techniques allow surgical modifications without extensive rewrites.

### Override Functions with Wrappers

For changes to utility functions in `modules/util/` or validation logic, create a wrapper function in a new package that calls the original and modifies the result. Adjust the call site in the router or service to use your wrapper instead of the original import.

### Replace Service Implementations

Gitea's global service container keeps the last registration for any interface type. To replace an existing service, create a new implementation that embeds the original (for method reuse) or implements the interface from scratch, then register it in an `init()` function that executes after the original registration:

```go
// services/init.go
func init() {
    // Original registration happens first
    Register(webhook.New())
    // Your override wins
    Register(customwebhook.New())
}

```

This pattern allows you to modify webhook payloads, alter issue processing logic, or change repository initialization sequences while maintaining the existing API contract.

### Patch Router Handlers

Copy the existing handler function from `routers/web/` to your own file, modify the logic as needed, and update the route registration to point to your version instead of the original. This approach works best for one-off behavioral changes in the presentation layer.

## Practical Code Examples

### Example: Creating a Utility Module

Create [`modules/hello/hello.go`](https://github.com/go-gitea/gitea/blob/main/modules/hello/hello.go) for simple, stateless utilities:

```go
package hello

import "fmt"

// Greet returns a friendly greeting.
func Greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

```

Reference existing utilities in `modules/util/` for patterns on error handling and input validation.

### Example: Building a Service Layer

Define the interface and implementation in [`services/greeting/greeting.go`](https://github.com/go-gitea/gitea/blob/main/services/greeting/greeting.go):

```go
package greeting

import (
    "context"
    "github.com/go-gitea/gitea/modules/hello"
)

// Service describes greeting-related operations.
type Service interface {
    SayHello(ctx context.Context, user string) (string, error)
}

type serviceImpl struct{}

func New() Service { return &serviceImpl{} }

func (s *serviceImpl) SayHello(_ context.Context, user string) (string, error) {
    return hello.Greet(user), nil
}

```

Register in [`services/init.go`](https://github.com/go-gitea/gitea/blob/main/services/init.go):

```go
package services

import (
    "github.com/go-gitea/gitea/services/greeting"
)

func init() {
    Register(greeting.New())
}

```

### Example: Adding an HTTP Endpoint

Implement the handler in [`routers/web/greeting/greeting.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/greeting/greeting.go):

```go
package greeting

import (
    "net/http"
    "github.com/go-gitea/gitea/modules/context"
    "github.com/go-gitea/gitea/services/greeting"
)

// GetGreeting handles GET /api/v1/greeting/:name
func GetGreeting(ctx *context.Context) {
    name := ctx.Params("name")
    msg, err := greeting.GetService().SayHello(ctx, name)
    if err != nil {
        ctx.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
        return
    }
    ctx.JSON(http.StatusOK, map[string]string{"message": msg})
}

```

Wire the route in [`routers/web/api/v1/init.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/api/v1/init.go) or equivalent:

```go
func init() {
    web.Group("/api/v1").GET("/greeting/:name", greeting.GetGreeting)
}

```

### Example: Overriding an Existing Service

To customize webhook payloads, create [`services/webhook/custom.go`](https://github.com/go-gitea/gitea/blob/main/services/webhook/custom.go):

```go
package webhook

import (
    "context"
    "github.com/go-gitea/gitea/models/webhook"
)

type customImpl struct {
    *serviceImpl // Embed original to inherit unmodified methods
}

func NewCustom() Service {
    return &customImpl{serviceImpl: &serviceImpl{}}
}

func (c *customImpl) Deliver(ctx context.Context, hook *webhook.Webhook) error {
    // Modify payload before delegating to original logic
    hook.Payload = []byte(`{"custom":"value"}`)
    return c.serviceImpl.Deliver(ctx, hook)
}

```

Ensure your registration in [`services/init.go`](https://github.com/go-gitea/gitea/blob/main/services/init.go) executes last:

```go
func init() {
    Register(NewCustom()) // Replaces the default webhook service
}

```

## Key Files for Module Development

Understanding these critical entry points accelerates development when you extend or modify Gitea modules:

- **[`main.go`](https://github.com/go-gitea/gitea/blob/main/main.go)**: Application bootstrap that initializes the global service container and starts the HTTP server.
- **[`modules/web/router.go`](https://github.com/go-gitea/gitea/blob/main/modules/web/router.go)**: Core HTTP routing implementation defining how URLs map to handlers.
- **`modules/web/middleware/`**: Common middleware for authentication, CSRF protection, logging, and locale detection.
- **[`services/webhook/webhook.go`](https://github.com/go-gitea/gitea/blob/main/services/webhook/webhook.go)**: Exemplary service implementation showing business logic separation from transport concerns.
- **[`routers/web/user/setting/webhooks.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/user/setting/webhooks.go)**: Reference for handler patterns that bridge HTTP requests to service calls.
- **[`models/user/user.go`](https://github.com/go-gitea/gitea/blob/main/models/user/user.go)**: Database model definition showing ORM struct conventions used throughout services.

## Summary

- Gitea's architecture separates concerns into **modules** (utilities), **services** (business logic), **routers** (HTTP handling), and **models** (data), enabling clean extension points.
- Extend functionality by creating packages under `modules/`, implementing interfaces in `services/`, and registering routes in `routers/web/`.
- Modify existing behavior by leveraging the **global service container**—later registrations override earlier ones—or by wrapping functions and patching handler registrations.
- Always follow the interface-based patterns found in [`services/webhook/webhook.go`](https://github.com/go-gitea/gitea/blob/main/services/webhook/webhook.go) and [`modules/web/router.go`](https://github.com/go-gitea/gitea/blob/main/modules/web/router.go) to ensure your code remains compatible with upstream Gitea updates.

## Frequently Asked Questions

### How do I ensure my custom module survives Gitea updates?

Place your code in dedicated directories under `modules/` and `services/` rather than modifying existing files. Use the service override pattern (registering your implementation after the default in [`services/init.go`](https://github.com/go-gitea/gitea/blob/main/services/init.go)) so that `git merge` operations from upstream apply cleanly without conflicts. Avoid editing files in `routers/web/` that you don't own; instead, add new handler files and register additional routes.

### Can I modify Gitea's database models when extending modules?

While you can add new tables and models in separate packages under `models/`, avoid modifying existing model files like [`models/user/user.go`](https://github.com/go-gitea/gitea/blob/main/models/user/user.go) or [`models/repo/repo.go`](https://github.com/go-gitea/gitea/blob/main/models/repo/repo.go) directly, as this breaks database migrations and upstream compatibility. Instead, create join tables or extension tables linked by foreign keys, and access them through your custom service layer.

### What is the difference between a module and a service in Gitea?

**Modules** (`modules/`) are low-level, reusable libraries with no application state—think of them as internal packages providing utilities, validation, or HTTP abstractions. **Services** (`services/`) contain business logic, operate on database models, and typically manage transactions. Services depend on modules, but modules should never import services or models, maintaining a strict dependency direction.

### How do I debug my custom service implementation during development?

Add your service initialization to [`services/init.go`](https://github.com/go-gitea/gitea/blob/main/services/init.go) with verbose logging, then run Gitea with the `debug` log level enabled in [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini). Since services are singletons registered at startup in [`main.go`](https://github.com/go-gitea/gitea/blob/main/main.go), you can add `log.Info()` calls in your service's constructor and methods to trace execution. Use `go test` in your package directories for unit testing, following the patterns in [`services/webhook/webhook_test.go`](https://github.com/go-gitea/gitea/blob/main/services/webhook/webhook_test.go).