Voltar para o blog
Desenvolvimento

APIs com Golang e TypeScript: Boas Práticas para Integrações

5 de setembro de 202515 min de leituraPor Equipe GenStack
APIsGolangTypeScript

Desenvolver APIs de qualidade exige mais do que conhecer uma linguagem. Vamos explorar boas práticas usando Golang (backend) e TypeScript (cliente).

Por Que Golang para APIs?

Go se destaca para APIs por diversos motivos:

- **Performance:** Compilado, concorrente, rápido

- **Simplicidade:** Sintaxe limpa, fácil de manter

- **Stdlib robusta:** HTTP server, JSON, crypto nativos

- **Deploy simples:** Binário único, sem dependências

Estrutura do Projeto

Backend (Golang)

```

api/

├── cmd/

│ └── server/

│ └── main.go

├── internal/

│ ├── handlers/

│ ├── models/

│ ├── services/

│ └── middleware/

├── pkg/

│ └── utils/

└── go.mod

```

Cliente (TypeScript)

```

client/

├── src/

│ ├── api/

│ │ ├── client.ts

│ │ └── types.ts

│ └── index.ts

├── package.json

└── tsconfig.json

```

Boas Práticas: Backend (Golang)

1. Estrutura de Handlers

❌ Ruim:

```go

func GetUser(w http.ResponseWriter, r *http.Request) {

// Tudo misturado

}

```

✅ Bom:

```go

type UserHandler struct {

service UserService

}

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {

// Handler limpo, delega ao service

}

```

2. Validação de Input

❌ Ruim:

```go

user := User{}

json.Unmarshal(body, &user)

// Usar sem validar

```

✅ Bom:

```go

type CreateUserRequest struct {

Email string `json:"email" validate:"required,email"`

Name string `json:"name" validate:"required,min=2"`

}

func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {

var req CreateUserRequest

if err := json.NewDecoder(r.Body).Decode(&req); err != nil {

respondError(w, http.StatusBadRequest, "Invalid JSON")

return

}

if err := validate.Struct(req); err != nil {

respondError(w, http.StatusBadRequest, err.Error())

return

}

// Prosseguir com dados válidos

}

```

3. Error Handling Consistente

✅ Crie tipos de erro padronizados:

```go

type APIError struct {

Code int `json:"code"`

Message string `json:"message"`

Details string `json:"details,omitempty"`

}

func respondError(w http.ResponseWriter, code int, message string) {

w.Header().Set("Content-Type", "application/json")

w.WriteHeader(code)

json.NewEncoder(w).Encode(APIError{

Code: code,

Message: message,

})

}

```

4. Middleware para Concerns Transversais

```go

func LoggingMiddleware(next http.Handler) http.Handler {

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

start := time.Now()

next.ServeHTTP(w, r)

log.Printf(

"%s %s %s",

r.Method,

r.RequestURI,

time.Since(start),

)

})

}

func CORSMiddleware(next http.Handler) http.Handler {

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Access-Control-Allow-Origin", "*")

w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")

if r.Method == "OPTIONS" {

w.WriteHeader(http.StatusOK)

return

}

next.ServeHTTP(w, r)

})

}

```

5. Context para Timeout e Cancelamento

```go

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {

ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)

defer cancel()

user, err := h.service.GetUser(ctx, userID)

if err != nil {

if err == context.DeadlineExceeded {

respondError(w, http.StatusGatewayTimeout, "Request timeout")

return

}

respondError(w, http.StatusInternalServerError, err.Error())

return

}

respondJSON(w, http.StatusOK, user)

}

```

Boas Práticas: Cliente (TypeScript)

1. Cliente Tipado

```typescript

// types.ts

export interface User {

id: string;

email: string;

name: string;

createdAt: string;

}

export interface CreateUserRequest {

email: string;

name: string;

}

export interface APIError {

code: number;

message: string;

details?: string;

}

```

2. Cliente HTTP com Interceptors

```typescript

// client.ts

class APIClient {

private baseURL: string;

private token?: string;

constructor(baseURL: string) {

this.baseURL = baseURL;

}

setToken(token: string) {

this.token = token;

}

private async request<T>(

path: string,

options: RequestInit = {}

): Promise<T> {

const url = `${this.baseURL}${path}`;

const headers = new Headers(options.headers);

headers.set('Content-Type', 'application/json');

if (this.token) {

headers.set('Authorization', `Bearer ${this.token}`);

}

const response = await fetch(url, {

...options,

headers,

});

if (!response.ok) {

const error: APIError = await response.json();

throw new Error(error.message);

}

return response.json();

}

async getUser(id: string): Promise<User> {

return this.request<User>(`/users/${id}`);

}

async createUser(data: CreateUserRequest): Promise<User> {

return this.request<User>('/users', {

method: 'POST',

body: JSON.stringify(data),

});

}

}

export const apiClient = new APIClient('https://api.example.com');

```

3. Retry Logic

```typescript

async function withRetry<T>(

fn: () => Promise<T>,

maxRetries = 3,

delay = 1000

): Promise<T> {

let lastError: Error;

for (let i = 0; i < maxRetries; i++) {

try {

return await fn();

} catch (error) {

lastError = error as Error;

if (i < maxRetries - 1) {

await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));

}

}

}

throw lastError!;

}

// Uso

const user = await withRetry(() => apiClient.getUser('123'));

```

Versionamento de API

Estratégias:

1. Via URL (Recomendado)

```

/v1/users

/v2/users

```

2. Via Header

```

Accept: application/vnd.api.v1+json

```

3. Via Query Parameter (Menos comum)

```

/users?version=1

```

Documentação

Swagger/OpenAPI

Use anotações para gerar docs automaticamente:

```go

// @Summary Get user by ID

// @Description Get details of a user

// @Tags users

// @Accept json

// @Produce json

// @Param id path string true "User ID"

// @Success 200 {object} User

// @Failure 404 {object} APIError

// @Router /users/{id} [get]

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {

// ...

}

```

Testes

Backend (Go)

```go

func TestGetUser(t *testing.T) {

// Setup

mockService := &MockUserService{}

handler := &UserHandler{service: mockService}

// Request

req := httptest.NewRequest("GET", "/users/123", nil)

w := httptest.NewRecorder()

// Execute

handler.GetUser(w, req)

// Assert

assert.Equal(t, http.StatusOK, w.Code)

}

```

Cliente (TypeScript)

```typescript

import { describe, it, expect, vi } from 'vitest';

describe('APIClient', () => {

it('should get user', async () => {

global.fetch = vi.fn().mockResolvedValue({

ok: true,

json: async () => ({ id: '123', name: 'Test' }),

});

const user = await apiClient.getUser('123');

expect(user.id).toBe('123');

});

});

```

Conclusão

APIs bem projetadas são:

- **Consistentes:** Padrões claros em toda API

- **Documentadas:** Fáceis de entender e usar

- **Testadas:** Cobertura de testes adequada

- **Versionadas:** Evoluem sem quebrar clientes

- **Monitoradas:** Métricas e logs para troubleshooting

Próximos passos:

1. Escolha seu framework (Gin, Echo, Chi para Go)

2. Configure estrutura de projeto

3. Implemente middleware essencial

4. Escreva testes desde o início

5. Configure CI/CD

Precisa de ajuda com sua API? [Entre em contato](/contato).

Compartilhar:

Precisa de ajuda com seu projeto?

Entre em contato e vamos criar uma solução sob medida para você.

Falar com especialista