Introduction
Spine is a web framework that lets you register constructors, routes, and interceptors in Go code. The registration code shows which handler receives a request and which tasks run before and after it.
If you are getting started, follow Your First App to create a project with the CLI and check the GET /health response. This page introduces the core concepts used to configure an app.
Configuring and Running an App
In a project generated by the CLI, app configuration lives in BuildApplication() in internal/application/application.go. main.go configures, validates, and runs the app in the order BuildApplication → Validate → Run.
The following example shows how to register a user feature you have implemented yourself. It omits the UserController and interceptor implementations, so do not overwrite generated files with this example.
func BuildApplication() (spine.App, boot.Options, error) {
app := spine.New()
// 생성자를 등록하면 등록 순서와 관계없이 의존성을 해결합니다.
app.Constructor(NewUserRepository, NewUserService, NewUserController)
// 인터셉터를 실행할 순서대로 등록합니다.
app.InterceptorFor(boot.InterceptorHTTP, &TxInterceptor{}, &LoggingInterceptor{})
// HTTP 메서드와 경로에 핸들러를 연결합니다.
app.Route("GET", "/users", (*UserController).GetUser)
return app, boot.Options{
Address: ":8080",
EnableGracefulShutdown: true,
ShutdownTimeout: 10 * time.Second,
HTTP: &boot.HTTPOptions{},
}, nil
}The code created by spine generate resource user uses different names and paths from the example above. The generated handler is UserController.Get in src/user, and its route is GET /user/:id. Do not register the same constructor or route again if the constructor is already registered. Keep the user.Register(app) call added to BuildApplication and its error handling. See Project Structure for the full procedure.
Core Concepts
1. Constructor-Based Dependency Injection
Declare the dependencies you need as constructor parameters, then register the constructors with app.Constructor. In the example below, UserService receives a UserRepository.
// 생성자 파라미터가 곧 의존성 선언
func NewUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
// 등록만 하면 자동으로 의존성 그래프 해결
app.Constructor(NewUserRepository, NewUserService, NewUserController)2. Interceptor Pipeline
Interceptors run shared tasks such as authentication, transactions, and logging before and after controller calls. The following example shows the registration order of three interceptors you have implemented yourself.
app.Interceptor(
&TxInterceptor{}, // 1. 트랜잭션 시작
&AuthInterceptor{}, // 2. 인증 확인
&LoggingInterceptor{}, // 3. 로깅
)PreHandle runs in registration order. PostHandle, which runs after the controller returns, BeforeResponse, which runs before response processing, and AfterCompletion, which runs after response processing, run in reverse order. Interceptors explains when these methods are called if an error occurs.
3. Explicit Routing
Use app.Route to connect an HTTP method and path to a handler. A registration function for each feature lets you manage related routes in one place.
// 한 곳에서 모든 라우트를 관리
func RegisterUserRoutes(app spine.App) {
app.Route("GET", "/users", (*UserController).GetUser)
app.Route("POST", "/users", (*UserController).CreateUser)
app.Route("PUT", "/users", (*UserController).UpdateUser)
app.Route("DELETE", "/users", (*UserController).DeleteUser)
}4. Type-Safe Handlers
Handler parameters and return types express which values to read from a request and the shape of the response. In the following example, the lookup handler reads query parameters, and the creation handler receives the JSON request body as a struct pointer. You must implement the service and the request and response types yourself.
// 함수 시그니처가 곧 API 스펙
func (c *UserController) GetUser(
ctx context.Context, // 컨텍스트
q query.Values, // 쿼리 파라미터
) (httpx.Response[UserResponse], error) { // 응답 타입
user, err := c.svc.Get(ctx, q.Int("id", 0))
if err != nil {
return httpx.Response[UserResponse]{}, err
}
return httpx.Response[UserResponse]{Body: user}, nil
}
// DTO는 자동으로 바인딩
func (c *UserController) CreateUser(
ctx context.Context,
req *CreateUserRequest, // JSON body → 구조체 (포인터)
) (httpx.Response[UserResponse], error) {
user, err := c.svc.Create(ctx, req)
if err != nil {
return httpx.Response[UserResponse]{}, err
}
return httpx.Response[UserResponse]{Body: user}, nil
}Getting Started
go install github.com/NARUBROWN/spine-cli/cmd/spine@v0.1.4
spine versionAfter installing the CLI, follow Your First App to create a project and check its HTTP response. If the command cannot be found, see the path setup in the installation guide.
